diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..62ffb27 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,201 @@ +name: Firmware PR + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + +permissions: + contents: read + pull-requests: read + +jobs: + validate-pr: + name: Validate PR title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + host-tests: + name: Host unit and release-boundary tests + needs: validate-pr + runs-on: ubuntu-latest + steps: + - name: Checkout PR head + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Compile and run calibration suite + run: | + mkdir -p "${RUNNER_TEMP}/ambit-host-tests" + c++ -std=c++11 -Wall -Wextra -Werror \ + test/calibration/test_main.cpp \ + -o "${RUNNER_TEMP}/ambit-host-tests/calibration" + "${RUNNER_TEMP}/ambit-host-tests/calibration" + + - name: Compile and run ADPD fake-transport suite + run: | + c++ -std=c++11 -Wall -Wextra -Werror \ + test/adpd6000/test_main.cpp src/src/adpd/jii_adpd6000.cpp \ + -o "${RUNNER_TEMP}/ambit-host-tests/adpd6000" + "${RUNNER_TEMP}/ambit-host-tests/adpd6000" + + - name: Run release-boundary tests + run: python3 -m unittest discover -s test/release_process -p 'test_*.py' -v + + build: + name: Build firmware + needs: validate-pr + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: read + steps: + - name: Checkout PR head + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + + - name: Install release tooling + run: bash tools/install_release_tooling.sh + + - name: Prove pinned GitHub plugin remains draft-only + run: node test/release_process/semantic_release_github_draft.mjs + + - name: Compute release preview + id: preview + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + shell: bash + run: | + set -euo pipefail + # Present semantic-release with the PR tree as one squash commit whose + # message is the already validated PR title. + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -B main "${PR_HEAD_SHA}" + git reset --soft "${PR_BASE_SHA}" + git commit --allow-empty -m "${PR_TITLE}" + + export GITHUB_REF="refs/heads/main" + export GITHUB_EVENT_NAME="push" + set +e + npx semantic-release --dry-run --no-ci 2>&1 | tee release-preview.log + RELEASE_STATUS=${PIPESTATUS[0]} + set -e + if [[ ${RELEASE_STATUS} -ne 0 ]]; then + exit "${RELEASE_STATUS}" + fi + + VERSION=$(grep -Eo 'The next release version is [0-9]+\.[0-9]+\.[0-9]+' release-preview.log | tail -n 1 | awk '{print $6}' || true) + if [[ -n "${VERSION}" ]]; then + BUILD_VERSION="${VERSION}" + WILL_RELEASE=true + else + BUILD_VERSION="pr-${PR_NUMBER}-${PR_HEAD_SHA:0:12}" + WILL_RELEASE=false + fi + + echo "version=${BUILD_VERSION}" >> "${GITHUB_OUTPUT}" + echo "will-release=${WILL_RELEASE}" >> "${GITHUB_OUTPUT}" + { + echo "## Firmware release preview" + echo + if [[ "${WILL_RELEASE}" == "true" ]]; then + echo "Merging this PR will publish **v${VERSION}**." + else + echo "Merging this PR will not create a firmware release." + fi + echo + echo "- Title: \`${PR_TITLE}\`" + echo "- Build version: \`${BUILD_VERSION}\`" + } | tee release-preview.md >> "${GITHUB_STEP_SUMMARY}" + + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install PlatformIO + run: | + python -m venv "${RUNNER_TEMP}/platformio" + "${RUNNER_TEMP}/platformio/bin/pip" install --disable-pip-version-check platformio==6.1.19 + + - name: Build firmware + env: + AMBIT_RELEASE_VERSION: ${{ steps.preview.outputs.version }} + # platformio.ini redirects build_dir under LOCALAPPDATA (a Windows + # OneDrive workaround); give it a sane root on the runner. + LOCALAPPDATA: ${{ runner.temp }}/pio_build_root + run: "${RUNNER_TEMP}/platformio/bin/pio run -e ambit" + + - name: Verify version and assemble release assets + env: + FIRMWARE_VERSION: ${{ steps.preview.outputs.version }} + LOCALAPPDATA: ${{ runner.temp }}/pio_build_root + shell: bash + run: | + set -euo pipefail + BUILD_DIR="${LOCALAPPDATA}/pio_build/ambit_fw_new/ambit" + for FILE in firmware.bin bootloader.bin partitions.bin; do + test -f "${BUILD_DIR}/${FILE}" + done + + # The build-injected AMBIT_FW_VERSION must be embedded verbatim: it is + # what cmd 33/2, `hello` and the JSON identity report after flashing. + strings "${BUILD_DIR}/firmware.bin" | grep -qx "${FIRMWARE_VERSION}" + + # boot_app0.bin is not a build product: it ships with the Arduino-ESP32 + # core, but the Calibratron's 4-region flash layout needs it at 0xe000. + BOOT_APP0=$(find "${HOME}/.platformio/packages" -path "*framework-arduinoespressif32*" -name boot_app0.bin | head -n 1) + test -n "${BOOT_APP0}" + + mkdir -p dist + APP_NAME="ambit-fw-v${FIRMWARE_VERSION}.bin" + cp "${BUILD_DIR}/firmware.bin" "dist/${APP_NAME}" + cp "${BUILD_DIR}/bootloader.bin" dist/bootloader.bin + cp "${BUILD_DIR}/partitions.bin" dist/partitions.bin + cp "${BOOT_APP0}" dist/boot_app0.bin + # Manifest: downstream flashers (Calibratron) read names, offsets and + # hashes from here instead of hardcoding them. + python3 - "$FIRMWARE_VERSION" "$APP_NAME" <<'EOF' + import hashlib, json, sys + version, app_name = sys.argv[1], sys.argv[2] + def entry(file, offset): + data = open(f"dist/{file}", "rb").read() + return {"file": file, "offset": offset, + "size": len(data), "sha256": hashlib.sha256(data).hexdigest()} + manifest = { + "name": "ambit-iot", + "version": version, + "chip": "esp32c3", + "flash": [ + entry("bootloader.bin", "0x0"), + entry("partitions.bin", "0x8000"), + entry("boot_app0.bin", "0xe000"), + entry(app_name, "0x10000"), + ], + "ota": {"file": app_name}, + } + json.dump(manifest, open("dist/manifest.json", "w"), indent=2) + EOF + + - name: Upload firmware candidate + uses: actions/upload-artifact@v6 + with: + name: firmware-${{ github.event.pull_request.head.sha }} + path: dist/ + if-no-files-found: error + retention-days: 30 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..67b4342 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,344 @@ +name: Release firmware + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: {} + +concurrency: + group: firmware-release + cancel-in-progress: false + +env: + # This workflow is intentionally pinned to ticket 21's qualified candidate. + # Change this only in a reviewed release-process PR for the next firmware. + EXPECTED_VERSION: 1.1.4 + EXPECTED_TAG: v1.1.4 + RELEASE_BRANCH: main + QUALIFIED_PR_NUMBER: 4 + QUALIFIED_PR_HEAD_SHA: ${{ vars.AMBIT_V114_PR_HEAD_SHA }} + QUALIFIED_PR_RUN_ID: ${{ vars.AMBIT_V114_PR_RUN_ID }} + QUALIFIED_ARTIFACT_ID: ${{ vars.AMBIT_V114_ARTIFACT_ID }} + QUALIFIED_APP_SHA256: ${{ vars.AMBIT_V114_APP_SHA256 }} + +jobs: + stage-draft: + name: Stage and prove immutable release draft + runs-on: ubuntu-latest + permissions: + contents: write + actions: read + pull-requests: read + outputs: + staged: ${{ steps.stage-status.outputs.staged }} + proof-artifact-id: ${{ steps.proof-upload.outputs.artifact-id }} + target-sha: ${{ steps.target.outputs.sha }} + steps: + - name: Checkout exact main target + uses: actions/checkout@v6 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Assert release target + id: target + env: + TARGET_SHA: ${{ github.sha }} + shell: bash + run: | + set -euo pipefail + test "$(git rev-parse HEAD)" = "${TARGET_SHA}" + test "${GITHUB_REF}" = "refs/heads/${RELEASE_BRANCH}" + echo "sha=${TARGET_SHA}" >> "${GITHUB_OUTPUT}" + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + + - name: Install pinned release tooling + run: bash tools/install_release_tooling.sh + + - name: Determine exact release + id: preview + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + set -euo pipefail + set +e + npx semantic-release --dry-run --no-ci 2>&1 | tee release-preview.log + RELEASE_STATUS=${PIPESTATUS[0]} + set -e + test "${RELEASE_STATUS}" -eq 0 + VERSION=$(grep -Eo 'The next release version is [0-9]+\.[0-9]+\.[0-9]+' release-preview.log | tail -n 1 | awk '{print $6}' || true) + if [[ -z "${VERSION}" ]]; then + echo "will-release=false" >> "${GITHUB_OUTPUT}" + echo "No firmware release is required." >> "${GITHUB_STEP_SUMMARY}" + exit 0 + fi + test "${VERSION}" = "${EXPECTED_VERSION}" + test "v${VERSION}" = "${EXPECTED_TAG}" + echo "will-release=true" >> "${GITHUB_OUTPUT}" + + - name: Fail closed if tag or release already exists + if: steps.preview.outputs.will-release == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + shell: bash + run: | + set -euo pipefail + gh api --paginate "repos/${REPO}/releases?per_page=100" --slurp > preflight-releases.json + gh api "repos/${REPO}/git/matching-refs/tags/${EXPECTED_TAG}" > preflight-refs.json + python3 tools/release_guard.py preflight \ + --releases-json preflight-releases.json \ + --refs-json preflight-refs.json \ + --tag "${EXPECTED_TAG}" + + - name: Require pinned qualification evidence + if: steps.preview.outputs.will-release == 'true' + shell: bash + run: | + set -euo pipefail + [[ "${QUALIFIED_PR_RUN_ID}" =~ ^[1-9][0-9]*$ ]] + [[ "${QUALIFIED_ARTIFACT_ID}" =~ ^[1-9][0-9]*$ ]] + [[ "${QUALIFIED_PR_HEAD_SHA}" =~ ^[0-9a-f]{40}$ ]] + [[ "${QUALIFIED_APP_SHA256}" =~ ^[0-9a-f]{64}$ ]] + + - name: Verify the pinned qualified PR + if: steps.preview.outputs.will-release == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + shell: bash + run: | + set -euo pipefail + gh api "repos/${REPO}/pulls/${QUALIFIED_PR_NUMBER}" > qualified-pr.json + python3 tools/release_guard.py verify-qualified-pr \ + --pr-json qualified-pr.json \ + --number "${QUALIFIED_PR_NUMBER}" \ + --head-sha "${QUALIFIED_PR_HEAD_SHA}" \ + --branch "${RELEASE_BRANCH}" + + - name: Verify the pinned PR run and artifact + id: artifact + if: steps.preview.outputs.will-release == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + shell: bash + run: | + set -euo pipefail + ARTIFACT_NAME="firmware-${QUALIFIED_PR_HEAD_SHA}" + gh api "repos/${REPO}/actions/artifacts/${QUALIFIED_ARTIFACT_ID}" > artifact.json + python3 tools/release_guard.py select-artifact \ + --artifacts-json artifact.json \ + --name "${ARTIFACT_NAME}" > selected-artifact.json + ARTIFACT_ID=$(jq -r .artifact_id selected-artifact.json) + RUN_ID=$(jq -r .run_id selected-artifact.json) + test "${ARTIFACT_ID}" = "${QUALIFIED_ARTIFACT_ID}" + test "${RUN_ID}" = "${QUALIFIED_PR_RUN_ID}" + gh api "repos/${REPO}/actions/runs/${RUN_ID}" > artifact-run.json + python3 tools/release_guard.py verify-run \ + --run-json artifact-run.json \ + --run-id "${RUN_ID}" \ + --head-sha "${QUALIFIED_PR_HEAD_SHA}" + echo "artifact-id=${ARTIFACT_ID}" >> "${GITHUB_OUTPUT}" + + - name: Download exact PR firmware artifact + if: steps.preview.outputs.will-release == 'true' + uses: actions/download-artifact@v6 + with: + artifact-ids: ${{ steps.artifact.outputs.artifact-id }} + path: release-assets + merge-multiple: true + github-token: ${{ secrets.GITHUB_TOKEN }} + repository: ${{ github.repository }} + run-id: ${{ env.QUALIFIED_PR_RUN_ID }} + + - name: Verify candidate bytes and manifest + if: steps.preview.outputs.will-release == 'true' + run: | + python3 tools/release_guard.py candidate \ + --assets-dir release-assets \ + --version "${EXPECTED_VERSION}" \ + --expected-app-sha256 "${QUALIFIED_APP_SHA256}" > candidate-proof.json + + - name: Create draft and upload assets + if: steps.preview.outputs.will-release == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: npx semantic-release + + - name: Assert draft metadata and download authenticated assets + if: steps.preview.outputs.will-release == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + TARGET_SHA: ${{ github.sha }} + shell: bash + run: | + set -euo pipefail + gh api --paginate "repos/${REPO}/releases?per_page=100" --slurp > staged-releases.json + gh api "repos/${REPO}/git/ref/tags/${EXPECTED_TAG}" > staged-ref.json + mkdir authenticated-release-assets + while IFS=$'\t' read -r ASSET_ID ASSET_NAME; do + gh api -H "Accept: application/octet-stream" \ + "repos/${REPO}/releases/assets/${ASSET_ID}" \ + > "authenticated-release-assets/${ASSET_NAME}" + done < <(python3 tools/release_guard.py release-asset-lines \ + --releases-json staged-releases.json \ + --tag "${EXPECTED_TAG}" \ + --version "${EXPECTED_VERSION}") + python3 tools/release_guard.py make-proof \ + --assets-dir release-assets \ + --downloads-dir authenticated-release-assets \ + --releases-json staged-releases.json \ + --ref-json staged-ref.json \ + --repository "${REPO}" \ + --version "${EXPECTED_VERSION}" \ + --tag "${EXPECTED_TAG}" \ + --target-sha "${TARGET_SHA}" \ + --branch "${RELEASE_BRANCH}" \ + --pr-number "${QUALIFIED_PR_NUMBER}" \ + --pr-head-sha "${QUALIFIED_PR_HEAD_SHA}" \ + --run-id "${QUALIFIED_PR_RUN_ID}" \ + --artifact-id "${QUALIFIED_ARTIFACT_ID}" \ + --app-sha256 "${QUALIFIED_APP_SHA256}" \ + --output release-proof.json + + - name: Upload release proof for protected finalizer + id: proof-upload + if: steps.preview.outputs.will-release == 'true' + uses: actions/upload-artifact@v6 + with: + name: release-proof-${{ github.run_id }} + path: release-proof.json + if-no-files-found: error + retention-days: 30 + + - name: Mark draft staged + id: stage-status + shell: bash + run: | + if [[ "${{ steps.preview.outputs.will-release }}" == "true" ]]; then + echo "staged=true" >> "${GITHUB_OUTPUT}" + echo "Staged and proved ${EXPECTED_TAG}; publication awaits the firmware-release environment." >> "${GITHUB_STEP_SUMMARY}" + else + echo "staged=false" >> "${GITHUB_OUTPUT}" + fi + + publish-finalizer: + name: Publish proved immutable release + needs: stage-draft + if: needs.stage-draft.outputs.staged == 'true' + runs-on: ubuntu-latest + environment: firmware-release + permissions: + contents: write + actions: read + attestations: read + steps: + - name: Checkout proved release target + uses: actions/checkout@v6 + with: + ref: ${{ needs.stage-draft.outputs.target-sha }} + persist-credentials: false + + - name: Download staging proof + uses: actions/download-artifact@v6 + with: + artifact-ids: ${{ needs.stage-draft.outputs.proof-artifact-id }} + path: finalizer-proof + merge-multiple: true + + - name: Revalidate draft and authenticated bytes + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + shell: bash + run: | + set -euo pipefail + gh api --paginate "repos/${REPO}/releases?per_page=100" --slurp > finalizer-pre-releases.json + gh api "repos/${REPO}/git/ref/tags/${EXPECTED_TAG}" > finalizer-pre-ref.json + mkdir finalizer-authenticated-assets + while IFS=$'\t' read -r ASSET_ID ASSET_NAME; do + gh api -H "Accept: application/octet-stream" \ + "repos/${REPO}/releases/assets/${ASSET_ID}" \ + > "finalizer-authenticated-assets/${ASSET_NAME}" + done < <(python3 tools/release_guard.py asset-lines \ + --proof finalizer-proof/release-proof.json --mode api) + python3 tools/release_guard.py verify-proof \ + --proof finalizer-proof/release-proof.json \ + --releases-json finalizer-pre-releases.json \ + --ref-json finalizer-pre-ref.json \ + --phase draft \ + --downloads-dir finalizer-authenticated-assets + + - name: Publish draft exactly once + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + shell: bash + run: | + set -euo pipefail + RELEASE_ID=$(jq -r .release.id finalizer-proof/release-proof.json) + gh api --method PATCH "repos/${REPO}/releases/${RELEASE_ID}" \ + -F draft=false -f make_latest=true > publish-response.json + test "$(jq -r .draft publish-response.json)" = "false" + + - name: Require immutable stable release and anonymous byte identity + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + shell: bash + run: | + set -euo pipefail + RELEASE_ID=$(jq -r .release.id finalizer-proof/release-proof.json) + for ATTEMPT in {1..12}; do + gh api "repos/${REPO}/releases/${RELEASE_ID}" > published-release.json + if jq -e '.draft == false and .prerelease == false and .immutable == true' published-release.json > /dev/null; then + break + fi + if [[ "${ATTEMPT}" -eq 12 ]]; then + echo "::error::Published release did not become immutable" + exit 1 + fi + sleep 5 + done + gh api --paginate "repos/${REPO}/releases?per_page=100" --slurp > finalizer-post-releases.json + gh api "repos/${REPO}/git/ref/tags/${EXPECTED_TAG}" > finalizer-post-ref.json + gh api "repos/${REPO}/releases/latest" > latest-release.json + mkdir finalizer-anonymous-assets + while IFS=$'\t' read -r ASSET_URL ASSET_NAME; do + curl --fail --location --silent --show-error \ + --output "finalizer-anonymous-assets/${ASSET_NAME}" "${ASSET_URL}" + done < <(python3 tools/release_guard.py asset-lines \ + --proof finalizer-proof/release-proof.json --mode browser) + python3 tools/release_guard.py verify-proof \ + --proof finalizer-proof/release-proof.json \ + --releases-json finalizer-post-releases.json \ + --ref-json finalizer-post-ref.json \ + --phase published \ + --latest-json latest-release.json \ + --downloads-dir finalizer-anonymous-assets + + - name: Verify GitHub release attestations + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + shell: bash + run: | + set -euo pipefail + gh --version + gh release verify "${EXPECTED_TAG}" --repo "${REPO}" + while IFS=$'\t' read -r _ ASSET_NAME; do + gh release verify-asset "${EXPECTED_TAG}" \ + "finalizer-anonymous-assets/${ASSET_NAME}" --repo "${REPO}" + done < <(python3 tools/release_guard.py asset-lines \ + --proof finalizer-proof/release-proof.json --mode browser) + echo "Published ${EXPECTED_TAG} exactly once; assets are immutable, latest, and byte-identical." >> "${GITHUB_STEP_SUMMARY}" diff --git a/.gitignore b/.gitignore index 947bc14..8d415e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ -.vscode -.upload.bat -old -*.ipynb -*.py -*.pdf -*.code-workspace \ No newline at end of file +.pio +node_modules/ +package-lock.json +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch +.plans/ +graphify-out/ +data/ \ No newline at end of file diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..50293e8 --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,47 @@ +{ + "branches": ["main"], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { "type": "feat", "release": "minor" }, + { "type": "fix", "release": "patch" }, + { "type": "perf", "release": "patch" }, + { "type": "revert", "release": "patch" }, + { "type": "docs", "release": false }, + { "type": "style", "release": false }, + { "type": "chore", "release": false }, + { "type": "refactor", "release": false }, + { "type": "test", "release": false }, + { "type": "build", "release": false }, + { "type": "ci", "release": false }, + { "breaking": true, "release": "major" } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { "preset": "conventionalcommits" } + ], + [ + "@semantic-release/github", + { + "assets": [ + { "path": "release-assets/manifest.json", "label": "Flash manifest (offsets + sha256)" }, + { "path": "release-assets/ambit-fw-v*.bin", "label": "Application image (OTA / 0x10000)" }, + { "path": "release-assets/bootloader.bin", "label": "Bootloader (0x0)" }, + { "path": "release-assets/partitions.bin", "label": "Partition table (0x8000)" }, + { "path": "release-assets/boot_app0.bin", "label": "OTA data init (0xe000)" } + ], + "successComment": false, + "failComment": false, + "failTitle": false, + "releasedLabels": false, + "addReleases": false, + "draftRelease": true + } + ] + ] +} diff --git a/.vscode/arduino.json b/.vscode/arduino.json deleted file mode 100644 index b21aeb8..0000000 --- a/.vscode/arduino.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "configuration": "UploadSpeed=921600,CDCOnBoot=default,CPUFreq=160,FlashFreq=80,FlashMode=qio,FlashSize=2M,PartitionScheme=minimal,DebugLevel=none,EraseFlash=none,JTAGAdapter=default,ZigbeeMode=default", - "board": "esp32:esp32:esp32c3", - "sketch": "ambit-1.ino", - "port": "COM13", - "output": "../tmp", - "programmer": "esptool", - "monitorPortSettings": { - "settings": { - "port": "COM13", - "baudRate": 115200, - "lineEnding": "\r\n", - "dataBits": 8, - "parity": "none", - "stopBits": "one" - }, - "profile_name": "" - }, - "configurationRequired": true, - "optimize_for_debug": false, - "compile_profile": "ambit", - "useProgrammer": true -} \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 887fc99..0000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "configurations": [ - { - "name": "Arduino", - "includePath": [ - "C:\\Users\\huangj27\\Documents\\Arduino\\libraries/**", - "D:\\AA_Folder\\FW\\ESP32\\Ambit_series\\ambit_1\\ambit-1/**", - "C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32-arduino-libs_idf-release_v5.3-083aad99-v2_e953d9a84a\\esp32c3\\include/**", - "C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32-arduino-libs_idf-release_v5.3-083aad99-v2_e953d9a84a\\esp32c3\\qio_qspi\\include/**", - "C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32_3.1.0_c0417d578482f9af\\cores\\esp32/**", - "C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32_3.1.0_c0417d578482f9af\\variants\\esp32c3/**", - "C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\Adafruit_BusIO_1.17.4_7b46ea3f5fdb9ecd\\Adafruit BusIO/**", - "C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32_3.1.0_c0417d578482f9af\\libraries\\Wire\\src/**", - "C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32_3.1.0_c0417d578482f9af\\libraries\\SPI\\src/**", - "C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32_3.1.0_c0417d578482f9af\\libraries\\Preferences\\src/**" - ], - "forcedInclude": [ - "C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32_3.1.0_c0417d578482f9af\\cores\\esp32\\Arduino.h" - ], - "compilerPath": "C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp-rv32_2405_ee89291512ffd117/bin/riscv32-esp-elf-g++", - "compilerArgs": [ - "C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp-rv32_2405_ee89291512ffd117/bin/riscv32-esp-elf-g++", - "-MMD", - "-c", - "@C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32-arduino-libs_idf-release_v5.3-083aad99-v2_e953d9a84a\\esp32c3/flags/cpp_flags", - "-w", - "-Os", - "-Werror=return-type", - "-DF_CPU=160000000L", - "-DARDUINO=10607", - "-DARDUINO_ESP32C3_DEV", - "-DARDUINO_ARCH_ESP32", - "-DARDUINO_BOARD=\"ESP32C3_DEV\"", - "-DARDUINO_VARIANT=\"esp32c3\"", - "-DARDUINO_PARTITION_minimal", - "-DARDUINO_HOST_OS=\"windows\"", - "-DARDUINO_FQBN=\"esp32:esp32:esp32c3:UploadSpeed=921600,CDCOnBoot=default,CPUFreq=160,FlashFreq=80,FlashMode=qio,FlashSize=4M,PartitionScheme=minimal,DebugLevel=none,EraseFlash=all,JTAGAdapter=default,ZigbeeMode=default\"", - "-DESP32=ESP32", - "-DCORE_DEBUG_LEVEL=0", - "-DARDUINO_USB_MODE=1", - "-DARDUINO_USB_CDC_ON_BOOT=0", - "@C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32-arduino-libs_idf-release_v5.3-083aad99-v2_e953d9a84a\\esp32c3/flags/defines", - "-ID:\\AA_Folder\\FW\\ESP32\\Ambit_series\\ambit_1\\ambit-1", - "-iprefix", - "C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32-arduino-libs_idf-release_v5.3-083aad99-v2_e953d9a84a\\esp32c3/include/", - "@C:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32-arduino-libs_idf-release_v5.3-083aad99-v2_e953d9a84a\\esp32c3/flags/includes", - "-IC:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32-arduino-libs_idf-release_v5.3-083aad99-v2_e953d9a84a\\esp32c3/qio_qspi/include", - "-IC:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32_3.1.0_c0417d578482f9af\\cores\\esp32", - "-IC:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32_3.1.0_c0417d578482f9af\\variants\\esp32c3", - "-IC:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\Adafruit_BusIO_1.17.4_7b46ea3f5fdb9ecd\\Adafruit BusIO", - "-IC:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32_3.1.0_c0417d578482f9af\\libraries\\Wire\\src", - "-IC:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32_3.1.0_c0417d578482f9af\\libraries\\SPI\\src", - "-IC:\\Users\\huangj27\\AppData\\Local\\Arduino15\\internal\\esp32_esp32_3.1.0_c0417d578482f9af\\libraries\\Preferences\\src", - "@D:\\AA_Folder\\FW\\ESP32\\Ambit_series\\ambit_1\\tmp\\build_ambit/build_opt.h", - "@D:\\AA_Folder\\FW\\ESP32\\Ambit_series\\ambit_1\\tmp\\build_ambit/file_opts", - "D:\\AA_Folder\\FW\\ESP32\\Ambit_series\\ambit_1\\tmp\\build_ambit\\sketch\\PAM.cpp", - "-o", - "D:\\AA_Folder\\FW\\ESP32\\Ambit_series\\ambit_1\\tmp\\build_ambit\\sketch\\PAM.cpp.o" - ], - "defines": [ - "F_CPU=160000000L", - "ARDUINO=10607", - "ARDUINO_ESP32C3_DEV", - "ARDUINO_ARCH_ESP32", - "ARDUINO_BOARD=\"ESP32C3_DEV\"", - "ARDUINO_VARIANT=\"esp32c3\"", - "ARDUINO_PARTITION_minimal", - "ARDUINO_HOST_OS=\"windows\"", - "ARDUINO_FQBN=\"esp32:esp32:esp32c3:UploadSpeed=921600,CDCOnBoot=default,CPUFreq=160,FlashFreq=80,FlashMode=qio,FlashSize=4M,PartitionScheme=minimal,DebugLevel=none,EraseFlash=all,JTAGAdapter=default,ZigbeeMode=default\"", - "ESP32=ESP32", - "CORE_DEBUG_LEVEL=0", - "ARDUINO_USB_MODE=1", - "ARDUINO_USB_CDC_ON_BOOT=0", - "ARDUINO_BOARD=\\\"ESP32C3_DEV\\\"\"", - "ARDUINO_VARIANT=\\\"esp32c3\\\"\"", - "ARDUINO_HOST_OS=\\\"windows\\\"\"", - "ARDUINO_FQBN=\\\"esp32:esp32:esp32c3:UploadSpeed=921600,CDCOnBoot=default,CPUFreq=160,FlashFreq=80,FlashMode=qio,FlashSize=4M,PartitionScheme=minimal,DebugLevel=none,EraseFlash=all,JTAGAdapter=default,ZigbeeMode=default\\\"\"", - "ARDUINO_CORE_BUILD", - "USBCON" - ], - "cStandard": "c17", - "cppStandard": "c++17" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/examples/FSM_combined.excalidraw b/.vscode/examples/FSM_combined.excalidraw deleted file mode 100644 index afd4aed..0000000 --- a/.vscode/examples/FSM_combined.excalidraw +++ /dev/null @@ -1,12570 +0,0 @@ -{ - "type": "excalidraw", - "version": 2, - "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", - "elements": [ - { - "type": "rectangle", - "version": 1092, - "versionNonce": 1353254064, - "isDeleted": false, - "id": "pt-9mP3SKkXwwMIxu9wrD", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2271.2276545457325, - "y": 3177.5945953942132, - "strokeColor": "#e03131", - "backgroundColor": "#ffec99", - "width": 1683.3331298828116, - "height": 1121.666755676269, - "seed": 994387473, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [], - "updated": 1714744053936, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 543, - "versionNonce": 769318480, - "isDeleted": false, - "id": "aaz6cNCI_j8N6G1raymwv", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1061.227654545733, - "y": 3157.5945953942132, - "strokeColor": "#e03131", - "backgroundColor": "#ffec99", - "width": 1175.8331298828118, - "height": 1166.666755676269, - "seed": 1024409525, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [], - "updated": 1714744053936, - "link": null, - "locked": false - }, - { - "type": "rectangle", - "version": 169, - "versionNonce": 1485106864, - "isDeleted": false, - "id": "MirtgVDjs3MKh0_VZszei", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -323.8951139904175, - "y": 3657.7246207585376, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 282.3999938964844, - "height": 145.76242065429682, - "seed": 1223074271, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "lwZzuQ1tXelScna8pNPTa" - }, - { - "id": "928BTNxR2z6vRYef32noK", - "type": "arrow" - }, - { - "id": "-WiN9iXrynz3M1JX3xK7v", - "type": "arrow" - }, - { - "id": "2jA6V3d4c1VkKuvHPMw1b", - "type": "arrow" - } - ], - "updated": 1714744053936, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 123, - "versionNonce": 294294608, - "isDeleted": false, - "id": "lwZzuQ1tXelScna8pNPTa", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -240.58310227166749, - "y": 3708.105831085686, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 115.77597045898438, - "height": 45, - "seed": 823532575, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053936, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "SLEEP", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "MirtgVDjs3MKh0_VZszei", - "originalText": "SLEEP", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "diamond", - "version": 273, - "versionNonce": 290691248, - "isDeleted": false, - "id": "L-iASTWFuRMdMSTyvh7t4", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 36.90490432012939, - "y": 3646.6872367253345, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 184.79998779296875, - "height": 184.79998779296875, - "seed": 452865983, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "aNfbUypu6HzNoJJLtdZDd" - }, - { - "id": "928BTNxR2z6vRYef32noK", - "type": "arrow" - }, - { - "id": "-WiN9iXrynz3M1JX3xK7v", - "type": "arrow" - }, - { - "id": "OvEZk-8scrlXLuGmE7FPb", - "type": "arrow" - } - ], - "updated": 1714744053936, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 218, - "versionNonce": 299076176, - "isDeleted": false, - "id": "aNfbUypu6HzNoJJLtdZDd", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 100.5849122546997, - "y": 3713.8872336735767, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 57.03997802734375, - "height": 50, - "seed": 639092927, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053936, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "UART\ninput", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "L-iASTWFuRMdMSTyvh7t4", - "originalText": "UART\ninput", - "lineHeight": 1.25, - "baseline": 46 - }, - { - "type": "arrow", - "version": 317, - "versionNonce": 1321009840, - "isDeleted": false, - "id": "928BTNxR2z6vRYef32noK", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -25.65747360955808, - "y": 3735.462389115326, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 50.90109560222547, - "height": 1.4532686988250134, - "seed": 494747313, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053936, - "link": null, - "locked": false, - "startBinding": { - "elementId": "MirtgVDjs3MKh0_VZszei", - "focus": -0.003106909499580352, - "gap": 15.837646484375 - }, - "endBinding": { - "elementId": "L-iASTWFuRMdMSTyvh7t4", - "focus": -0.008652201328443859, - "gap": 11.861755096939916 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 50.90109560222547, - 1.4532686988250134 - ] - ] - }, - { - "type": "rectangle", - "version": 390, - "versionNonce": 1234026576, - "isDeleted": false, - "id": "ViWZZy9Q7c0UYyERaQ5M0", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 376.50489516485595, - "y": 3665.5310019841236, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 282.3999938964844, - "height": 145.76242065429682, - "seed": 347662161, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "qK6m5n9Nbto6yahbct-jf" - }, - { - "id": "OvEZk-8scrlXLuGmE7FPb", - "type": "arrow" - }, - { - "id": "3TDl2MJg2FQvz-gbZsQQP", - "type": "arrow" - }, - { - "id": "B0Fc9vslccT_uvolbahsS", - "type": "arrow" - }, - { - "id": "G5nVT5VA8UUPppUVgsIHm", - "type": "arrow" - }, - { - "id": "iXpExTgBKgqPR5laoFls7", - "type": "arrow" - }, - { - "id": "QHTBVNXQrucCR_A6H0uaZ", - "type": "arrow" - }, - { - "id": "1pwwcSYOnzOIeKOG9nO85", - "type": "arrow" - } - ], - "updated": 1714744053936, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 345, - "versionNonce": 795421872, - "isDeleted": false, - "id": "qK6m5n9Nbto6yahbct-jf", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 470.94090743292236, - "y": 3715.912212311272, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 93.52796936035156, - "height": 45, - "seed": 1822545201, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053936, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "IDLE", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "ViWZZy9Q7c0UYyERaQ5M0", - "originalText": "IDLE", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 233, - "versionNonce": 949040720, - "isDeleted": false, - "id": "-WiN9iXrynz3M1JX3xK7v", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 135.30450148809814, - "y": 3848.124614655022, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 330.4000244140625, - "height": 115.99996948242188, - "seed": 1609308639, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "I3ao2m-5w5FzW_XrAW_Eo" - } - ], - "updated": 1714744053936, - "link": null, - "locked": false, - "startBinding": { - "elementId": "L-iASTWFuRMdMSTyvh7t4", - "focus": -0.07519278226210531, - "gap": 16.006771544615603 - }, - "endBinding": { - "elementId": "MirtgVDjs3MKh0_VZszei", - "focus": 0.09224382420379566, - "gap": 20.6375732421875 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -0.800048828125, - 91.99996948242188 - ], - [ - -329.60003662109375, - 87.19998168945312 - ], - [ - -330.4000244140625, - -24 - ] - ] - }, - { - "type": "text", - "version": 28, - "versionNonce": 1023415984, - "isDeleted": false, - "id": "I3ao2m-5w5FzW_XrAW_Eo", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -54.55552963983155, - "y": 3915.2245902409595, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 49.319976806640625, - "height": 45, - "seed": 979230431, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053936, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "NO", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "-WiN9iXrynz3M1JX3xK7v", - "originalText": "NO", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 114, - "versionNonce": 599125072, - "isDeleted": false, - "id": "OvEZk-8scrlXLuGmE7FPb", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 235.7833449662752, - "y": 3741.649261986373, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 117.12113210776045, - "height": 1.4778070768534235, - "seed": 2083668433, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053936, - "link": null, - "locked": false, - "startBinding": { - "elementId": "L-iASTWFuRMdMSTyvh7t4", - "focus": 0.037201062302432523, - "gap": 14.30967642723229 - }, - "endBinding": { - "elementId": "ViWZZy9Q7c0UYyERaQ5M0", - "focus": 0.004288217994394458, - "gap": 23.600418090820312 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 117.12113210776045, - -1.4778070768534235 - ] - ] - }, - { - "type": "diamond", - "version": 463, - "versionNonce": 204062896, - "isDeleted": false, - "id": "jj_yHGzqEfb3EVrV7IAkE", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 765.1044587634888, - "y": 3647.674632965569, - "strokeColor": "#1971c2", - "backgroundColor": "#ffc9c9", - "width": 184.79998779296875, - "height": 184.79998779296875, - "seed": 1264239377, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "divVu8Qqns3cT8RUrS4w7" - }, - { - "id": "3TDl2MJg2FQvz-gbZsQQP", - "type": "arrow" - }, - { - "id": "1K2QjfeA0GWeHrjWOTtMK", - "type": "arrow" - }, - { - "id": "zKHIolzbkZXnArU9Xcx6Q", - "type": "arrow" - }, - { - "id": "L50BGCy0awqTIQHghyt9z", - "type": "arrow" - } - ], - "updated": 1714744053936, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 407, - "versionNonce": 1960043088, - "isDeleted": false, - "id": "divVu8Qqns3cT8RUrS4w7", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 832.8344697498169, - "y": 3714.874629913811, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 48.939971923828125, - "height": 50, - "seed": 1804666641, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053936, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Wake\n(211)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "jj_yHGzqEfb3EVrV7IAkE", - "originalText": "Wake\n(211)", - "lineHeight": 1.25, - "baseline": 46 - }, - { - "type": "diamond", - "version": 610, - "versionNonce": 1552820912, - "isDeleted": false, - "id": "3nOMpYEVbfxy5YlUz3RSc", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 768.3044099353638, - "y": 3878.449581085686, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 184.79998779296875, - "height": 184.79998779296875, - "seed": 721195647, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "w3PJaAR1UFs4J9cI-jX-i" - }, - { - "id": "1K2QjfeA0GWeHrjWOTtMK", - "type": "arrow" - }, - { - "id": "2jA6V3d4c1VkKuvHPMw1b", - "type": "arrow" - }, - { - "id": "3qj6dE7YV5VkyyM-orVEg", - "type": "arrow" - } - ], - "updated": 1714744053936, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 576, - "versionNonce": 448993360, - "isDeleted": false, - "id": "w3PJaAR1UFs4J9cI-jX-i", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 823.5144471668091, - "y": 3933.1495780339283, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 73.97991943359375, - "height": 75, - "seed": 1742989983, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053936, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Complet\ne\n240", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "3nOMpYEVbfxy5YlUz3RSc", - "originalText": "Complete\n240", - "lineHeight": 1.25, - "baseline": 71 - }, - { - "type": "arrow", - "version": 93, - "versionNonce": 953504944, - "isDeleted": false, - "id": "3TDl2MJg2FQvz-gbZsQQP", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 669.7044038318481, - "y": 3737.208992925008, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 89.00246009566376, - "height": 2.7166506316098094, - "seed": 1534593407, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "yMC16ViMT7oPtxH6R0Uqb" - } - ], - "updated": 1714744053936, - "link": null, - "locked": false, - "startBinding": { - "elementId": "ViWZZy9Q7c0UYyERaQ5M0", - "focus": -0.07710905772468238, - "gap": 10.799514770507812 - }, - "endBinding": { - "elementId": "jj_yHGzqEfb3EVrV7IAkE", - "focus": -0.031024322165604547, - "gap": 6.399329317250107 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 89.00246009566376, - 2.7166506316098094 - ] - ] - }, - { - "type": "text", - "version": 30, - "versionNonce": 1767607888, - "isDeleted": false, - "id": "yMC16ViMT7oPtxH6R0Uqb", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 690.3204316638794, - "y": 3728.92461770678, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 45.16796875, - "height": 20, - "seed": 323450367, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053936, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "100ms", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "3TDl2MJg2FQvz-gbZsQQP", - "originalText": "100ms", - "lineHeight": 1.25, - "baseline": 17 - }, - { - "type": "arrow", - "version": 196, - "versionNonce": 380059312, - "isDeleted": false, - "id": "1K2QjfeA0GWeHrjWOTtMK", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 922.9216007221565, - "y": 3790.907295694401, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 3.011202334860741, - "height": 123.63337077368078, - "seed": 1051573759, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053936, - "link": null, - "locked": false, - "startBinding": { - "elementId": "jj_yHGzqEfb3EVrV7IAkE", - "focus": -0.6948033911504963, - "gap": 16.864371572017816 - }, - "endBinding": { - "elementId": "3nOMpYEVbfxy5YlUz3RSc", - "focus": 0.7207776830617192, - "gap": 20.603192203785753 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 3.011202334860741, - 123.63337077368078 - ] - ] - }, - { - "type": "arrow", - "version": 226, - "versionNonce": 2037728336, - "isDeleted": false, - "id": "2jA6V3d4c1VkKuvHPMw1b", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 807.3043794177856, - "y": 4030.5246085515064, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 1068.0000610351562, - "height": 212, - "seed": 510787793, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "o9VTEhz_yQ6WEf6Z2pU_w" - } - ], - "updated": 1714744053937, - "link": null, - "locked": false, - "startBinding": { - "elementId": "3nOMpYEVbfxy5YlUz3RSc", - "focus": -0.6419376269810059, - "gap": 14.619478017323388 - }, - "endBinding": { - "elementId": "MirtgVDjs3MKh0_VZszei", - "focus": 0.5155642464843946, - "gap": 22.237579345703125 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -1068.0000610351562, - 7.20001220703125 - ], - [ - -1064.8000793457031, - -204.79998779296875 - ] - ] - }, - { - "type": "text", - "version": 33, - "versionNonce": 1990977712, - "isDeleted": false, - "id": "o9VTEhz_yQ6WEf6Z2pU_w", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -295.55965714227295, - "y": 4027.7246207585376, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 69.72795104980469, - "height": 20, - "seed": 938218833, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "No signal", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "2jA6V3d4c1VkKuvHPMw1b", - "originalText": "No signal", - "lineHeight": 1.25, - "baseline": 17 - }, - { - "type": "rectangle", - "version": 399, - "versionNonce": 149437008, - "isDeleted": false, - "id": "fi4W3ItbbvRhqtjyzb4Dx", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 725.304211571106, - "y": 4158.218410431389, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 282.3999938964844, - "height": 145.76242065429682, - "seed": 1867227313, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "xDQjHsUQjsjhRddDXUFxw" - }, - { - "id": "3qj6dE7YV5VkyyM-orVEg", - "type": "arrow" - } - ], - "updated": 1714744053937, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 372, - "versionNonce": 76239536, - "isDeleted": false, - "id": "xDQjHsUQjsjhRddDXUFxw", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 779.8342484973755, - "y": 4208.599620758538, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 173.3399200439453, - "height": 45, - "seed": 1263952529, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "Completed", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "fi4W3ItbbvRhqtjyzb4Dx", - "originalText": "Completed", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 74, - "versionNonce": 1519153232, - "isDeleted": false, - "id": "3qj6dE7YV5VkyyM-orVEg", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 860.1044282459106, - "y": 4074.5246085515064, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 3.199951171875, - "height": 68, - "seed": 220484799, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "startBinding": { - "elementId": "3nOMpYEVbfxy5YlUz3RSc", - "focus": 0.05929357805215138, - "gap": 8.396903816183467 - }, - "endBinding": { - "elementId": "fi4W3ItbbvRhqtjyzb4Dx", - "focus": 0.0066953295810437225, - "gap": 15.693801879882812 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 3.199951171875, - 68 - ] - ] - }, - { - "type": "rectangle", - "version": 640, - "versionNonce": 921533616, - "isDeleted": false, - "id": "KfH21YEUEsYkyEZLKCAZB", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1132.5045289539185, - "y": 3711.393398224358, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 282.3999938964844, - "height": 64.74990844726557, - "seed": 2060753777, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "_zExFCe3_MOPeE1yrVxbX" - }, - { - "id": "zKHIolzbkZXnArU9Xcx6Q", - "type": "arrow" - }, - { - "id": "-QGeO6hJY6qLAm44eYDdi", - "type": "arrow" - } - ], - "updated": 1714744053937, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 625, - "versionNonce": 372893264, - "isDeleted": false, - "id": "_zExFCe3_MOPeE1yrVxbX", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1159.6565598987427, - "y": 3721.2683524479908, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 228.09593200683594, - "height": 45, - "seed": 1500644689, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "Length array", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "KfH21YEUEsYkyEZLKCAZB", - "originalText": "Length array", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 366, - "versionNonce": 1870292656, - "isDeleted": false, - "id": "zKHIolzbkZXnArU9Xcx6Q", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 960.6335237489178, - "y": 3746.168708669787, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 155.68352809318424, - "height": 0.9221920678483002, - "seed": 1167207999, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "startBinding": { - "elementId": "jj_yHGzqEfb3EVrV7IAkE", - "gap": 12.339000384193383, - "focus": 0.07253845745618966 - }, - "endBinding": { - "elementId": "KfH21YEUEsYkyEZLKCAZB", - "gap": 16.187477111816392, - "focus": -0.016436510554634826 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 155.68352809318424, - -0.9221920678483002 - ] - ] - }, - { - "type": "ellipse", - "version": 228, - "versionNonce": 684235856, - "isDeleted": false, - "id": "aIvbGx1MfjAb_6oY4CqMb", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1145.704342796692, - "y": 3792.237163483147, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 115.2000732421875, - "height": 115.2000732421875, - "seed": 249772767, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "NMcqlJsEyYXR0Zs5kRI9l" - }, - { - "id": "QHTBVNXQrucCR_A6H0uaZ", - "type": "arrow" - } - ], - "updated": 1714744053937, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 179, - "versionNonce": 450498736, - "isDeleted": false, - "id": "NMcqlJsEyYXR0Zs5kRI9l", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1184.2430250821785, - "y": 3839.607823612872, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 37.66395568847656, - "height": 20, - "seed": 2107016959, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "flush", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "aIvbGx1MfjAb_6oY4CqMb", - "originalText": "flush", - "lineHeight": 1.25, - "baseline": 17 - }, - { - "type": "ellipse", - "version": 307, - "versionNonce": 605065808, - "isDeleted": false, - "id": "4uG_zS5TMGWF1M74StXiw", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1272.9043550037231, - "y": 3792.237194000725, - "strokeColor": "#1971c2", - "backgroundColor": "#b2f2bb", - "width": 115.2000732421875, - "height": 115.2000732421875, - "seed": 249869343, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "8r-DwMJpdfHD8ndMm-YNi" - }, - { - "id": "O4MRTCghp9QBztgDBgQdC", - "type": "arrow" - } - ], - "updated": 1714744053937, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 265, - "versionNonce": 1354005168, - "isDeleted": false, - "id": "8r-DwMJpdfHD8ndMm-YNi", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1306.5470335660652, - "y": 3829.60785413045, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 47.455963134765625, - "height": 40, - "seed": 492402399, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "Awake\n210", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "4uG_zS5TMGWF1M74StXiw", - "originalText": "Awake\n210", - "lineHeight": 1.25, - "baseline": 37 - }, - { - "type": "diamond", - "version": 524, - "versionNonce": 518637648, - "isDeleted": false, - "id": "RJxANj4RhJ_aciZMjF5IY", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1524.7046784900513, - "y": 3655.8872184147876, - "strokeColor": "#1971c2", - "backgroundColor": "#ffc9c9", - "width": 184.79998779296875, - "height": 184.79998779296875, - "seed": 1837615697, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "SnK8aSJAtTHmCgIHKqEq4" - }, - { - "id": "-QGeO6hJY6qLAm44eYDdi", - "type": "arrow" - }, - { - "id": "QHTBVNXQrucCR_A6H0uaZ", - "type": "arrow" - }, - { - "id": "NGBHlZ4_l_GHyKePYfaeX", - "type": "arrow" - }, - { - "id": "ObbXkkUi4RX8Luqzn8JMg", - "type": "arrow" - }, - { - "id": "SPFSlS5fEkc_qQBkM9eLq", - "type": "arrow" - } - ], - "updated": 1714744053937, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 484, - "versionNonce": 1941021872, - "isDeleted": false, - "id": "SnK8aSJAtTHmCgIHKqEq4", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1580.174702598938, - "y": 3710.58721536303, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 73.45994567871094, - "height": 75, - "seed": 798775345, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Length \narray\n(212)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "RJxANj4RhJ_aciZMjF5IY", - "originalText": "Length array\n(212)", - "lineHeight": 1.25, - "baseline": 71 - }, - { - "type": "arrow", - "version": 86, - "versionNonce": 355221072, - "isDeleted": false, - "id": "-QGeO6hJY6qLAm44eYDdi", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1425.7045259021606, - "y": 3746.9389775637524, - "strokeColor": "#1e1e1e", - "backgroundColor": "#b2f2bb", - "width": 90.5076695043881, - "height": 1.519763803449223, - "seed": 242224927, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "PaRBQL0YM0cBiapoHkwYC" - } - ], - "updated": 1714744053937, - "link": null, - "locked": false, - "startBinding": { - "elementId": "KfH21YEUEsYkyEZLKCAZB", - "focus": 0.020934024143426978, - "gap": 10.800003051757812 - }, - "endBinding": { - "elementId": "RJxANj4RhJ_aciZMjF5IY", - "focus": -0.020191233413333516, - "gap": 8.494215157423724 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 90.5076695043881, - 1.519763803449223 - ] - ] - }, - { - "type": "text", - "version": 27, - "versionNonce": 1366920880, - "isDeleted": false, - "id": "PaRBQL0YM0cBiapoHkwYC", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1439.2805223010864, - "y": 3736.018428741936, - "strokeColor": "#1e1e1e", - "backgroundColor": "#b2f2bb", - "width": 44.04795837402344, - "height": 20, - "seed": 227682673, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "150ms", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "-QGeO6hJY6qLAm44eYDdi", - "originalText": "150ms", - "lineHeight": 1.25, - "baseline": 17 - }, - { - "type": "ellipse", - "version": 303, - "versionNonce": 1202713680, - "isDeleted": false, - "id": "qD194lAbU6OCv9YNsEdgc", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1791.7044648670044, - "y": 3688.6621207585376, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 115.2000732421875, - "height": 115.2000732421875, - "seed": 685827615, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "67h2ln-uEeQlwJso1_aJE" - }, - { - "id": "NGBHlZ4_l_GHyKePYfaeX", - "type": "arrow" - }, - { - "id": "bHaGLZ3mcvx3uoIcm7J3S", - "type": "arrow" - }, - { - "id": "SPFSlS5fEkc_qQBkM9eLq", - "type": "arrow" - } - ], - "updated": 1714744053937, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 278, - "versionNonce": 1119107248, - "isDeleted": false, - "id": "67h2ln-uEeQlwJso1_aJE", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1826.3791411100106, - "y": 3726.0327808882626, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 45.3919677734375, - "height": 40, - "seed": 960285247, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "get \nlength", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "qD194lAbU6OCv9YNsEdgc", - "originalText": "get length", - "lineHeight": 1.25, - "baseline": 37 - }, - { - "type": "arrow", - "version": 592, - "versionNonce": 474802768, - "isDeleted": false, - "id": "QHTBVNXQrucCR_A6H0uaZ", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1615.2045259021606, - "y": 3655.212230621819, - "strokeColor": "#1e1e1e", - "backgroundColor": "#b2f2bb", - "width": 1025.9715140206472, - "height": 72.95533970424094, - "seed": 1445721521, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "Q3sy61C7uU4rY6u7Sca8j" - } - ], - "updated": 1714744053937, - "link": null, - "locked": false, - "startBinding": { - "elementId": "RJxANj4RhJ_aciZMjF5IY", - "focus": 0.02841037195942465, - "gap": 1.820894909975678 - }, - "endBinding": { - "elementId": "ViWZZy9Q7c0UYyERaQ5M0", - "focus": 0.45298152656145046, - "gap": 9.350919451032496 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -3.5, - -71.98748779296875 - ], - [ - -1021.8714773995533, - -61.47494070870562 - ], - [ - -1025.9715140206472, - 0.9678519112721915 - ] - ] - }, - { - "type": "text", - "version": 34, - "versionNonce": 1258922672, - "isDeleted": false, - "id": "Q3sy61C7uU4rY6u7Sca8j", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1356.144528343567, - "y": 3958.9247245183033, - "strokeColor": "#1e1e1e", - "backgroundColor": "#b2f2bb", - "width": 90.3199462890625, - "height": 20, - "seed": 1409966257, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "no response", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "QHTBVNXQrucCR_A6H0uaZ", - "originalText": "no response", - "lineHeight": 1.25, - "baseline": 17 - }, - { - "type": "arrow", - "version": 78, - "versionNonce": 2111512656, - "isDeleted": false, - "id": "NGBHlZ4_l_GHyKePYfaeX", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1723.4362670438531, - "y": 3750.8672188472137, - "strokeColor": "#1e1e1e", - "backgroundColor": "#b2f2bb", - "width": 57.44773159596252, - "height": 1.8192351632887949, - "seed": 1295562431, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "startBinding": { - "elementId": "RJxANj4RhJ_aciZMjF5IY", - "focus": 0.061761125756669914, - "gap": 14.168483810371228 - }, - "endBinding": { - "elementId": "qD194lAbU6OCv9YNsEdgc", - "focus": -0.010743030743175907, - "gap": 10.877156941504424 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 57.44773159596252, - -1.8192351632887949 - ] - ] - }, - { - "type": "diamond", - "version": 107, - "versionNonce": 958516400, - "isDeleted": false, - "id": "dwSyGncFb0E5Cqk0pkmyg", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1985.7042817615356, - "y": 3645.2997855534595, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 205.38748168945312, - "height": 205.38748168945312, - "seed": 396803697, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "OxXFhnNB5cEBfpuzdFUWe" - }, - { - "id": "bHaGLZ3mcvx3uoIcm7J3S", - "type": "arrow" - }, - { - "id": "ePBq6IfgOVxC4L3VUSArw", - "type": "arrow" - }, - { - "id": "siYv23QE5fZuK8S_aLGSW", - "type": "arrow" - }, - { - "id": "VwKQnf_zVtP_2qxBcKzUw", - "type": "arrow" - } - ], - "updated": 1714744053937, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 54, - "versionNonce": 1009948240, - "isDeleted": false, - "id": "OxXFhnNB5cEBfpuzdFUWe", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 2042.9751710437622, - "y": 3725.646655975823, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 91.15196228027344, - "height": 45, - "seed": 727800511, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053937, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "check", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "dwSyGncFb0E5Cqk0pkmyg", - "originalText": "check", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "ellipse", - "version": 429, - "versionNonce": 1185199792, - "isDeleted": false, - "id": "0XOIdMglnzJdbVbiI4UlR", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1796.082075815355, - "y": 3451.263554406541, - "strokeColor": "#1971c2", - "backgroundColor": "#b2f2bb", - "width": 115.2000732421875, - "height": 115.2000732421875, - "seed": 1088946879, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "fNg-UCz9figTJYVT-DfOl" - }, - { - "id": "OBwdSuILKZp4xw1epS01V", - "type": "arrow" - }, - { - "id": "fNlhld2BS4snfpZ0Sn2La", - "type": "arrow" - } - ], - "updated": 1714744053938, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 399, - "versionNonce": 1814776912, - "isDeleted": false, - "id": "fNg-UCz9figTJYVT-DfOl", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1832.3967514480096, - "y": 3488.634214536266, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 42.111968994140625, - "height": 40, - "seed": 1199002335, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053938, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "reset\n222", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "0XOIdMglnzJdbVbiI4UlR", - "originalText": "reset\n222", - "lineHeight": 1.25, - "baseline": 37 - }, - { - "type": "arrow", - "version": 65, - "versionNonce": 943349936, - "isDeleted": false, - "id": "bHaGLZ3mcvx3uoIcm7J3S", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1921.637767004591, - "y": 3747.4149894108814, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 48.888888888888914, - "height": 1.7777845594619066, - "seed": 1709095825, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053938, - "link": null, - "locked": false, - "startBinding": { - "elementId": "qD194lAbU6OCv9YNsEdgc", - "focus": -0.02563371081574025, - "gap": 14.742415106586463 - }, - "endBinding": { - "elementId": "dwSyGncFb0E5Cqk0pkmyg", - "focus": -0.05341606406134963, - "gap": 11.580198264208377 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 48.888888888888914, - 1.7777845594619066 - ] - ] - }, - { - "type": "ellipse", - "version": 382, - "versionNonce": 1201488464, - "isDeleted": false, - "id": "RLGE222iSRNZwVPwD-ZOS", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1934.4820663209973, - "y": 3453.551135108581, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 115.2000732421875, - "height": 115.2000732421875, - "seed": 406583551, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "RPfqH37NtfweHtDKqAEwS" - }, - { - "id": "ePBq6IfgOVxC4L3VUSArw", - "type": "arrow" - } - ], - "updated": 1714744053938, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 333, - "versionNonce": 2106247856, - "isDeleted": false, - "id": "RPfqH37NtfweHtDKqAEwS", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1973.020748606484, - "y": 3500.921795238306, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 37.66395568847656, - "height": 20, - "seed": 1970438431, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053938, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "flush", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "RLGE222iSRNZwVPwD-ZOS", - "originalText": "flush", - "lineHeight": 1.25, - "baseline": 17 - }, - { - "type": "arrow", - "version": 98, - "versionNonce": 224426064, - "isDeleted": false, - "id": "ePBq6IfgOVxC4L3VUSArw", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2088.7489052424385, - "y": 3638.970572093173, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 23.111165364583485, - "height": 126.22224595811622, - "seed": 757944017, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053938, - "link": null, - "locked": false, - "startBinding": { - "elementId": "dwSyGncFb0E5Cqk0pkmyg", - "focus": -0.026487875877383715, - "gap": 4.723541248786674 - }, - "endBinding": { - "elementId": "RLGE222iSRNZwVPwD-ZOS", - "focus": 0.09458042699230661, - "gap": 19.55116601669355 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 3.555501302083485, - -126.22224595811622 - ], - [ - -19.5556640625, - -125.33335367838527 - ] - ] - }, - { - "type": "arrow", - "version": 166, - "versionNonce": 1898188880, - "isDeleted": false, - "id": "OBwdSuILKZp4xw1epS01V", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1781.1932954334106, - "y": 3508.303864736403, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 275.76749030962856, - "height": 8.203961378917938, - "seed": 1538388625, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714746086744, - "link": null, - "locked": false, - "startBinding": { - "elementId": "0XOIdMglnzJdbVbiI4UlR", - "focus": -0.027709669026886503, - "gap": 14.890941328034792 - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -275.76749030962856, - -8.203961378917938 - ] - ] - }, - { - "type": "arrow", - "version": 216, - "versionNonce": 1367873616, - "isDeleted": false, - "id": "B0Fc9vslccT_uvolbahsS", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1466.8035830201425, - "y": 3496.9651357132116, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 937.1658024521832, - "height": 150.00538212648962, - "seed": 2098559761, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714746088585, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": { - "elementId": "ViWZZy9Q7c0UYyERaQ5M0", - "focus": 0.08451054329154957, - "gap": 18.56048414442239 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -937.1658024521832, - 7.78315990426745 - ], - [ - -937.1658024521832, - 150.00538212648962 - ] - ] - }, - { - "type": "arrow", - "version": 299, - "versionNonce": 1010103376, - "isDeleted": false, - "id": "AIfWh0zeQmsdxyozN0htX", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1484.7773382782707, - "y": 3483.2459822560527, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 2.536415323688516, - "height": 232.25600776487545, - "seed": 909467103, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714746090050, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": { - "elementId": "nv-KG2JPAMopF0URTfGuD", - "focus": 0.0006375270414996052, - "gap": 12.155510796441149 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -2.536415323688516, - -232.25600776487545 - ] - ] - }, - { - "type": "rectangle", - "version": 251, - "versionNonce": 2080479824, - "isDeleted": false, - "id": "nv-KG2JPAMopF0URTfGuD", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1438.5711844308066, - "y": 3155.7511371430874, - "strokeColor": "#e03131", - "backgroundColor": "#ffc9c9", - "width": 86.2221950954861, - "height": 83.08332655164895, - "seed": 1872529695, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "fWh8fw65I9uHbHGXLcsa8" - }, - { - "id": "AIfWh0zeQmsdxyozN0htX", - "type": "arrow" - } - ], - "updated": 1714744053938, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 215, - "versionNonce": 1916269232, - "isDeleted": false, - "id": "fWh8fw65I9uHbHGXLcsa8", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1463.1602940635107, - "y": 3174.792800418912, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 37.043975830078125, - "height": 45, - "seed": 1210514975, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053938, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "-5", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "nv-KG2JPAMopF0URTfGuD", - "originalText": "-5", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "rectangle", - "version": 777, - "versionNonce": 1668927568, - "isDeleted": false, - "id": "yNZIuCW9fYJaiSxldyEzO", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2353.9857146248014, - "y": 3717.151095920136, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 282.3999938964844, - "height": 64.74990844726557, - "seed": 2027580849, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "N1vwXFEt4e3ZP3rjJ-f2E" - }, - { - "id": "siYv23QE5fZuK8S_aLGSW", - "type": "arrow" - }, - { - "id": "h0EYmx16SKh0tRtXR5XhJ", - "type": "arrow" - }, - { - "id": "R_t8UO3YLfP0qZT237eff", - "type": "arrow" - }, - { - "id": "ObbXkkUi4RX8Luqzn8JMg", - "type": "arrow" - } - ], - "updated": 1714744053938, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 764, - "versionNonce": 101063856, - "isDeleted": false, - "id": "N1vwXFEt4e3ZP3rjJ-f2E", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2407.5437361702116, - "y": 3727.0260501437688, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 175.28395080566406, - "height": 45, - "seed": 864259985, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053938, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "Get data", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "yNZIuCW9fYJaiSxldyEzO", - "originalText": "Get data", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "ellipse", - "version": 258, - "versionNonce": 1518028368, - "isDeleted": false, - "id": "VQlEEltnpBfObZWSn_WBA", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 2378.84284658281, - "y": 3806.49748787047, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 115.2000732421875, - "height": 115.2000732421875, - "seed": 1345305663, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "uu50DfbD7jrKocfnBIPZy" - } - ], - "updated": 1714744053938, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 210, - "versionNonce": 310314672, - "isDeleted": false, - "id": "uu50DfbD7jrKocfnBIPZy", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 2417.3815288682968, - "y": 3853.868148000195, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 37.66395568847656, - "height": 20, - "seed": 1485560927, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053938, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "flush", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "VQlEEltnpBfObZWSn_WBA", - "originalText": "flush", - "lineHeight": 1.25, - "baseline": 17 - }, - { - "type": "ellipse", - "version": 333, - "versionNonce": 501087312, - "isDeleted": false, - "id": "Mdil-sv7Ec0Ncq-XpaH48", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 2506.0428587898414, - "y": 3806.4975183880483, - "strokeColor": "#1971c2", - "backgroundColor": "#b2f2bb", - "width": 115.2000732421875, - "height": 115.2000732421875, - "seed": 481228927, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "VDtyhPE1d6rpvutGa5x_e" - }, - { - "id": "U2EvPXeHbkdhv1IajJrxn", - "type": "arrow" - } - ], - "updated": 1714744053938, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 305, - "versionNonce": 877310128, - "isDeleted": false, - "id": "VDtyhPE1d6rpvutGa5x_e", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 2539.973531736949, - "y": 3843.8681785177732, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 46.879974365234375, - "height": 40, - "seed": 39424159, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053938, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "Ready\n200", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Mdil-sv7Ec0Ncq-XpaH48", - "originalText": "Ready\n200", - "lineHeight": 1.25, - "baseline": 37 - }, - { - "type": "arrow", - "version": 52, - "versionNonce": 259650128, - "isDeleted": false, - "id": "siYv23QE5fZuK8S_aLGSW", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2207.7572098989367, - "y": 3745.276085021001, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 134.857177734375, - "height": 0, - "seed": 1103405215, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053938, - "link": null, - "locked": false, - "startBinding": { - "elementId": "dwSyGncFb0E5Cqk0pkmyg", - "focus": -0.026461606664945167, - "gap": 13.70577142012965 - }, - "endBinding": { - "elementId": "yNZIuCW9fYJaiSxldyEzO", - "focus": 0.13127323959782808, - "gap": 11.371326991489696 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 134.857177734375, - 0 - ] - ] - }, - { - "type": "ellipse", - "version": 308, - "versionNonce": 856758960, - "isDeleted": false, - "id": "NYb28wO65_lvUaT1Cockk", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 2719.300065297932, - "y": 3698.479576231939, - "strokeColor": "#1971c2", - "backgroundColor": "#ffc9c9", - "width": 115.2000732421875, - "height": 115.2000732421875, - "seed": 1502846335, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "LjxP0wGHK-eX-3WNTP8ZD" - }, - { - "id": "h0EYmx16SKh0tRtXR5XhJ", - "type": "arrow" - }, - { - "id": "IHs7M-PwfAD5r15fGjb9t", - "type": "arrow" - }, - { - "id": "bLoyDI7x4qG_GKX6CeMT3", - "type": "arrow" - } - ], - "updated": 1714744053938, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 272, - "versionNonce": 371664976, - "isDeleted": false, - "id": "LjxP0wGHK-eX-3WNTP8ZD", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 2742.118738733321, - "y": 3720.850236361664, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 69.10397338867188, - "height": 70, - "seed": 1700894111, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053938, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 1, - "text": "Get \ndata", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "NYb28wO65_lvUaT1Cockk", - "originalText": "Get data", - "lineHeight": 1.25, - "baseline": 64 - }, - { - "type": "ellipse", - "version": 533, - "versionNonce": 561661104, - "isDeleted": false, - "id": "iBZRtI2xGc5MgU0R2emqB", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 3218.72842746311, - "y": 3687.5153951493503, - "strokeColor": "#1971c2", - "backgroundColor": "#ffc9c9", - "width": 140.3428955078125, - "height": 144.8393031529018, - "seed": 66654737, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "0ONVDP4A5ZJaTQOKliW_p" - }, - { - "id": "CqJZf-4HS85UA4KXz_pP_", - "type": "arrow" - }, - { - "id": "UCWmZfJ31JSBJj1P0S_KC", - "type": "arrow" - }, - { - "id": "ObbXkkUi4RX8Luqzn8JMg", - "type": "arrow" - } - ], - "updated": 1714744053938, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 549, - "versionNonce": 2109578832, - "isDeleted": false, - "id": "0ONVDP4A5ZJaTQOKliW_p", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 3246.0212123046886, - "y": 3734.7266200049257, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 85.51991271972656, - "height": 50, - "seed": 1074702321, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Get \nchecksum", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "iBZRtI2xGc5MgU0R2emqB", - "originalText": "Get checksum", - "lineHeight": 1.25, - "baseline": 46 - }, - { - "type": "arrow", - "version": 114, - "versionNonce": 2100178608, - "isDeleted": false, - "id": "h0EYmx16SKh0tRtXR5XhJ", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2644.328603593133, - "y": 3751.881046146817, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 69.710065902123, - "height": 2.3767629593407946, - "seed": 1105780465, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "startBinding": { - "elementId": "yNZIuCW9fYJaiSxldyEzO", - "focus": -0.07508062880961323, - "gap": 7.942895071847033 - }, - "endBinding": { - "elementId": "NYb28wO65_lvUaT1Cockk", - "focus": -0.005577575392632, - "gap": 5.287789349123756 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 69.710065902123, - 2.3767629593407946 - ] - ] - }, - { - "type": "diamond", - "version": 164, - "versionNonce": 1461015632, - "isDeleted": false, - "id": "xcVSmTB-gt0cN1YieTegp", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 2910.7777547684964, - "y": 3656.2608894469226, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 205.38748168945312, - "height": 205.38748168945312, - "seed": 2087491999, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "wFtq9-bbenLPBluVjdBTQ" - }, - { - "id": "IHs7M-PwfAD5r15fGjb9t", - "type": "arrow" - }, - { - "id": "CqJZf-4HS85UA4KXz_pP_", - "type": "arrow" - }, - { - "id": "ObbXkkUi4RX8Luqzn8JMg", - "type": "arrow" - }, - { - "id": "gyPcyFeW-J2407VPWQOUM", - "type": "arrow" - }, - { - "id": "1pwwcSYOnzOIeKOG9nO85", - "type": "arrow" - } - ], - "updated": 1714744053939, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 114, - "versionNonce": 1322571952, - "isDeleted": false, - "id": "wFtq9-bbenLPBluVjdBTQ", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 2969.2006368485745, - "y": 3714.107759869286, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 88.84797668457031, - "height": 90, - "seed": 403049919, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "data\nsize", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "xcVSmTB-gt0cN1YieTegp", - "originalText": "data size", - "lineHeight": 1.25, - "baseline": 83 - }, - { - "type": "diamond", - "version": 183, - "versionNonce": 1476807248, - "isDeleted": false, - "id": "MeyabU_KuxgmXdgvyrffK", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 3493.0634341769783, - "y": 3654.2609417627714, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 205.38748168945312, - "height": 205.38748168945312, - "seed": 1145797361, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "qXPQBk0vCTDesZ3ehy1sN" - }, - { - "id": "UCWmZfJ31JSBJj1P0S_KC", - "type": "arrow" - }, - { - "id": "FNpbosK2lphWsC9POj79B", - "type": "arrow" - }, - { - "id": "R_t8UO3YLfP0qZT237eff", - "type": "arrow" - }, - { - "id": "gyPcyFeW-J2407VPWQOUM", - "type": "arrow" - } - ], - "updated": 1714744053939, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 146, - "versionNonce": 1242972848, - "isDeleted": false, - "id": "qXPQBk0vCTDesZ3ehy1sN", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 3550.334323459205, - "y": 3712.1078121851347, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 91.15196228027344, - "height": 90, - "seed": 534836433, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "check\nsum", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "MeyabU_KuxgmXdgvyrffK", - "originalText": "checksum", - "lineHeight": 1.25, - "baseline": 83 - }, - { - "type": "ellipse", - "version": 394, - "versionNonce": 996704336, - "isDeleted": false, - "id": "I1nmKrm-fWZatTXwyA29D", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 3817.5856749519503, - "y": 3701.908356923905, - "strokeColor": "#1971c2", - "backgroundColor": "#b2f2bb", - "width": 115.2000732421875, - "height": 115.2000732421875, - "seed": 1475797983, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "vr3UtOfRy6FMkQnzXawBx" - }, - { - "id": "FNpbosK2lphWsC9POj79B", - "type": "arrow" - }, - { - "id": "G5nVT5VA8UUPppUVgsIHm", - "type": "arrow" - }, - { - "id": "-3xsQ8CUpzKqR3rQuEvHY", - "type": "arrow" - } - ], - "updated": 1714744053939, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 378, - "versionNonce": 1838836912, - "isDeleted": false, - "id": "vr3UtOfRy6FMkQnzXawBx", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 3845.9003582139994, - "y": 3739.27901705363, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 58.11195373535156, - "height": 40, - "seed": 407703551, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "Finished\n180", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "I1nmKrm-fWZatTXwyA29D", - "originalText": "Finished\n180", - "lineHeight": 1.25, - "baseline": 37 - }, - { - "type": "arrow", - "version": 86, - "versionNonce": 554956368, - "isDeleted": false, - "id": "IHs7M-PwfAD5r15fGjb9t", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2853.4715828063026, - "y": 3758.990466647677, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 41.14283970424094, - "height": 1.14288330078125, - "seed": 1733680337, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "startBinding": { - "elementId": "NYb28wO65_lvUaT1Cockk", - "focus": 0.08742956876768007, - "gap": 19.0267521350872 - }, - "endBinding": { - "elementId": "xcVSmTB-gt0cN1YieTegp", - "focus": 0.04293065224006023, - "gap": 12.212002247896805 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 41.14283970424094, - -1.14288330078125 - ] - ] - }, - { - "type": "arrow", - "version": 53, - "versionNonce": 587814576, - "isDeleted": false, - "id": "CqJZf-4HS85UA4KXz_pP_", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3138.043028816347, - "y": 3757.8475833468956, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 59.428536551339675, - "height": 0, - "seed": 1485765023, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "startBinding": { - "elementId": "xcVSmTB-gt0cN1YieTegp", - "focus": -0.010780081976247025, - "gap": 16.25273573574124 - }, - "endBinding": { - "elementId": "iBZRtI2xGc5MgU0R2emqB", - "focus": 0.02882454324848109, - "gap": 21.2795556480239 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 59.428536551339675, - 0 - ] - ] - }, - { - "type": "arrow", - "version": 63, - "versionNonce": 1843440720, - "isDeleted": false, - "id": "UCWmZfJ31JSBJj1P0S_KC", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3366.6144573877755, - "y": 3756.704743642654, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 110.85710797991123, - "height": 0, - "seed": 1479588447, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "startBinding": { - "elementId": "iBZRtI2xGc5MgU0R2emqB", - "focus": -0.044605338645362265, - "gap": 7.606519777442898 - }, - "endBinding": { - "elementId": "MeyabU_KuxgmXdgvyrffK", - "focus": 0.002433828612998517, - "gap": 11.201849703344834 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 110.85710797991123, - 0 - ] - ] - }, - { - "type": "arrow", - "version": 53, - "versionNonce": 1594556592, - "isDeleted": false, - "id": "FNpbosK2lphWsC9POj79B", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3711.757279653401, - "y": 3754.419020637632, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 89.14289202008922, - "height": 1.14288330078125, - "seed": 30695519, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "startBinding": { - "elementId": "MeyabU_KuxgmXdgvyrffK", - "focus": -0.03917352919837444, - "gap": 11.202003840390304 - }, - "endBinding": { - "elementId": "I1nmKrm-fWZatTXwyA29D", - "focus": 0.05197641523578342, - "gap": 16.790259912682096 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 89.14289202008922, - 1.14288330078125 - ] - ] - }, - { - "type": "arrow", - "version": 306, - "versionNonce": 732340816, - "isDeleted": false, - "id": "R_t8UO3YLfP0qZT237eff", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3598.614492265008, - "y": 3883.561912657721, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 1259.4285365513392, - "height": 180.26785714285688, - "seed": 2105252219, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "startBinding": { - "focus": 0.02373368924095548, - "gap": 18.929818777778806, - "elementId": "MeyabU_KuxgmXdgvyrffK" - }, - "endBinding": { - "focus": 1.080670393703399, - "gap": 11.371326991489696, - "elementId": "yNZIuCW9fYJaiSxldyEzO" - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 3.428431919643117, - 81.98216029575906 - ], - [ - -1254.8571777343745, - 84.26783970424094 - ], - [ - -1256.000104631696, - -96.00001743861594 - ] - ] - }, - { - "type": "arrow", - "version": 490, - "versionNonce": 1095199408, - "isDeleted": false, - "id": "G5nVT5VA8UUPppUVgsIHm", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3878.490602523564, - "y": 3683.0739046127064, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 3389.866658528645, - "height": 374.6416727701826, - "seed": 1176066097, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "startBinding": { - "elementId": "I1nmKrm-fWZatTXwyA29D", - "focus": 0.06304296269521902, - "gap": 18.905867742788914 - }, - "endBinding": { - "elementId": "ViWZZy9Q7c0UYyERaQ5M0", - "focus": -0.18141147608207922, - "gap": 12.857091267901524 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -1.5999348958330302, - -374.6416727701826 - ], - [ - -3389.866658528645, - -360.7082824707031 - ], - [ - -3387.199951171874, - -30.399993896484375 - ] - ] - }, - { - "type": "rectangle", - "version": 504, - "versionNonce": 1765660752, - "isDeleted": false, - "id": "LUIcjOG-pExPEpZZJQzlm", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1795.3821297297375, - "y": 3326.836719573486, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 948.685961042132, - "height": 145.76242065429682, - "seed": 1996228065, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "m3Xa6yXTyfn-0cNoEqvXL" - }, - { - "id": "1PypevWTBXChXql2NBnfV", - "type": "arrow" - }, - { - "id": "iXpExTgBKgqPR5laoFls7", - "type": "arrow" - } - ], - "updated": 1714744053939, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 506, - "versionNonce": 879137968, - "isDeleted": false, - "id": "m3Xa6yXTyfn-0cNoEqvXL", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1408.5731152731246, - "y": 3354.7179299006343, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 175.06793212890625, - "height": 90, - "seed": 874684865, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "RUN_MPF\n2", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "LUIcjOG-pExPEpZZJQzlm", - "originalText": "RUN_MPF\n2", - "lineHeight": 1.25, - "baseline": 83 - }, - { - "type": "text", - "version": 114, - "versionNonce": 245087984, - "isDeleted": false, - "id": "zuvqv-bFqKG3ZoKU-hDV9", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -2131.0392582000222, - "y": 3374.325037880546, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 147.65625, - "height": 43.199999999999996, - "seed": 1806435311, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1717506989674, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 3, - "text": "run_MPF", - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "run_MPF", - "lineHeight": 1.2, - "baseline": 34 - }, - { - "type": "arrow", - "version": 526, - "versionNonce": 235962032, - "isDeleted": false, - "id": "1PypevWTBXChXql2NBnfV", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1967.8963763524587, - "y": 3395.035870481208, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 145.71427844819596, - "height": 0.8547003201483676, - "seed": 474179585, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053939, - "link": null, - "locked": false, - "startBinding": { - "focus": 0.17249076682334344, - "gap": 11.486642020089903, - "elementId": "zuvqv-bFqKG3ZoKU-hDV9" - }, - "endBinding": { - "focus": 0.011734317410885505, - "gap": 26.799968174525247, - "elementId": "LUIcjOG-pExPEpZZJQzlm" - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 145.71427844819596, - 0.8547003201483676 - ] - ] - }, - { - "type": "ellipse", - "version": 453, - "versionNonce": 1172142256, - "isDeleted": false, - "id": "uYwEC6UHGHtBawFQ1yRXK", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": -1854.1151692223898, - "y": 3517.3203846764904, - "strokeColor": "#1971c2", - "backgroundColor": "#b2f2bb", - "width": 151.19998168945312, - "height": 151.19998168945312, - "seed": 1261839745, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "v8Qr4f-p1eNaONyU3HxYm", - "type": "arrow" - }, - { - "type": "text", - "id": "cQbHPbTLQ-p6eVbLGwIJB" - }, - { - "id": "xIDgdoUwo4XtG0resbUs1", - "type": "arrow" - } - ], - "updated": 1714744077470, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 442, - "versionNonce": 386589360, - "isDeleted": false, - "id": "cQbHPbTLQ-p6eVbLGwIJB", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": -1814.981676128993, - "y": 3555.6554170295776, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 73.01846313476562, - "height": 74.61538461538461, - "seed": 657968481, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744077470, - "link": null, - "locked": false, - "fontSize": 29.846153846153843, - "fontFamily": 1, - "text": "Wake\n170", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "uYwEC6UHGHtBawFQ1yRXK", - "originalText": "Wake\n170", - "lineHeight": 1.25, - "baseline": 68 - }, - { - "type": "diamond", - "version": 479, - "versionNonce": 20110512, - "isDeleted": false, - "id": "nTdCg9YES9bcUbYEdQyta", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1600.5819711836525, - "y": 3482.1245126875583, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 220.13316345214852, - "height": 230, - "seed": 142178625, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "id": "v8Qr4f-p1eNaONyU3HxYm", - "type": "arrow" - }, - { - "id": "xX9zNOd036gOt9NBE8Rvi", - "type": "arrow" - }, - { - "id": "JgAs79Mhh9PankgSUE3N-", - "type": "arrow" - }, - { - "type": "text", - "id": "Zh3fcBVd8hpkdWFRpjVw5" - }, - { - "id": "9kzbQWa7MJj_5DR5ThIqF", - "type": "arrow" - } - ], - "updated": 1714744073401, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 402, - "versionNonce": 169068208, - "isDeleted": false, - "id": "Zh3fcBVd8hpkdWFRpjVw5", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1532.0726595076271, - "y": 3562.1245126875583, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 83.04795837402344, - "height": 70, - "seed": 1348118817, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744073401, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 1, - "text": "Awake\n128", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "nTdCg9YES9bcUbYEdQyta", - "originalText": "Awake\n128", - "lineHeight": 1.25, - "baseline": 64 - }, - { - "type": "ellipse", - "version": 470, - "versionNonce": 445490864, - "isDeleted": false, - "id": "IyEMitLQCn9ktYumohw1w", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": -1278.448690747455, - "y": 3506.9661264570896, - "strokeColor": "#1971c2", - "backgroundColor": "#b2f2bb", - "width": 158.20010375976562, - "height": 163, - "seed": 139123969, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "xX9zNOd036gOt9NBE8Rvi", - "type": "arrow" - }, - { - "id": "UvCZgYnpIziv3kllJrXSu", - "type": "arrow" - }, - { - "type": "text", - "id": "zZOZtekgqGD5GVTzmQp37" - }, - { - "id": "t2mJ36gZcTfYuLR0nvMGj", - "type": "arrow" - } - ], - "updated": 1714744061491, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 487, - "versionNonce": 1336254640, - "isDeleted": false, - "id": "zZOZtekgqGD5GVTzmQp37", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": -1236.56280436592, - "y": 3553.3369237903858, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 74.56396484375, - "height": 70, - "seed": 1607046369, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744061492, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 1, - "text": "array\n160", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "IyEMitLQCn9ktYumohw1w", - "originalText": "array\n160", - "lineHeight": 1.25, - "baseline": 64 - }, - { - "type": "diamond", - "version": 484, - "versionNonce": 1005657680, - "isDeleted": false, - "id": "uobFuAsT0NWnRMntfj5nA", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1039.5818592858661, - "y": 3491.4578561934177, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 206.13360595703108, - "height": 230, - "seed": 450433295, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "Qhq4_JM-nSBfudx4KwI3w" - }, - { - "id": "UvCZgYnpIziv3kllJrXSu", - "type": "arrow" - }, - { - "id": "dWqgALvF9Tt7Y7xljGzgz", - "type": "arrow" - }, - { - "id": "0ov9dJFlVufiTGN_Batn_", - "type": "arrow" - } - ], - "updated": 1714744082328, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 485, - "versionNonce": 1281448016, - "isDeleted": false, - "id": "Qhq4_JM-nSBfudx4KwI3w", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -975.3284413171161, - "y": 3571.4578561934177, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 77.55996704101562, - "height": 70, - "seed": 47322927, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744082329, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 1, - "text": "start\n161", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "uobFuAsT0NWnRMntfj5nA", - "originalText": "start\n161", - "lineHeight": 1.25, - "baseline": 64 - }, - { - "type": "arrow", - "version": 302, - "versionNonce": 1935013552, - "isDeleted": false, - "id": "v8Qr4f-p1eNaONyU3HxYm", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1696.5029965461563, - "y": 3593.6014571092824, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 86.1103623030699, - "height": 0.9750729043958017, - "seed": 1785090241, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744077471, - "link": null, - "locked": false, - "startBinding": { - "elementId": "uYwEC6UHGHtBawFQ1yRXK", - "gap": 6.415019006481387, - "focus": 0.010680436385080695 - }, - "endBinding": { - "elementId": "nTdCg9YES9bcUbYEdQyta", - "gap": 10.792695192228948, - "focus": 0.0509179265231678 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 86.1103623030699, - -0.9750729043958017 - ] - ] - }, - { - "type": "arrow", - "version": 192, - "versionNonce": 610463408, - "isDeleted": false, - "id": "xX9zNOd036gOt9NBE8Rvi", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1366.6940117176691, - "y": 3599.8292813762528, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 68.68906999390629, - "height": 4.53107866674145, - "seed": 499159201, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744073402, - "link": null, - "locked": false, - "startBinding": { - "elementId": "nTdCg9YES9bcUbYEdQyta", - "gap": 14.018209123905663, - "focus": 0.08586378806646597 - }, - "endBinding": { - "elementId": "IyEMitLQCn9ktYumohw1w", - "gap": 19.781480549311887, - "focus": -0.003969875383615567 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 68.68906999390629, - -4.53107866674145 - ] - ] - }, - { - "type": "arrow", - "version": 153, - "versionNonce": 1062849104, - "isDeleted": false, - "id": "UvCZgYnpIziv3kllJrXSu", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1107.4111953175195, - "y": 3595.148697392535, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 64.52409946038006, - "height": 4.936904705241432, - "seed": 260560001, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744082328, - "link": null, - "locked": false, - "startBinding": { - "elementId": "IyEMitLQCn9ktYumohw1w", - "gap": 13.067797381304693, - "focus": -0.005107202519207527 - }, - "endBinding": { - "elementId": "uobFuAsT0NWnRMntfj5nA", - "gap": 6.714296040635084, - "focus": -0.015361252813825369 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 64.52409946038006, - 4.936904705241432 - ] - ] - }, - { - "type": "arrow", - "version": 153, - "versionNonce": 2016262832, - "isDeleted": false, - "id": "iXpExTgBKgqPR5laoFls7", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -823.1819772871686, - "y": 3403.649465893939, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 1256.0001627604165, - "height": 250.66665649414062, - "seed": 200897505, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053940, - "link": null, - "locked": false, - "startBinding": { - "elementId": "LUIcjOG-pExPEpZZJQzlm", - "focus": 0.03348251315767217, - "gap": 23.514191400436857 - }, - "endBinding": { - "elementId": "ViWZZy9Q7c0UYyERaQ5M0", - "focus": -0.6121335146544702, - "gap": 11.21487959604383 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 1256.0001627604165, - 3.6459350585932953 - ], - [ - 1254.666748046875, - 250.66665649414062 - ] - ] - }, - { - "type": "arrow", - "version": 220, - "versionNonce": 829528240, - "isDeleted": false, - "id": "JgAs79Mhh9PankgSUE3N-", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1477.5786765478156, - "y": 3727.9894984688544, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 274.6666463216145, - "height": 101.33341471354152, - "seed": 1937480801, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "78O2q2PxtGz1Gg4RXJBpS" - } - ], - "updated": 1714744073402, - "link": null, - "locked": false, - "startBinding": { - "elementId": "nTdCg9YES9bcUbYEdQyta", - "gap": 20.47086502203458, - "focus": -0.05271894646311373 - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 5.396633139228015, - 98.99335162104808 - ], - [ - -269.2700131823865, - 98.99335162104808 - ], - [ - -265.27007421754274, - -2.3400630924934376 - ] - ] - }, - { - "type": "text", - "version": 35, - "versionNonce": 472808016, - "isDeleted": false, - "id": "78O2q2PxtGz1Gg4RXJBpS", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1673.9684915693947, - "y": 3814.9828500899025, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 128.90625, - "height": 24, - "seed": 1500211265, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744085177, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 3, - "text": "10 x re-try", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "JgAs79Mhh9PankgSUE3N-", - "originalText": "10 x re-try", - "lineHeight": 1.2, - "baseline": 20 - }, - { - "type": "arrow", - "version": 167, - "versionNonce": 1770485328, - "isDeleted": false, - "id": "dWqgALvF9Tt7Y7xljGzgz", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -931.749744704638, - "y": 3732.369685731488, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 3.9029481307186416, - "height": 87.90479033497695, - "seed": 211599620, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744082328, - "link": null, - "locked": false, - "startBinding": { - "elementId": "uobFuAsT0NWnRMntfj5nA", - "gap": 11.906981924016634, - "focus": 0.005182860059776323 - }, - "endBinding": { - "elementId": "4Kr7ILxxgOKk4cL8Frdqj", - "gap": 14.791649712458138, - "focus": 0.03343417125319411 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 3.9029481307186416, - 87.90479033497695 - ] - ] - }, - { - "type": "rectangle", - "version": 250, - "versionNonce": 1537396400, - "isDeleted": false, - "id": "4Kr7ILxxgOKk4cL8Frdqj", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -969.9597618466297, - "y": 3835.0661257789234, - "strokeColor": "#e03131", - "backgroundColor": "#ffc9c9", - "width": 86.2221950954861, - "height": 83.08332655164895, - "seed": 1759903876, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "dWqgALvF9Tt7Y7xljGzgz", - "type": "arrow" - }, - { - "type": "text", - "id": "nCDFFqPz2NaPevY59Q_sK" - } - ], - "updated": 1714744053940, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 214, - "versionNonce": 1069200464, - "isDeleted": false, - "id": "nCDFFqPz2NaPevY59Q_sK", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -939.1246576460546, - "y": 3854.1077890547476, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 24.551986694335938, - "height": 45, - "seed": 2027638788, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053940, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "-1", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "4Kr7ILxxgOKk4cL8Frdqj", - "originalText": "-1", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "rectangle", - "version": 553, - "versionNonce": 901085360, - "isDeleted": false, - "id": "fsf5g_MH6n-Xe8361Ds-Y", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1795.1915837847953, - "y": 3165.3932962568956, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 948.685961042132, - "height": 145.76242065429682, - "seed": 790388271, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "id": "cP4_-wTdxlg1lANFXh59e", - "type": "arrow" - }, - { - "type": "text", - "id": "rIrygkewhTvE4fK0UIFY-" - } - ], - "updated": 1714744053940, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 560, - "versionNonce": 1157605968, - "isDeleted": false, - "id": "rIrygkewhTvE4fK0UIFY-", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1429.6765680464441, - "y": 3193.274506584044, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 217.6559295654297, - "height": 90, - "seed": 124698703, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053940, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "RUN_ARRAY\n1", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "fsf5g_MH6n-Xe8361Ds-Y", - "originalText": "RUN_ARRAY\n1", - "lineHeight": 1.25, - "baseline": 83 - }, - { - "type": "text", - "version": 153, - "versionNonce": 154658832, - "isDeleted": false, - "id": "bTp-8luSuqfkr5dZXRzbu", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -2131.0392582000222, - "y": 3211.3042045472125, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 189.84375, - "height": 43.199999999999996, - "seed": 124771951, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1717506989675, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 3, - "text": "run_array", - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "run_array", - "lineHeight": 1.2, - "baseline": 34 - }, - { - "type": "arrow", - "version": 44, - "versionNonce": 37085264, - "isDeleted": false, - "id": "cP4_-wTdxlg1lANFXh59e", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1929.848542228573, - "y": 3228.9828195723258, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 121.33331298828102, - "height": 0.354156494140625, - "seed": 1105737871, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053940, - "link": null, - "locked": false, - "startBinding": { - "focus": -0.16506783603693345, - "gap": 11.346965971449208, - "elementId": "bTp-8luSuqfkr5dZXRzbu" - }, - "endBinding": { - "focus": 0.1490495853850813, - "gap": 13.323645455496717, - "elementId": "fsf5g_MH6n-Xe8361Ds-Y" - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 121.33331298828102, - -0.354156494140625 - ] - ] - }, - { - "type": "rectangle", - "version": 592, - "versionNonce": 1288899760, - "isDeleted": false, - "id": "Pr4PBT6gtyxcUP4k4uCsX", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1795.1915837847953, - "y": 4097.039109245177, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 948.685961042132, - "height": 145.76242065429682, - "seed": 610179599, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "5c9x3dweo76bhGW5AV51w" - }, - { - "id": "fsL0FB0S9Y5X_0v2NpbrG", - "type": "arrow" - } - ], - "updated": 1714744053940, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 618, - "versionNonce": 1731548752, - "isDeleted": false, - "id": "5c9x3dweo76bhGW5AV51w", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1394.1805811079676, - "y": 4124.920319572326, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 146.66395568847656, - "height": 90, - "seed": 884899887, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053940, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "Set gain\nnot FSM", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Pr4PBT6gtyxcUP4k4uCsX", - "originalText": "Set gain\nnot FSM", - "lineHeight": 1.25, - "baseline": 83 - }, - { - "type": "text", - "version": 203, - "versionNonce": 883319024, - "isDeleted": false, - "id": "WoDN3x20JgQV_4M3zgm2E", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -2131.0392582000222, - "y": 4142.950017535494, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 189.84375, - "height": 43.199999999999996, - "seed": 1683577121, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "1PypevWTBXChXql2NBnfV", - "type": "arrow" - }, - { - "id": "fsL0FB0S9Y5X_0v2NpbrG", - "type": "arrow" - } - ], - "updated": 1717506989677, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 3, - "text": "set gains", - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "set gains", - "lineHeight": 1.2, - "baseline": 34 - }, - { - "type": "arrow", - "version": 188, - "versionNonce": 685116496, - "isDeleted": false, - "id": "fsL0FB0S9Y5X_0v2NpbrG", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1929.848542228573, - "y": 4160.6172362743755, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 121.33331298828102, - "height": 0.3515877575155173, - "seed": 1982559553, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false, - "startBinding": { - "elementId": "WoDN3x20JgQV_4M3zgm2E", - "focus": -0.16506783603693345, - "gap": 11.346965971449208 - }, - "endBinding": { - "elementId": "Pr4PBT6gtyxcUP4k4uCsX", - "focus": 0.1490495853850874, - "gap": 13.323645455496717 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 121.33331298828102, - -0.3515877575155173 - ] - ] - }, - { - "type": "ellipse", - "version": 453, - "versionNonce": 10199216, - "isDeleted": false, - "id": "D6RCrZZfUskxRWf_dQGQy", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": -1794.115245516335, - "y": 4286.966228182349, - "strokeColor": "#1971c2", - "backgroundColor": "#b2f2bb", - "width": 115.2000732421875, - "height": 115.2000732421875, - "seed": 1508824801, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "rGXtYr1wg85vT4cKNEyTV" - }, - { - "id": "Gce5Uag0FalsnQV4V5iGy", - "type": "arrow" - }, - { - "id": "1DsZ45isiDwiBY2WN00mP", - "type": "arrow" - } - ], - "updated": 1714744053941, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 425, - "versionNonce": 552600144, - "isDeleted": false, - "id": "rGXtYr1wg85vT4cKNEyTV", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": -1756.3205741561414, - "y": 4324.336888312075, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 39.1519775390625, - "height": 40, - "seed": 848515777, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "Wake\n170", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "D6RCrZZfUskxRWf_dQGQy", - "originalText": "Wake\n170", - "lineHeight": 1.25, - "baseline": 37 - }, - { - "type": "diamond", - "version": 460, - "versionNonce": 667159216, - "isDeleted": false, - "id": "U9zJlCi1cx3t3KTdW_MOO", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1552.582093253965, - "y": 4279.770386710996, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 146.13330078124994, - "height": 144.44578043619777, - "seed": 352820577, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "F1qzQG_LJqW8QZc9pHFKr" - }, - { - "id": "Gce5Uag0FalsnQV4V5iGy", - "type": "arrow" - }, - { - "id": "mgTwcu3_Vl7Te2jdNRQfS", - "type": "arrow" - }, - { - "id": "1DsZ45isiDwiBY2WN00mP", - "type": "arrow" - } - ], - "updated": 1714744053941, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 422, - "versionNonce": 777730128, - "isDeleted": false, - "id": "F1qzQG_LJqW8QZc9pHFKr", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1503.2767496260353, - "y": 4331.881831820045, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 47.455963134765625, - "height": 40, - "seed": 1271875905, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "Awake\n128", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "U9zJlCi1cx3t3KTdW_MOO", - "originalText": "Awake\n128", - "lineHeight": 1.25, - "baseline": 37 - }, - { - "type": "rectangle", - "version": 230, - "versionNonce": 1649358000, - "isDeleted": false, - "id": "5ZUJfQAvOLg0_P7upig6F", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1036.227654545733, - "y": 1485.0945953942132, - "strokeColor": "#e03131", - "backgroundColor": "#ffec99", - "width": 863.333129882812, - "height": 1246.666755676269, - "seed": 1238532844, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false - }, - { - "type": "ellipse", - "version": 505, - "versionNonce": 351252048, - "isDeleted": false, - "id": "Onjg_i4-ah2ZiKQ2dExDG", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": -1235.4486602298769, - "y": 4290.612061515683, - "strokeColor": "#1971c2", - "backgroundColor": "#b2f2bb", - "width": 115.2000732421875, - "height": 115.2000732421875, - "seed": 1575725263, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "M7tosNMgSJnNzEvXvHCjs" - }, - { - "id": "mgTwcu3_Vl7Te2jdNRQfS", - "type": "arrow" - }, - { - "id": "h08UrC6DZ5KTYrflQ6eMN", - "type": "arrow" - } - ], - "updated": 1714744053941, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 499, - "versionNonce": 904208048, - "isDeleted": false, - "id": "M7tosNMgSJnNzEvXvHCjs", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": -1197.2539873438043, - "y": 4327.982721645408, - "strokeColor": "#1971c2", - "backgroundColor": "transparent", - "width": 38.35197448730469, - "height": 40, - "seed": 1060878063, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "gains\n160-1", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Onjg_i4-ah2ZiKQ2dExDG", - "originalText": "gains\n160-1", - "lineHeight": 1.25, - "baseline": 37 - }, - { - "type": "arrow", - "version": 278, - "versionNonce": 1569023056, - "isDeleted": false, - "id": "Gce5Uag0FalsnQV4V5iGy", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1672.5153716556579, - "y": 4345.962037101621, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 109.33339436848951, - "height": 1.333414713541515, - "seed": 319409487, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false, - "startBinding": { - "elementId": "D6RCrZZfUskxRWf_dQGQy", - "focus": 0.010680436385085314, - "gap": 6.415019006481387 - }, - "endBinding": { - "elementId": "U9zJlCi1cx3t3KTdW_MOO", - "focus": 0.05091792652318522, - "gap": 10.792695192229587 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 109.33339436848951, - 1.333414713541515 - ] - ] - }, - { - "type": "arrow", - "version": 232, - "versionNonce": 858638512, - "isDeleted": false, - "id": "mgTwcu3_Vl7Te2jdNRQfS", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1389.848643953835, - "y": 4355.295431470111, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 134.66664632161473, - "height": 4.354146321614735, - "seed": 1327861647, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false, - "startBinding": { - "elementId": "U9zJlCi1cx3t3KTdW_MOO", - "focus": 0.08586378806647016, - "gap": 14.018209123905656 - }, - "endBinding": { - "elementId": "Onjg_i4-ah2ZiKQ2dExDG", - "focus": -0.0039698753836031435, - "gap": 19.781480549311844 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 134.66664632161473, - -4.354146321614735 - ] - ] - }, - { - "type": "rectangle", - "version": 631, - "versionNonce": 905206352, - "isDeleted": false, - "id": "13A-cIBI0hFl0Kou3xlia", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1967.8942194871388, - "y": 1493.0945953942132, - "strokeColor": "#e03131", - "backgroundColor": "#ffec99", - "width": 770, - "height": 1246.666755676269, - "seed": 869527039, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false - }, - { - "type": "arrow", - "version": 158, - "versionNonce": 366031536, - "isDeleted": false, - "id": "h08UrC6DZ5KTYrflQ6eMN", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1107.1820179772726, - "y": 4348.628764803444, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 97.3333740234375, - "height": 0.9791056315107198, - "seed": 1782263937, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false, - "startBinding": { - "elementId": "Onjg_i4-ah2ZiKQ2dExDG", - "focus": -0.005107202519207527, - "gap": 13.067797381304693 - }, - "endBinding": { - "elementId": "N0cX1tGro7njXrsAOLgdr", - "focus": 0.030303133580792906, - "gap": 7.4763777515814525 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 97.3333740234375, - 0.9791056315107198 - ] - ] - }, - { - "type": "arrow", - "version": 309, - "versionNonce": 1360302160, - "isDeleted": false, - "id": "1DsZ45isiDwiBY2WN00mP", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1476.5153106205016, - "y": 4433.96211848183, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 274.6666463216145, - "height": 101.33341471354152, - "seed": 1189902927, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "D5gdBq6jF-zLZy7WfzaTh" - } - ], - "updated": 1714744053941, - "link": null, - "locked": false, - "startBinding": { - "elementId": "U9zJlCi1cx3t3KTdW_MOO", - "focus": -0.023801452166815396, - "gap": 9.040391251510648 - }, - "endBinding": { - "elementId": "D6RCrZZfUskxRWf_dQGQy", - "focus": 0.11074199316822089, - "gap": 17.70967973621081 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 1.33331298828125, - 86.66666666666652 - ], - [ - -273.33333333333326, - 86.66666666666652 - ], - [ - -269.3333943684895, - -14.666748046875 - ] - ] - }, - { - "type": "text", - "version": 31, - "versionNonce": 1685544112, - "isDeleted": false, - "id": "D5gdBq6jF-zLZy7WfzaTh", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1664.0778207930275, - "y": 3727.3828500899026, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 103.125, - "height": 19.2, - "seed": 453937231, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 3, - "text": "10 x re-try", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "1DsZ45isiDwiBY2WN00mP", - "originalText": "10 x re-try", - "lineHeight": 1.2, - "baseline": 16 - }, - { - "type": "arrow", - "version": 202, - "versionNonce": 1186858576, - "isDeleted": false, - "id": "bC8G2hYSNIFc2_Me7VqsJ", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -927.1820179772726, - "y": 4437.9620574466735, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 1.33331298828125, - "height": 165.95835367838527, - "seed": 1191860399, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false, - "startBinding": { - "elementId": "N0cX1tGro7njXrsAOLgdr", - "focus": -0.017986064516638294, - "gap": 10.744892908205493 - }, - "endBinding": { - "elementId": "uCNGKGtZaXicEtZGcUrvR", - "focus": 0.03343417125319422, - "gap": 14.79164971245882 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 1.33331298828125, - 165.95835367838527 - ] - ] - }, - { - "type": "rectangle", - "version": 297, - "versionNonce": 281560752, - "isDeleted": false, - "id": "uCNGKGtZaXicEtZGcUrvR", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -969.9597618466297, - "y": 4618.712060837517, - "strokeColor": "#e03131", - "backgroundColor": "#ffc9c9", - "width": 86.2221950954861, - "height": 83.08332655164895, - "seed": 992918767, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "bhGbuqXzpmUaQXBBQbWk3" - }, - { - "id": "bC8G2hYSNIFc2_Me7VqsJ", - "type": "arrow" - } - ], - "updated": 1714744053941, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 263, - "versionNonce": 1942535248, - "isDeleted": false, - "id": "bhGbuqXzpmUaQXBBQbWk3", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -939.1246576460546, - "y": 4637.753724113341, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 24.551986694335938, - "height": 45, - "seed": 122111759, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "-1", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "uCNGKGtZaXicEtZGcUrvR", - "originalText": "-1", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "diamond", - "version": 484, - "versionNonce": 392949936, - "isDeleted": false, - "id": "N0cX1tGro7njXrsAOLgdr", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1002.2485869876848, - "y": 4280.385010734436, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 146.13330078124994, - "height": 144.44578043619777, - "seed": 474451023, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "lxahC8Py7GJOXqxS0jtRJ" - }, - { - "id": "h08UrC6DZ5KTYrflQ6eMN", - "type": "arrow" - }, - { - "id": "bC8G2hYSNIFc2_Me7VqsJ", - "type": "arrow" - } - ], - "updated": 1714744053941, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 464, - "versionNonce": 1510781520, - "isDeleted": false, - "id": "lxahC8Py7GJOXqxS0jtRJ", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -951.3752425662981, - "y": 4332.496455843486, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 44.31996154785156, - "height": 40, - "seed": 525281903, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053941, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "start\n161", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "N0cX1tGro7njXrsAOLgdr", - "originalText": "start\n161", - "lineHeight": 1.25, - "baseline": 37 - }, - { - "type": "rectangle", - "version": 258, - "versionNonce": 537549488, - "isDeleted": false, - "id": "949o_Sk--Ld6wo2FKS2fL", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -2718.0883511511747, - "y": 2144.6222031974667, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 268.50899366592813, - "height": 120.34072952849813, - "seed": 1290344508, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "Ku5jwN-ehg_ppOzAQcOz5" - }, - { - "id": "5e8pE6-Q5WVxBCsQPlJL-", - "type": "arrow" - }, - { - "id": "Aujbd37Vz68tzdcf0qX0K", - "type": "arrow" - }, - { - "id": "7p9T0a-IxPmDdjBqC-0im", - "type": "arrow" - } - ], - "updated": 1714744053941, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 298, - "versionNonce": 6416464, - "isDeleted": false, - "id": "Ku5jwN-ehg_ppOzAQcOz5", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -2618.465835214207, - "y": 2182.2925679617156, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 69.26396179199219, - "height": 45, - "seed": 219328700, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053942, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "Idle", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "949o_Sk--Ld6wo2FKS2fL", - "originalText": "Idle", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "rectangle", - "version": 306, - "versionNonce": 491488432, - "isDeleted": false, - "id": "QljoeT8sSPtX_l3e_a3My", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -2015.3499767931817, - "y": 2144.6222031974667, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 268.50899366592813, - "height": 120.34072952849813, - "seed": 896874812, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "qX54YZHFaLlmhuYfJptkK" - }, - { - "id": "ORyDG0-MmRvrIZpmyRPlC", - "type": "arrow" - }, - { - "id": "ZTgJCi82oC_3iuspj4sVs", - "type": "arrow" - } - ], - "updated": 1714744053942, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 344, - "versionNonce": 1386241616, - "isDeleted": false, - "id": "qX54YZHFaLlmhuYfJptkK", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1924.169446940198, - "y": 2182.2925679617156, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 86.14793395996094, - "height": 45, - "seed": 829600188, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053942, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "sleep", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "QljoeT8sSPtX_l3e_a3My", - "originalText": "sleep", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "diamond", - "version": 302, - "versionNonce": 1826057904, - "isDeleted": false, - "id": "oojvRHlnGhZu1D9CQPzga", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -2338.399894807081, - "y": 2098.1937387087164, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 219.45825162823473, - "seed": 808262204, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "VEK9mOzvCT7cIABUKVdF1" - }, - { - "id": "7p9T0a-IxPmDdjBqC-0im", - "type": "arrow" - }, - { - "id": "ORyDG0-MmRvrIZpmyRPlC", - "type": "arrow" - } - ], - "updated": 1714744053942, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 309, - "versionNonce": 27268176, - "isDeleted": false, - "id": "VEK9mOzvCT7cIABUKVdF1", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -2260.6855303549614, - "y": 2173.0583016157752, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 79.04397583007812, - "height": 70, - "seed": 747008700, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053942, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 1, - "text": "idle\n100ms", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "oojvRHlnGhZu1D9CQPzga", - "originalText": "idle\n100ms", - "lineHeight": 1.25, - "baseline": 64 - }, - { - "type": "diamond", - "version": 595, - "versionNonce": 2002131120, - "isDeleted": false, - "id": "4HG3lkfSICKQoMrMvMSOL", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -773.0239289434176, - "y": 2111.0701354668563, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 219.45825162823473, - "seed": 1965079356, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "-1QesW8sjDIOBHp0WoVlf" - }, - { - "id": "XOgSlXTT9UnidMDOPb8Og", - "type": "arrow" - }, - { - "id": "z8rBoA7QcvEpK40GcZt3l", - "type": "arrow" - }, - { - "id": "t2mJ36gZcTfYuLR0nvMGj", - "type": "arrow" - }, - { - "id": "ek9xtHVfDe-Hf4jNvTJNN", - "type": "arrow" - } - ], - "updated": 1714744053942, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 605, - "versionNonce": 1664682576, - "isDeleted": false, - "id": "-1QesW8sjDIOBHp0WoVlf", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -688.7155627212783, - "y": 2185.934698373915, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 65.85597229003906, - "height": 70, - "seed": 878572476, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053942, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 1, - "text": "CMD\n(160)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "4HG3lkfSICKQoMrMvMSOL", - "originalText": "CMD\n(160)", - "lineHeight": 1.25, - "baseline": 64 - }, - { - "type": "diamond", - "version": 338, - "versionNonce": 1349485232, - "isDeleted": false, - "id": "ZVlRo5ABA2pBBSuDikef_", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1674.4696597442662, - "y": 2098.1937387087164, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 219.45825162823473, - "seed": 2019564604, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "tl-Ch-qaGtP3U275BDcYl" - }, - { - "id": "ZTgJCi82oC_3iuspj4sVs", - "type": "arrow" - }, - { - "id": "glCxPNKW4x-VKTsz6vtbT", - "type": "arrow" - }, - { - "id": "gyPcyFeW-J2407VPWQOUM", - "type": "arrow" - }, - { - "id": "GkT7kVIBQeOtiyApJWoB5", - "type": "arrow" - } - ], - "updated": 1714744053942, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 353, - "versionNonce": 1380139088, - "isDeleted": false, - "id": "tl-Ch-qaGtP3U275BDcYl", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1595.4532933390215, - "y": 2173.0583016157752, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 76.43997192382812, - "height": 70, - "seed": 990459068, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053942, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 1, - "text": "uart \ninput", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "ZVlRo5ABA2pBBSuDikef_", - "originalText": "uart input", - "lineHeight": 1.25, - "baseline": 64 - }, - { - "type": "rectangle", - "version": 303, - "versionNonce": 1456188592, - "isDeleted": false, - "id": "hCliSU94s9BGXBwt9V9Cz", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -465.0767722216874, - "y": 2146.622248973834, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 268.50899366592813, - "height": 120.34072952849813, - "seed": 1778072892, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "XxYAkqaHV42o7VvCwwdhJ" - }, - { - "id": "jYKNwDNwCT9HqtyU1cb6j", - "type": "arrow" - }, - { - "id": "ek9xtHVfDe-Hf4jNvTJNN", - "type": "arrow" - } - ], - "updated": 1714744053942, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 351, - "versionNonce": 244758096, - "isDeleted": false, - "id": "XxYAkqaHV42o7VvCwwdhJ", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -410.32824920464134, - "y": 2184.2926137380828, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 159.01194763183594, - "height": 45, - "seed": 1855468988, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053942, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "read cmd", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "hCliSU94s9BGXBwt9V9Cz", - "originalText": "read cmd", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "rectangle", - "version": 330, - "versionNonce": 1410504368, - "isDeleted": false, - "id": "zkcdoo4IxMpf3Io6otHZq", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 32.08426882387903, - "y": 1672.2575258163251, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 268.50899366592813, - "height": 120.34072952849813, - "seed": 906108476, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "c6kmxmWIl4-5yo2Pi83fh" - }, - { - "id": "OSsAiS2MCRmxV-q2MalgZ", - "type": "arrow" - } - ], - "updated": 1714744053942, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 400, - "versionNonce": 487287888, - "isDeleted": false, - "id": "c6kmxmWIl4-5yo2Pi83fh", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 76.10439065684307, - "y": 1715.627890580574, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 180.46875, - "height": 33.6, - "seed": 958068412, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053942, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 3, - "text": "1:set gains", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "zkcdoo4IxMpf3Io6otHZq", - "originalText": "1:set gains", - "lineHeight": 1.2, - "baseline": 28 - }, - { - "type": "rectangle", - "version": 378, - "versionNonce": 485245104, - "isDeleted": false, - "id": "6IK43Bfug0fISgEXMfPcp", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 32.08426882387903, - "y": 1832.4549615654232, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 268.50899366592813, - "height": 120.34072952849813, - "seed": 75808572, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "UPwfygmX8bemeNlVw1EeP" - }, - { - "id": "7CmBjWZiaxLIltxekbWVi", - "type": "arrow" - } - ], - "updated": 1714744053942, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 469, - "versionNonce": 662266448, - "isDeleted": false, - "id": "UPwfygmX8bemeNlVw1EeP", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 59.698140656843066, - "y": 1875.8253263296724, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 213.28125, - "height": 33.6, - "seed": 1084323772, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053942, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 3, - "text": "2.set current", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "6IK43Bfug0fISgEXMfPcp", - "originalText": "2.set current", - "lineHeight": 1.2, - "baseline": 28 - }, - { - "type": "rectangle", - "version": 418, - "versionNonce": 822127280, - "isDeleted": false, - "id": "bdtDFfCqknXWzFkbBWzlY", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 33.13311582593906, - "y": 1987.1296460922222, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 268.50899366592813, - "height": 120.34072952849813, - "seed": 965943356, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "Ij_bOM4RLwQ1FQsCPmfeb" - }, - { - "id": "mQQVZWFCaZll-lGe9sJFL", - "type": "arrow" - } - ], - "updated": 1714744053942, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 535, - "versionNonce": 1555601488, - "isDeleted": false, - "id": "Ij_bOM4RLwQ1FQsCPmfeb", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 68.9501126589031, - "y": 2013.7000108564712, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 196.875, - "height": 67.2, - "seed": 1731967164, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053942, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 3, - "text": "10.run array\nconfig", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "bdtDFfCqknXWzFkbBWzlY", - "originalText": "10.run array\nconfig", - "lineHeight": 1.2, - "baseline": 62 - }, - { - "type": "rectangle", - "version": 458, - "versionNonce": 1526191280, - "isDeleted": false, - "id": "NbYnTiIlKnGn-vT1BgUXG", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 33.13311582593906, - "y": 2141.525494319436, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 268.50899366592813, - "height": 120.34072952849813, - "seed": 1966948668, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "f3gVRuvhdURfRJvpSdgQ3" - }, - { - "id": "s1oV_1bxxeApejlYxosK_", - "type": "arrow" - }, - { - "id": "jYKNwDNwCT9HqtyU1cb6j", - "type": "arrow" - } - ], - "updated": 1714744053942, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 571, - "versionNonce": 1352732240, - "isDeleted": false, - "id": "f3gVRuvhdURfRJvpSdgQ3", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 85.3563626589031, - "y": 2184.895859083685, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 164.0625, - "height": 33.6, - "seed": 1257879996, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053942, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 3, - "text": "20.run MPF", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "NbYnTiIlKnGn-vT1BgUXG", - "originalText": "20.run MPF", - "lineHeight": 1.2, - "baseline": 28 - }, - { - "type": "rectangle", - "version": 491, - "versionNonce": 1350155952, - "isDeleted": false, - "id": "yi8YUhr6OLbLopAi9MGHa", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 33.13311582593906, - "y": 2295.429768034279, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 268.50899366592813, - "height": 120.34072952849813, - "seed": 328409660, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "EV2Vp2bvAYI5ZOmPQuYNE" - }, - { - "id": "EWVImJd5PN_hqIZKeyIS9", - "type": "arrow" - } - ], - "updated": 1714744053942, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 614, - "versionNonce": 1544209488, - "isDeleted": false, - "id": "EV2Vp2bvAYI5ZOmPQuYNE", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 68.9501126589031, - "y": 2338.800132798528, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 196.875, - "height": 33.6, - "seed": 1173060284, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053942, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 3, - "text": "21.run array", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "yi8YUhr6OLbLopAi9MGHa", - "originalText": "21.run array", - "lineHeight": 1.2, - "baseline": 28 - }, - { - "type": "rectangle", - "version": 533, - "versionNonce": 1378773168, - "isDeleted": false, - "id": "Ur3AZBQlRtcWGMQc7cqJZ", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 33.13311582593906, - "y": 2431.5034026484154, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 268.50899366592813, - "height": 120.34072952849813, - "seed": 748995388, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "-9GR8zduWrQC-XQutHFrt" - }, - { - "id": "7mtOYU4QDRX4K4NLvqMk3", - "type": "arrow" - } - ], - "updated": 1714744053942, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 680, - "versionNonce": 2093857360, - "isDeleted": false, - "id": "-9GR8zduWrQC-XQutHFrt", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 68.9501126589031, - "y": 2474.8737674126646, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 196.875, - "height": 33.6, - "seed": 1847655356, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 3, - "text": "31. get spec", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Ur3AZBQlRtcWGMQc7cqJZ", - "originalText": "31. get spec", - "lineHeight": 1.2, - "baseline": 28 - }, - { - "type": "rectangle", - "version": 551, - "versionNonce": 1270731440, - "isDeleted": false, - "id": "w8M4H0JRvxO08MbJjpNhG", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 33.13311582593906, - "y": 2573.0999085176936, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 268.50899366592813, - "height": 120.34072952849813, - "seed": 344073276, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "ZvZCZgvqRaDJEJlbn82VF" - }, - { - "id": "C9zNCvnFdLQJDCrQGY8nP", - "type": "arrow" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 711, - "versionNonce": 901902416, - "isDeleted": false, - "id": "ZvZCZgvqRaDJEJlbn82VF", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 68.9501126589031, - "y": 2616.470273281943, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 196.875, - "height": 33.6, - "seed": 363478204, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 3, - "text": "32. get temp", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "w8M4H0JRvxO08MbJjpNhG", - "originalText": "32. get temp", - "lineHeight": 1.2, - "baseline": 28 - }, - { - "type": "rectangle", - "version": 321, - "versionNonce": 1943010480, - "isDeleted": false, - "id": "NDEGY3EfBEYzOwrS2OYXC", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 483.79423282886455, - "y": 1907.2027352302175, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 233.89656246657583, - "height": 121.11090027476531, - "seed": 1530611004, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "VYBcw0ZEu1x5FhGAVUwuV" - }, - { - "id": "OSsAiS2MCRmxV-q2MalgZ", - "type": "arrow" - }, - { - "id": "7CmBjWZiaxLIltxekbWVi", - "type": "arrow" - }, - { - "id": "mQQVZWFCaZll-lGe9sJFL", - "type": "arrow" - }, - { - "id": "5e8pE6-Q5WVxBCsQPlJL-", - "type": "arrow" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 329, - "versionNonce": 1858037328, - "isDeleted": false, - "id": "VYBcw0ZEu1x5FhGAVUwuV", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 534.6943573238713, - "y": 1934.9812164163782, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 132.0963134765625, - "height": 65.553937902444, - "seed": 1251425724, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "CMD Done\n(161)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "NDEGY3EfBEYzOwrS2OYXC", - "originalText": "CMD Done\n(161)", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "arrow", - "version": 414, - "versionNonce": 1812784816, - "isDeleted": false, - "id": "OSsAiS2MCRmxV-q2MalgZ", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 315.27700048579845, - "y": 1730.502083610896, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 160.72423860764746, - "height": 164.9177156199111, - "seed": 1289758268, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "startBinding": { - "elementId": "zkcdoo4IxMpf3Io6otHZq", - "focus": -0.7818511425353244, - "gap": 14.683737995991294 - }, - "endBinding": { - "elementId": "NDEGY3EfBEYzOwrS2OYXC", - "focus": -0.3082579603972137, - "gap": 11.782935999410256 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 160.72423860764746, - 164.9177156199111 - ] - ] - }, - { - "type": "arrow", - "version": 392, - "versionNonce": 1419648080, - "isDeleted": false, - "id": "7CmBjWZiaxLIltxekbWVi", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 322.61900952211363, - "y": 1897.2712976336188, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 145.44235823206162, - "height": 45.06178574940823, - "seed": 1459093180, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "startBinding": { - "elementId": "6IK43Bfug0fISgEXMfPcp", - "focus": -0.43014100212738277, - "gap": 22.02574703230647 - }, - "endBinding": { - "elementId": "NDEGY3EfBEYzOwrS2OYXC", - "focus": -0.16203210909219073, - "gap": 15.732865074689244 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 145.44235823206162, - 45.06178574940823 - ] - ] - }, - { - "type": "arrow", - "version": 404, - "versionNonce": 1629262000, - "isDeleted": false, - "id": "mQQVZWFCaZll-lGe9sJFL", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 317.37469448991806, - "y": 2046.2098525501606, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 149.63782626219742, - "height": 71.88620736856174, - "seed": 1904265020, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "startBinding": { - "elementId": "bdtDFfCqknXWzFkbBWzlY", - "focus": 0.569229813113076, - "gap": 15.732584998050868 - }, - "endBinding": { - "elementId": "NDEGY3EfBEYzOwrS2OYXC", - "focus": 0.494087807924145, - "gap": 16.78171207674899 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 149.63782626219742, - -71.88620736856174 - ] - ] - }, - { - "type": "arrow", - "version": 728, - "versionNonce": 959844944, - "isDeleted": false, - "id": "5e8pE6-Q5WVxBCsQPlJL-", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 603.6512416399964, - "y": 1889.340799946709, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 3090.989014818514, - "height": 688.0832522879243, - "seed": 1088192444, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "startBinding": { - "elementId": "NDEGY3EfBEYzOwrS2OYXC", - "focus": 0.012193318881083352, - "gap": 17.8619352835085 - }, - "endBinding": { - "elementId": "949o_Sk--Ld6wo2FKS2fL", - "focus": 0.7775389777746909, - "gap": 15.116554543785924 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 5.608157783207142, - -299.390323902586 - ], - [ - -3085.380857035307, - -447.9184035809528 - ], - [ - -3083.187832256145, - 240.1648487069715 - ] - ] - }, - { - "type": "rectangle", - "version": 376, - "versionNonce": 715586224, - "isDeleted": false, - "id": "R0nMXwKFeadrft0ZosQ-f", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 448.9193431448157, - "y": 2231.6415856866715, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 233.89656246657583, - "height": 121.11090027476531, - "seed": 221063228, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "id": "s1oV_1bxxeApejlYxosK_", - "type": "arrow" - }, - { - "id": "EWVImJd5PN_hqIZKeyIS9", - "type": "arrow" - }, - { - "type": "text", - "id": "zVF10-saLAt7kjq1aldcL" - }, - { - "id": "BTXvg-RCegp3WrYRd1Fx_", - "type": "arrow" - }, - { - "id": "0ov9dJFlVufiTGN_Batn_", - "type": "arrow" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 377, - "versionNonce": 1935624272, - "isDeleted": false, - "id": "zVF10-saLAt7kjq1aldcL", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 504.4144993781036, - "y": 2260.731145630881, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 122.90625, - "height": 62.93178038634625, - "seed": 1247068348, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 3, - "text": "CMD Done\n(161)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "R0nMXwKFeadrft0ZosQ-f", - "originalText": "CMD Done\n(161)", - "lineHeight": 1.2, - "baseline": 58 - }, - { - "type": "arrow", - "version": 385, - "versionNonce": 1800676528, - "isDeleted": false, - "id": "s1oV_1bxxeApejlYxosK_", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 318.0739524985911, - "y": 2240.4604561540273, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 113.62682569757001, - "height": 28.09206390009679, - "seed": 536168764, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "startBinding": { - "elementId": "NbYnTiIlKnGn-vT1BgUXG", - "focus": 0.016176516931354017, - "gap": 16.43184300672391 - }, - "endBinding": { - "elementId": "R0nMXwKFeadrft0ZosQ-f", - "focus": -0.10646939352813495, - "gap": 17.218564948654546 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 113.62682569757001, - 28.09206390009679 - ] - ] - }, - { - "type": "arrow", - "version": 380, - "versionNonce": 1056030288, - "isDeleted": false, - "id": "EWVImJd5PN_hqIZKeyIS9", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 316.32600753165207, - "y": 2384.1056235194374, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 106.63424561084935, - "height": 51.38105636489672, - "seed": 1903215036, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "startBinding": { - "elementId": "yi8YUhr6OLbLopAi9MGHa", - "focus": 0.8030633168768259, - "gap": 14.683898039784879 - }, - "endBinding": { - "elementId": "R0nMXwKFeadrft0ZosQ-f", - "focus": 0.24234380222657106, - "gap": 25.959090002314213 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 106.63424561084935, - -51.38105636489672 - ] - ] - }, - { - "type": "rectangle", - "version": 405, - "versionNonce": 818679472, - "isDeleted": false, - "id": "TAfAwbMTEgK3giBptpu7a", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 854.4796522866723, - "y": 2232.925270282032, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 233.89656246657583, - "height": 121.11090027476531, - "seed": 1709779516, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "id": "s1oV_1bxxeApejlYxosK_", - "type": "arrow" - }, - { - "id": "EWVImJd5PN_hqIZKeyIS9", - "type": "arrow" - }, - { - "id": "BTXvg-RCegp3WrYRd1Fx_", - "type": "arrow" - }, - { - "type": "text", - "id": "yc1kY6T5k1WDP2E5hMY5_" - }, - { - "id": "5HnIVT4wdBSYi6xZCQtv4", - "type": "arrow" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 422, - "versionNonce": 482702416, - "isDeleted": false, - "id": "yc1kY6T5k1WDP2E5hMY5_", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 884.2719581781632, - "y": 2270.980720419415, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 174.31195068359375, - "height": 45, - "seed": 1883275964, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "ADPD run", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "TAfAwbMTEgK3giBptpu7a", - "originalText": "ADPD run", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 358, - "versionNonce": 330741936, - "isDeleted": false, - "id": "BTXvg-RCegp3WrYRd1Fx_", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 695.664608142702, - "y": 2293.1530307517987, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 153.8331609225059, - "height": 0.9358156326924376, - "seed": 1461242684, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "startBinding": { - "elementId": "R0nMXwKFeadrft0ZosQ-f", - "focus": 0.028491589862995152, - "gap": 12.848702531310607 - }, - "endBinding": { - "elementId": "TAfAwbMTEgK3giBptpu7a", - "focus": 0.03272967111097992, - "gap": 4.981883221464386 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 153.8331609225059, - -0.9358156326924376 - ] - ] - }, - { - "type": "rectangle", - "version": 521, - "versionNonce": 850553424, - "isDeleted": false, - "id": "SHT8Z3GcYk1eYuTfyw4Wt", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 575.2203036057099, - "y": 2514.8072032625405, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 233.89656246657583, - "height": 121.11090027476531, - "seed": 1020527548, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "iAJdqxhOI0k_feq8Uwxtj" - }, - { - "id": "7mtOYU4QDRX4K4NLvqMk3", - "type": "arrow" - }, - { - "id": "C9zNCvnFdLQJDCrQGY8nP", - "type": "arrow" - }, - { - "id": "Jy7jiHD5Z1gj-YInr76xP", - "type": "arrow" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 523, - "versionNonce": 1471947440, - "isDeleted": false, - "id": "iAJdqxhOI0k_feq8Uwxtj", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 626.1204281007166, - "y": 2542.585684448701, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 132.0963134765625, - "height": 65.553937902444, - "seed": 664022076, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "CMD Done\n(161)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "SHT8Z3GcYk1eYuTfyw4Wt", - "originalText": "CMD Done\n(161)", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "arrow", - "version": 411, - "versionNonce": 730898512, - "isDeleted": false, - "id": "7mtOYU4QDRX4K4NLvqMk3", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 318.0742192382472, - "y": 2478.832353262067, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 237.74212141573616, - "height": 112, - "seed": 154879164, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "kCQCzgIGfsxrXb1SZe3jH" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false, - "startBinding": { - "elementId": "Ur3AZBQlRtcWGMQc7cqJZ", - "focus": -0.5535205562715501, - "gap": 16.432109746380036 - }, - "endBinding": { - "elementId": "SHT8Z3GcYk1eYuTfyw4Wt", - "focus": -0.040339719106359855, - "gap": 19.40396295172644 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 237.74212141573616, - 63.70210159886882 - ] - ] - }, - { - "type": "text", - "version": 93, - "versionNonce": 193219760, - "isDeleted": false, - "id": "kCQCzgIGfsxrXb1SZe3jH", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -164.44798991065522, - "y": 2774.9504589649146, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 61.453125, - "height": 31.465890193173124, - "seed": 1198214460, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 3, - "text": "meas", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "7mtOYU4QDRX4K4NLvqMk3", - "originalText": "meas", - "lineHeight": 1.2, - "baseline": 26 - }, - { - "type": "arrow", - "version": 383, - "versionNonce": 389551696, - "isDeleted": false, - "id": "C9zNCvnFdLQJDCrQGY8nP", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 316.326007531653, - "y": 2618.3005360739553, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 241.238544828925, - "height": 112, - "seed": 1132873148, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "eFSZKVRSOkEGvhMKxDmAh" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false, - "startBinding": { - "elementId": "w8M4H0JRvxO08MbJjpNhG", - "focus": 0.11936697090636625, - "gap": 14.683898039785788 - }, - "endBinding": { - "elementId": "SHT8Z3GcYk1eYuTfyw4Wt", - "focus": 0.24613615402321784, - "gap": 17.655751245131853 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 241.238544828925, - -40.20633522493557 - ] - ] - }, - { - "type": "text", - "version": 93, - "versionNonce": 683095728, - "isDeleted": false, - "id": "eFSZKVRSOkEGvhMKxDmAh", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -164.44798991065505, - "y": 2862.464423364901, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 61.453125, - "height": 31.465890193173124, - "seed": 1655582268, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 3, - "text": "meas", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "C9zNCvnFdLQJDCrQGY8nP", - "originalText": "meas", - "lineHeight": 1.2, - "baseline": 26 - }, - { - "type": "arrow", - "version": 439, - "versionNonce": 1249954896, - "isDeleted": false, - "id": "Jy7jiHD5Z1gj-YInr76xP", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 737.6194218138885, - "y": 2491.972768277614, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 111.87888073063108, - "height": 902.0221588636638, - "seed": 365233852, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "startBinding": { - "elementId": "SHT8Z3GcYk1eYuTfyw4Wt", - "focus": 0.37982535473551726, - "gap": 22.834434984926702 - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 8.740525053659002, - -902.0221588636638 - ], - [ - -103.13835567697207, - -902.0221588636638 - ] - ] - }, - { - "type": "rectangle", - "version": 618, - "versionNonce": 872210608, - "isDeleted": false, - "id": "qSLZScUOArZiVJFz_NsGj", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1263.5362514718886, - "y": 2242.020959187892, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 312.5615546891644, - "height": 121.11090027476531, - "seed": 1634520892, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "id": "s1oV_1bxxeApejlYxosK_", - "type": "arrow" - }, - { - "id": "EWVImJd5PN_hqIZKeyIS9", - "type": "arrow" - }, - { - "id": "BTXvg-RCegp3WrYRd1Fx_", - "type": "arrow" - }, - { - "type": "text", - "id": "gAXVS9hXxi3dcVkSgbUn8" - }, - { - "id": "5HnIVT4wdBSYi6xZCQtv4", - "type": "arrow" - }, - { - "id": "6BugvbGckeMPIHactOOZE", - "type": "arrow" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 659, - "versionNonce": 1061339728, - "isDeleted": false, - "id": "gAXVS9hXxi3dcVkSgbUn8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1350.1390503008458, - "y": 2257.5764093252747, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 139.35595703125, - "height": 90, - "seed": 1711769532, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "wake up\ncalls 0", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "qSLZScUOArZiVJFz_NsGj", - "originalText": "wake up\ncalls 0", - "lineHeight": 1.25, - "baseline": 83 - }, - { - "type": "ellipse", - "version": 157, - "versionNonce": 1560103600, - "isDeleted": false, - "id": "y2e4ni4LWBSfKtYa3s8nT", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1263.799136740035, - "y": 2394.0787142958543, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 139.3841404869686, - "height": 139.3841404869686, - "seed": 1740058684, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "lBtEZLY2LoTkLrZHlaEBE" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 213, - "versionNonce": 1951791184, - "isDeleted": false, - "id": "lBtEZLY2LoTkLrZHlaEBE", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1302.1294936751842, - "y": 2431.21408012402, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 63.16395568847656, - "height": 65.553937902444, - "seed": 1448986812, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "buf\nclear", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "y2e4ni4LWBSfKtYa3s8nT", - "originalText": "buf\nclear", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "ellipse", - "version": 180, - "versionNonce": 509637808, - "isDeleted": false, - "id": "gKny8cgFf1eK8KNEfsu1X", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1429.8691127595603, - "y": 2394.0787142958543, - "strokeColor": "#2f9e44", - "backgroundColor": "#a5d8ff", - "width": 139.3841404869686, - "height": 139.3841404869686, - "seed": 1436373308, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "P5_Yz5mVJDY9sXWVbzD_n" - }, - { - "id": "L50BGCy0awqTIQHghyt9z", - "type": "arrow" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 243, - "versionNonce": 1301442128, - "isDeleted": false, - "id": "P5_Yz5mVJDY9sXWVbzD_n", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1469.4973594041821, - "y": 2431.21408012402, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 60.56817626953125, - "height": 65.553937902444, - "seed": 1154992572, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "wake\n211", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "gKny8cgFf1eK8KNEfsu1X", - "originalText": "wake\n211", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "diamond", - "version": 479, - "versionNonce": 1573805744, - "isDeleted": false, - "id": "Vswn_gFvxMoxlj_jej-d8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1687.802065775767, - "y": 2189.0505779300256, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 219.45825162823473, - "seed": 1194275388, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "id": "6BugvbGckeMPIHactOOZE", - "type": "arrow" - }, - { - "id": "qhDyQfFkqHq0kXOVvxY_b", - "type": "arrow" - }, - { - "id": "4_WZn9xeDSBDx7Mkg_hO0", - "type": "arrow" - }, - { - "type": "text", - "id": "lq7O7hIUiNEJnNgmn2SjF" - }, - { - "id": "lkSF2Cz3w84tbQRvuXG1P", - "type": "arrow" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 516, - "versionNonce": 1943020624, - "isDeleted": false, - "id": "lq7O7hIUiNEJnNgmn2SjF", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1766.154171316754, - "y": 2266.1381718858624, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 77.76849365234375, - "height": 65.553937902444, - "seed": 1267043004, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "Awake\n(210)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Vswn_gFvxMoxlj_jej-d8", - "originalText": "Awake\n(210)", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "diamond", - "version": 575, - "versionNonce": 200214704, - "isDeleted": false, - "id": "A6XONEaAFuF1YW-ZTzIXs", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1687.802065775767, - "y": 1913.6692904253305, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 222.88338886830962, - "seed": 343724860, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "zTmzZBLGTl-LJ9j4ea9lU" - }, - { - "id": "4_WZn9xeDSBDx7Mkg_hO0", - "type": "arrow" - }, - { - "id": "w48z_r3CFcohLuEU1P684", - "type": "arrow" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 632, - "versionNonce": 887560784, - "isDeleted": false, - "id": "zTmzZBLGTl-LJ9j4ea9lU", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1771.2015269686094, - "y": 1975.7246842155746, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 67.67378234863281, - "height": 98.33090685366601, - "seed": 1081543612, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "Ambit\nwake\n170", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "A6XONEaAFuF1YW-ZTzIXs", - "originalText": "Ambit\nwake\n170", - "lineHeight": 1.25, - "baseline": 92 - }, - { - "type": "diamond", - "version": 620, - "versionNonce": 1147826864, - "isDeleted": false, - "id": "Dy1HFFDMmS5fyBx8k3h7k", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1694.7949126021458, - "y": 1654.8404989423398, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 219.45825162823473, - "seed": 1368798268, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "cMv-xGTbgpxOxQUClVR73" - }, - { - "id": "Lbhp_UFrpA9qz0Sjh3F_X", - "type": "arrow" - }, - { - "id": "OVXw14xqvamX9AQXfYgcc", - "type": "arrow" - }, - { - "id": "fNlhld2BS4snfpZ0Sn2La", - "type": "arrow" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 676, - "versionNonce": 1672433744, - "isDeleted": false, - "id": "cMv-xGTbgpxOxQUClVR73", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1774.1565091099296, - "y": 1731.9280928981764, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 75.74951171875, - "height": 65.553937902444, - "seed": 901532860, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "Reset\n(222)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Dy1HFFDMmS5fyBx8k3h7k", - "originalText": "Reset\n(222)", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "rectangle", - "version": 697, - "versionNonce": 1843961008, - "isDeleted": false, - "id": "kcrgX4XIE6URlJf9Ew8_e", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2037.9465578343684, - "y": 2242.020959187892, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 312.5615546891644, - "height": 121.11090027476531, - "seed": 1317754172, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "id": "s1oV_1bxxeApejlYxosK_", - "type": "arrow" - }, - { - "id": "EWVImJd5PN_hqIZKeyIS9", - "type": "arrow" - }, - { - "id": "BTXvg-RCegp3WrYRd1Fx_", - "type": "arrow" - }, - { - "id": "qhDyQfFkqHq0kXOVvxY_b", - "type": "arrow" - }, - { - "type": "text", - "id": "ue0buZG8YRti9qmUTlH-R" - }, - { - "id": "rva0j4f5-xA3KLWub0fg7", - "type": "arrow" - } - ], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 762, - "versionNonce": 939574864, - "isDeleted": false, - "id": "ue0buZG8YRti9qmUTlH-R", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2055.8253881574665, - "y": 2257.5764093252747, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 276.80389404296875, - "height": 90, - "seed": 2036682172, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "send length info\n1", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "kcrgX4XIE6URlJf9Ew8_e", - "originalText": "send length info\n1", - "lineHeight": 1.25, - "baseline": 83 - }, - { - "type": "rectangle", - "version": 765, - "versionNonce": 1511098032, - "isDeleted": false, - "id": "gAmwiSt1Yl0N5qJUv0p_Z", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "dotted", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2824.561089604326, - "y": 1493.0945953942132, - "strokeColor": "#e03131", - "backgroundColor": "#ffec99", - "width": 1350.0003051757808, - "height": 1246.666755676269, - "seed": 466529531, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false - }, - { - "type": "arrow", - "version": 346, - "versionNonce": 783514704, - "isDeleted": false, - "id": "5HnIVT4wdBSYi6xZCQtv4", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1113.4619991212344, - "y": 2296.6491874253315, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "width": 138.1004558916087, - "height": 1.2838179651874806, - "seed": 392967740, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "startBinding": { - "elementId": "TAfAwbMTEgK3giBptpu7a", - "focus": 0.029980568922257398, - "gap": 25.085784367986207 - }, - "endBinding": { - "elementId": "qSLZScUOArZiVJFz_NsGj", - "focus": 0.049658941415414874, - "gap": 11.973796459045388 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 138.1004558916087, - 1.2838179651874806 - ] - ] - }, - { - "type": "arrow", - "version": 344, - "versionNonce": 1703751856, - "isDeleted": false, - "id": "6BugvbGckeMPIHactOOZE", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1599.4352454526174, - "y": 2314.037565768769, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "width": 80.35548509632035, - "height": 3.9215436107445356, - "seed": 360844988, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053943, - "link": null, - "locked": false, - "startBinding": { - "elementId": "qSLZScUOArZiVJFz_NsGj", - "focus": 0.2966598891762295, - "gap": 23.33743929156435 - }, - "endBinding": { - "elementId": "Vswn_gFvxMoxlj_jej-d8", - "focus": -0.04750234949950599, - "gap": 13.753003304900218 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 80.35548509632035, - -3.9215436107445356 - ] - ] - }, - { - "type": "arrow", - "version": 354, - "versionNonce": 1772057168, - "isDeleted": false, - "id": "qhDyQfFkqHq0kXOVvxY_b", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1933.015906326351, - "y": 2301.744795169394, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "width": 91.20891008203469, - "height": 1.3562021069867687, - "seed": 1950930748, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": { - "elementId": "Vswn_gFvxMoxlj_jej-d8", - "focus": 0.009711960318178375, - "gap": 9.176175869011061 - }, - "endBinding": { - "elementId": "kcrgX4XIE6URlJf9Ew8_e", - "focus": -0.04854365024789139, - "gap": 13.721741425983055 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 91.20891008203469, - 1.3562021069867687 - ] - ] - }, - { - "type": "arrow", - "version": 362, - "versionNonce": 402889392, - "isDeleted": false, - "id": "4_WZn9xeDSBDx7Mkg_hO0", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1802.470830749241, - "y": 2189.3116969104212, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "width": 3.541252778497889, - "height": 46.76052119185137, - "seed": 1344442300, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": { - "elementId": "Vswn_gFvxMoxlj_jej-d8", - "focus": 0.046702390998293965, - "gap": 1.7231777265871244 - }, - "endBinding": { - "elementId": "A6XONEaAFuF1YW-ZTzIXs", - "focus": 0.12972490421438407, - "gap": 8.718821959924895 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -3.541252778497889, - -46.76052119185137 - ] - ] - }, - { - "type": "arrow", - "version": 576, - "versionNonce": 261924944, - "isDeleted": false, - "id": "w48z_r3CFcohLuEU1P684", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1678.210617877989, - "y": 2026.024876345019, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "width": 183.66140632962447, - "height": 2.5164180489332466, - "seed": 1764833340, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": { - "elementId": "A6XONEaAFuF1YW-ZTzIXs", - "focus": -0.023822753611799197, - "gap": 9.634888189445448 - }, - "endBinding": { - "elementId": "_xAS16LWhNy5Gw6c3Abgu", - "focus": -0.48212803665819465, - "gap": 26.571011077067737 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -183.66140632962447, - -2.5164180489332466 - ] - ] - }, - { - "type": "arrow", - "version": 229, - "versionNonce": 1142736048, - "isDeleted": false, - "id": "Lbhp_UFrpA9qz0Sjh3F_X", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1803.9634783603115, - "y": 1919.0585317812183, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "width": 5.103656499848702, - "height": 40.81884762541737, - "seed": 727109820, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": { - "elementId": "Dy1HFFDMmS5fyBx8k3h7k", - "focus": -0.09374004509086609, - "gap": 5.064647792888465 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 5.103656499848702, - -40.81884762541737 - ] - ] - }, - { - "type": "arrow", - "version": 397, - "versionNonce": 746790480, - "isDeleted": false, - "id": "ksZH5o4yNx8HLY0yxGFbD", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1265.9543940254225, - "y": 1982.755085231943, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 514.3500787777475, - "height": 387.56034083506404, - "seed": 1466251836, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": { - "elementId": "r8QgYWHtt1ERGS9dITEB-", - "focus": -1.1187841319912741, - "gap": 17.702114966580893 - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -514.3500787777475, - -387.56034083506404 - ] - ] - }, - { - "type": "arrow", - "version": 387, - "versionNonce": 662744752, - "isDeleted": false, - "id": "OVXw14xqvamX9AQXfYgcc", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1691.2884221676595, - "y": 1764.9041793277192, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 518.3906926377496, - "height": 518.7228077829324, - "seed": 2008715964, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": { - "elementId": "Dy1HFFDMmS5fyBx8k3h7k", - "focus": -0.01723764136291547, - "gap": 2.638053944030233 - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -518.3906926377496, - -6.671321870813335 - ], - [ - -514.8942692245607, - 512.051485912119 - ] - ] - }, - { - "type": "diamond", - "version": 679, - "versionNonce": 288019536, - "isDeleted": false, - "id": "N_kTAn2ip0eQE6iXbIrQv", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1982.3577334101178, - "y": 2377.354304565746, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 156.28081735553135, - "height": 157.34578876980638, - "seed": 80796476, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "Kc42nuulcX904ruXDYBVk" - }, - { - "id": "meH7fxdoDWONFZedxAMC9", - "type": "arrow" - } - ], - "updated": 1714744053944, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 776, - "versionNonce": 181885104, - "isDeleted": false, - "id": "Kc42nuulcX904ruXDYBVk", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2029.3294422656022, - "y": 2429.96917659722, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 62.196990966796875, - "height": 52.443150321955216, - "seed": 420090812, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "fontSize": 20.977260128782085, - "fontFamily": 1, - "text": "Awake\n(210)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "N_kTAn2ip0eQE6iXbIrQv", - "originalText": "Awake\n(210)", - "lineHeight": 1.25, - "baseline": 47 - }, - { - "type": "ellipse", - "version": 278, - "versionNonce": 862702160, - "isDeleted": false, - "id": "3ZsaM9HAI9xyZdhjU5G6O", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2289.6289107200214, - "y": 2387.4120984918172, - "strokeColor": "#2f9e44", - "backgroundColor": "#a5d8ff", - "width": 139.3841404869686, - "height": 139.3841404869686, - "seed": 847789116, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "MzwMYkNhdgHEUHpWZZ5zF" - }, - { - "id": "SPFSlS5fEkc_qQBkM9eLq", - "type": "arrow" - } - ], - "updated": 1714744053944, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 373, - "versionNonce": 1640546992, - "isDeleted": false, - "id": "MzwMYkNhdgHEUHpWZZ5zF", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2322.348198929584, - "y": 2424.5474643199827, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 74.38609313964844, - "height": 65.553937902444, - "seed": 992826556, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "length\n212", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "3ZsaM9HAI9xyZdhjU5G6O", - "originalText": "length\n212", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "diamond", - "version": 508, - "versionNonce": 236847184, - "isDeleted": false, - "id": "k4wYSdGSsCo2g1t0Te4BW", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2462.2125055080755, - "y": 2189.0505779300256, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 222.88338886830962, - "seed": 278617404, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "id": "6BugvbGckeMPIHactOOZE", - "type": "arrow" - }, - { - "id": "qhDyQfFkqHq0kXOVvxY_b", - "type": "arrow" - }, - { - "id": "4_WZn9xeDSBDx7Mkg_hO0", - "type": "arrow" - }, - { - "type": "text", - "id": "tNGUuAf_S3eZboOy3I-iT" - }, - { - "id": "CMoLNeKsWZqOByiv7kqiN", - "type": "arrow" - }, - { - "id": "rva0j4f5-xA3KLWub0fg7", - "type": "arrow" - }, - { - "id": "KgnwsuNsjv2jMGhxXrA0g", - "type": "arrow" - }, - { - "id": "U2EvPXeHbkdhv1IajJrxn", - "type": "arrow" - } - ], - "updated": 1714744053944, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 555, - "versionNonce": 1794960560, - "isDeleted": false, - "id": "tNGUuAf_S3eZboOy3I-iT", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2544.2878547624414, - "y": 2251.1059717202697, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 70.32200622558594, - "height": 98.33090685366601, - "seed": 204756412, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "Data\nready\n200", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "k4wYSdGSsCo2g1t0Te4BW", - "originalText": "Data\nready\n200", - "lineHeight": 1.25, - "baseline": 92 - }, - { - "type": "diamond", - "version": 538, - "versionNonce": 18445904, - "isDeleted": false, - "id": "34chdPI9dxnIxbtR92YBx", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2462.2125055080755, - "y": 1914.1337508516517, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 222.88338886830962, - "seed": 186269244, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "id": "6BugvbGckeMPIHactOOZE", - "type": "arrow" - }, - { - "id": "qhDyQfFkqHq0kXOVvxY_b", - "type": "arrow" - }, - { - "id": "4_WZn9xeDSBDx7Mkg_hO0", - "type": "arrow" - }, - { - "type": "text", - "id": "C4ffhyqmxNXcwVJ9Fa1SE" - }, - { - "id": "lkSF2Cz3w84tbQRvuXG1P", - "type": "arrow" - }, - { - "id": "KgnwsuNsjv2jMGhxXrA0g", - "type": "arrow" - } - ], - "updated": 1714744053944, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 592, - "versionNonce": 1344001712, - "isDeleted": false, - "id": "C4ffhyqmxNXcwVJ9Fa1SE", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2540.5646110490625, - "y": 1992.577629117507, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 77.76849365234375, - "height": 65.553937902444, - "seed": 1982797500, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "Awake\n(210)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "34chdPI9dxnIxbtR92YBx", - "originalText": "Awake\n(210)", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "diamond", - "version": 659, - "versionNonce": 825633872, - "isDeleted": false, - "id": "WE37nkuHKBGUDppwGsS5u", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2465.708395441952, - "y": 1650.5250180723979, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 202.31586085689204, - "seed": 1242881852, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "b1SGoGCgXHnm0iAPk63bd" - }, - { - "id": "6BugvbGckeMPIHactOOZE", - "type": "arrow" - }, - { - "id": "qhDyQfFkqHq0kXOVvxY_b", - "type": "arrow" - }, - { - "id": "4_WZn9xeDSBDx7Mkg_hO0", - "type": "arrow" - }, - { - "id": "yU4-Ia2xMJoY7ELd6lx_h", - "type": "arrow" - }, - { - "id": "RUnPrOD-VXiYiCxnrfXqG", - "type": "arrow" - } - ], - "updated": 1714744053944, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 716, - "versionNonce": 1171095728, - "isDeleted": false, - "id": "b1SGoGCgXHnm0iAPk63bd", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2545.0699919497356, - "y": 1718.8270143353989, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 75.74951171875, - "height": 65.553937902444, - "seed": 523081660, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "Reset\n(222)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "WE37nkuHKBGUDppwGsS5u", - "originalText": "Reset\n(222)", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "arrow", - "version": 740, - "versionNonce": 1749819984, - "isDeleted": false, - "id": "lkSF2Cz3w84tbQRvuXG1P", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2454.258889114109, - "y": 2026.9767287270824, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 557.9883641971126, - "height": 228.53008413746977, - "seed": 832258108, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": { - "elementId": "34chdPI9dxnIxbtR92YBx", - "focus": -0.01777290911946115, - "gap": 6.490593978475275 - }, - "endBinding": { - "elementId": "Vswn_gFvxMoxlj_jej-d8", - "focus": 0.6407492940644685, - "gap": 15.166454499261874 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -478.9809329843176, - -2.212472078173153 - ], - [ - -557.9883641971126, - 226.3176120592966 - ] - ] - }, - { - "type": "arrow", - "version": 362, - "versionNonce": 733604528, - "isDeleted": false, - "id": "rva0j4f5-xA3KLWub0fg7", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2368.601716914283, - "y": 2301.8935558054563, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 76.91651377633869, - "height": 1.7480783367662225, - "seed": 1765791932, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": { - "elementId": "kcrgX4XIE6URlJf9Ew8_e", - "focus": 0.051166542257359934, - "gap": 18.09360439074976 - }, - "endBinding": { - "elementId": "k4wYSdGSsCo2g1t0Te4BW", - "focus": 0.030473353352103227, - "gap": 11.741235840797415 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 76.91651377633869, - -1.7480783367662225 - ] - ] - }, - { - "type": "arrow", - "version": 288, - "versionNonce": 1966299216, - "isDeleted": false, - "id": "KgnwsuNsjv2jMGhxXrA0g", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2585.3666315491687, - "y": 2179.526205054228, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 0, - "height": 41.95448024661574, - "seed": 1639801148, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": { - "elementId": "k4wYSdGSsCo2g1t0Te4BW", - "focus": 0.04836375666691641, - "gap": 10.819958053979562 - }, - "endBinding": { - "elementId": "34chdPI9dxnIxbtR92YBx", - "focus": -0.04836375666691641, - "gap": 4.312515406365563 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 0, - -41.95448024661574 - ] - ] - }, - { - "type": "arrow", - "version": 235, - "versionNonce": 747181232, - "isDeleted": false, - "id": "yU4-Ia2xMJoY7ELd6lx_h", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2578.374318202105, - "y": 1915.5623751076855, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 1.7479449669377358, - "height": 54.655622400128294, - "seed": 1920326076, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": { - "elementId": "WE37nkuHKBGUDppwGsS5u", - "focus": -0.003696658593138085, - "gap": 8.108016770816732 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 1.7479449669377358, - -54.655622400128294 - ] - ] - }, - { - "type": "arrow", - "version": 649, - "versionNonce": 197381712, - "isDeleted": false, - "id": "RUnPrOD-VXiYiCxnrfXqG", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2452.5108107773426, - "y": 1759.9810024785875, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 1272.2421494841942, - "height": 164.32186434030154, - "seed": 915096124, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": { - "elementId": "WE37nkuHKBGUDppwGsS5u", - "focus": -0.05142061028716597, - "gap": 14.899755456221797 - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -368.8502639602806, - 8.740525053659152 - ], - [ - -360.1097389066216, - -152.0851159281959 - ], - [ - -854.8235102916636, - -155.5813392866424 - ], - [ - -1272.2421494841942, - -8.352795886928106 - ] - ] - }, - { - "type": "rectangle", - "version": 821, - "versionNonce": 1575157424, - "isDeleted": false, - "id": "wd7YXgtyvh01iiFDbjeH9", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2838.5784393578283, - "y": 2246.336606770119, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 510.09747424979207, - "height": 121.11090027476531, - "seed": 350198460, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "kgw0XxlnSkZpNb_p7KodU" - }, - { - "id": "s1oV_1bxxeApejlYxosK_", - "type": "arrow" - }, - { - "id": "EWVImJd5PN_hqIZKeyIS9", - "type": "arrow" - }, - { - "id": "BTXvg-RCegp3WrYRd1Fx_", - "type": "arrow" - }, - { - "id": "qhDyQfFkqHq0kXOVvxY_b", - "type": "arrow" - }, - { - "id": "lkSF2Cz3w84tbQRvuXG1P", - "type": "arrow" - }, - { - "id": "CFu19CmnN1JxOHt6ZiolU", - "type": "arrow" - }, - { - "id": "CMoLNeKsWZqOByiv7kqiN", - "type": "arrow" - } - ], - "updated": 1714744053944, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 901, - "versionNonce": 617972816, - "isDeleted": false, - "id": "kgw0XxlnSkZpNb_p7KodU", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2966.565218169736, - "y": 2284.3920569075017, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 254.12391662597656, - "height": 45, - "seed": 1209328444, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "send data (2)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "wd7YXgtyvh01iiFDbjeH9", - "originalText": "send data (2)", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "diamond", - "version": 703, - "versionNonce": 1617930416, - "isDeleted": false, - "id": "CJVOPFdInQ7GHYiRl2bPN", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2817.514862276306, - "y": 2397.9764808642212, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 156.28081735553135, - "height": 157.34578876980638, - "seed": 1720523708, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "SihYnjDpcrRVcWISADJ71" - }, - { - "id": "38p8yj5Uhz8GhXiRADMtD", - "type": "arrow" - } - ], - "updated": 1714744053944, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 812, - "versionNonce": 1581283920, - "isDeleted": false, - "id": "SihYnjDpcrRVcWISADJ71", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2864.8640430556184, - "y": 2450.5913528956953, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 61.442047119140625, - "height": 52.443150321955216, - "seed": 1785349180, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "fontSize": 20.977260128782085, - "fontFamily": 1, - "text": "Ready\n200", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "CJVOPFdInQ7GHYiRl2bPN", - "originalText": "Ready\n200", - "lineHeight": 1.25, - "baseline": 47 - }, - { - "type": "arrow", - "version": 350, - "versionNonce": 2144801456, - "isDeleted": false, - "id": "meH7fxdoDWONFZedxAMC9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2067.92757504651, - "y": 2548.3763356446816, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 886.2893204629399, - "height": 304.1702985413057, - "seed": 965631164, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": { - "elementId": "N_kTAn2ip0eQE6iXbIrQv", - "focus": 0.00550407107670552, - "gap": 14.90888094252869 - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 6.992446716893377, - 82.16101552629344 - ], - [ - -879.2968737460466, - 78.66472548293281 - ], - [ - -877.5486620394524, - -222.00928301501224 - ] - ] - }, - { - "type": "arrow", - "version": 361, - "versionNonce": 1524412496, - "isDeleted": false, - "id": "38p8yj5Uhz8GhXiRADMtD", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2814.3684946509034, - "y": 2490.22455657102, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 725.4637128235424, - "height": 139.38400711714024, - "seed": 765158716, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": { - "elementId": "CJVOPFdInQ7GHYiRl2bPN", - "focus": 0.025962199840136233, - "gap": 11.798812015943149 - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -725.4637128235424, - 139.38400711714024 - ] - ] - }, - { - "type": "ellipse", - "version": 242, - "versionNonce": 1388395696, - "isDeleted": false, - "id": "OkLwDHyZTul2qE2aBhHEy", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2989.411125909877, - "y": 2409.142436269056, - "strokeColor": "#2f9e44", - "backgroundColor": "#a5d8ff", - "width": 139.3841404869686, - "height": 139.3841404869686, - "seed": 468934076, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "-Pw65CXy3gpCSlR4FtT51" - }, - { - "id": "gyPcyFeW-J2407VPWQOUM", - "type": "arrow" - }, - { - "id": "bLoyDI7x4qG_GKX6CeMT3", - "type": "arrow" - } - ], - "updated": 1714744053944, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 341, - "versionNonce": 578954832, - "isDeleted": false, - "id": "-Pw65CXy3gpCSlR4FtT51", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3026.9679919392643, - "y": 2462.666286572832, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 64.7109375, - "height": 32.776968951222, - "seed": 1572357692, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "data", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "OkLwDHyZTul2qE2aBhHEy", - "originalText": "data", - "lineHeight": 1.25, - "baseline": 26 - }, - { - "type": "ellipse", - "version": 297, - "versionNonce": 1523984048, - "isDeleted": false, - "id": "ZIqLdTRRThlw8lHcE4onP", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3141.4962084956146, - "y": 2402.9694137759416, - "strokeColor": "#2f9e44", - "backgroundColor": "#a5d8ff", - "width": 177.84266411479416, - "height": 157.32945096586562, - "seed": 1472995004, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "Deda_sUeSYuIMRJ5U1KIy" - }, - { - "id": "ObbXkkUi4RX8Luqzn8JMg", - "type": "arrow" - } - ], - "updated": 1714744053944, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 438, - "versionNonce": 34135120, - "isDeleted": false, - "id": "Deda_sUeSYuIMRJ5U1KIy", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3174.4823293124996, - "y": 2448.7328094784925, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 112.11666870117188, - "height": 65.553937902444, - "seed": 47245116, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "checksum\n212", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "ZIqLdTRRThlw8lHcE4onP", - "originalText": "checksum\n212", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "diamond", - "version": 547, - "versionNonce": 616419504, - "isDeleted": false, - "id": "8rA9xpm9xmRmEM21VM0lU", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3446.395679898031, - "y": 2200.8229992856377, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 222.88338886830962, - "seed": 1932042172, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "id": "6BugvbGckeMPIHactOOZE", - "type": "arrow" - }, - { - "id": "qhDyQfFkqHq0kXOVvxY_b", - "type": "arrow" - }, - { - "id": "4_WZn9xeDSBDx7Mkg_hO0", - "type": "arrow" - }, - { - "type": "text", - "id": "QNsPWLwVwhzn49ygKxv5a" - }, - { - "id": "CFu19CmnN1JxOHt6ZiolU", - "type": "arrow" - }, - { - "id": "8CEtBqAZieSjbWOmO_PSg", - "type": "arrow" - }, - { - "id": "DB3e9gtzmMSohg8hJcoZI", - "type": "arrow" - }, - { - "id": "-3xsQ8CUpzKqR3rQuEvHY", - "type": "arrow" - } - ], - "updated": 1714744053944, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 611, - "versionNonce": 1799934544, - "isDeleted": false, - "id": "QNsPWLwVwhzn49ygKxv5a", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3531.984502663139, - "y": 2279.266877551493, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 63.29505920410156, - "height": 65.553937902444, - "seed": 1626687548, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "Pass\n180", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "8rA9xpm9xmRmEM21VM0lU", - "originalText": "Pass\n180", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "diamond", - "version": 539, - "versionNonce": 1746942640, - "isDeleted": false, - "id": "fLrsRoZN3z9h4LDm0lB1t", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3447.2696523815025, - "y": 1914.633654309786, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 222.88338886830962, - "seed": 806936764, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "2W0bhnszToQz3HNbeD6Q5" - }, - { - "id": "DB3e9gtzmMSohg8hJcoZI", - "type": "arrow" - }, - { - "id": "dmIr2dlTpFl6-BrVArGX1", - "type": "arrow" - } - ], - "updated": 1714744053944, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 587, - "versionNonce": 1427769424, - "isDeleted": false, - "id": "2W0bhnszToQz3HNbeD6Q5", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3529.3450016358684, - "y": 1976.68904810003, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 70.32200622558594, - "height": 98.33090685366601, - "seed": 2016766268, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "Data\nready\n200", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "fLrsRoZN3z9h4LDm0lB1t", - "originalText": "Data\nready\n200", - "lineHeight": 1.25, - "baseline": 92 - }, - { - "type": "diamond", - "version": 717, - "versionNonce": 665150640, - "isDeleted": false, - "id": "LdRxYtp-vyX2QeDnxXSrS", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3449.017864088097, - "y": 1639.5392953189967, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 202.31586085689204, - "seed": 212656572, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "ooWWRl3O0f9_XGRFNJqxD" - }, - { - "id": "P7nbNgM-TYS29fTy3DVih", - "type": "arrow" - }, - { - "id": "mazLiIjyU9ysfCDKZcVgX", - "type": "arrow" - }, - { - "id": "gyPcyFeW-J2407VPWQOUM", - "type": "arrow" - } - ], - "updated": 1714744053944, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 765, - "versionNonce": 458522192, - "isDeleted": false, - "id": "ooWWRl3O0f9_XGRFNJqxD", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3528.379460595881, - "y": 1707.8412915819977, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 75.74951171875, - "height": 65.553937902444, - "seed": 77679164, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "Reset\n(222)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "LdRxYtp-vyX2QeDnxXSrS", - "originalText": "Reset\n(222)", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "arrow", - "version": 335, - "versionNonce": 1358253744, - "isDeleted": false, - "id": "CFu19CmnN1JxOHt6ZiolU", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3365.0215730314326, - "y": 2315.8783825543287, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 73.42035710280446, - "height": 5.708495381877409, - "seed": 1123328700, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053944, - "link": null, - "locked": false, - "startBinding": { - "elementId": "wd7YXgtyvh01iiFDbjeH9", - "focus": 0.3742888204104405, - "gap": 16.34565942381232 - }, - "endBinding": { - "elementId": "8rA9xpm9xmRmEM21VM0lU", - "focus": 0.106305098963807, - "gap": 6.993826081966404 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 73.42035710280446, - -5.708495381877409 - ] - ] - }, - { - "type": "arrow", - "version": 342, - "versionNonce": 1590703184, - "isDeleted": false, - "id": "CMoLNeKsWZqOByiv7kqiN", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2704.2378256268644, - "y": 2301.8935558054563, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 111.87861399097531, - "height": 1.2837512802733864, - "seed": 2118708028, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "k4wYSdGSsCo2g1t0Te4BW", - "focus": -0.00025031638454271223, - "gap": 5.889276108841742 - }, - "endBinding": { - "elementId": "wd7YXgtyvh01iiFDbjeH9", - "focus": 0.008355951359017745, - "gap": 22.46199973998864 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 111.87861399097531, - 1.2837512802733864 - ] - ] - }, - { - "type": "rectangle", - "version": 714, - "versionNonce": 1555560624, - "isDeleted": false, - "id": "fLK6ZVR8UYtruAYgkEaKw", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3806.1554697127326, - "y": 2258.327487904142, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 312.5615546891644, - "height": 121.11090027476531, - "seed": 1224745916, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "VdrP7iaNj0HsSEIJBGrhq" - }, - { - "id": "8CEtBqAZieSjbWOmO_PSg", - "type": "arrow" - }, - { - "id": "Aujbd37Vz68tzdcf0qX0K", - "type": "arrow" - } - ], - "updated": 1714744053945, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 800, - "versionNonce": 1701128784, - "isDeleted": false, - "id": "VdrP7iaNj0HsSEIJBGrhq", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3886.0082838004787, - "y": 2296.3829380415245, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 152.85592651367188, - "height": 45, - "seed": 423382076, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "Complete", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "fLK6ZVR8UYtruAYgkEaKw", - "originalText": "Complete", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 338, - "versionNonce": 1855283888, - "isDeleted": false, - "id": "8CEtBqAZieSjbWOmO_PSg", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3697.1615250704804, - "y": 2310.634080859116, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 87.4052505365936, - "height": 1.2837512802733864, - "seed": 1356817596, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "8rA9xpm9xmRmEM21VM0lU", - "focus": -0.032199218974212185, - "gap": 12.071216306949111 - }, - "endBinding": { - "elementId": "fLK6ZVR8UYtruAYgkEaKw", - "focus": 0.06925409328961642, - "gap": 21.588694105658988 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 87.4052505365936, - 1.2837512802733864 - ] - ] - }, - { - "type": "arrow", - "version": 258, - "versionNonce": 2045468752, - "isDeleted": false, - "id": "DB3e9gtzmMSohg8hJcoZI", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3564.3054375589986, - "y": 2181.274350075908, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 0, - "height": 40.670795651256455, - "seed": 1166765372, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "8rA9xpm9xmRmEM21VM0lU", - "focus": 0.0037204636399393283, - "gap": 14.483040010456321 - }, - "endBinding": { - "elementId": "fLrsRoZN3z9h4LDm0lB1t", - "focus": 0.0037193283138422326, - "gap": 2.539921118278727 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 0, - -40.670795651256455 - ] - ] - }, - { - "type": "arrow", - "version": 482, - "versionNonce": 1966443696, - "isDeleted": false, - "id": "dmIr2dlTpFl6-BrVArGX1", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3436.6937184276444, - "y": 2037.929725858914, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 654.8500208638843, - "height": 256.88986983428936, - "seed": 1218602428, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "fLrsRoZN3z9h4LDm0lB1t", - "focus": -0.11557471422657803, - "gap": 15.87893188155762 - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -654.8500208638843, - -5.244301695212698 - ], - [ - -649.5240057185702, - 251.64556813907666 - ] - ] - }, - { - "type": "arrow", - "version": 282, - "versionNonce": 1278924368, - "isDeleted": false, - "id": "P7nbNgM-TYS29fTy3DVih", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3562.5574925920614, - "y": 1919.0585984661338, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 1.7479449669389282, - "height": 62.93182039729468, - "seed": 793136700, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": { - "elementId": "LdRxYtp-vyX2QeDnxXSrS", - "focus": -0.008690951667154442, - "gap": 12.24040562982438 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 1.7479449669389282, - -62.93182039729468 - ] - ] - }, - { - "type": "arrow", - "version": 319, - "versionNonce": 1245738672, - "isDeleted": false, - "id": "mazLiIjyU9ysfCDKZcVgX", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3431.4496167871725, - "y": 1740.7518406920462, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 1337.3004665796852, - "height": 129.35976412566478, - "seed": 1934121892, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "focus": -0.12967246181941028, - "gap": 11.505130366976317, - "elementId": "LdRxYtp-vyX2QeDnxXSrS" - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -1337.3004665796852, - -129.35976412566478 - ] - ] - }, - { - "type": "arrow", - "version": 678, - "versionNonce": 1361808464, - "isDeleted": false, - "id": "Aujbd37Vz68tzdcf0qX0K", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3968.117855081848, - "y": 2235.916248720727, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 6519.118763895883, - "height": 840.6017236318519, - "seed": 1048375100, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "fLK6ZVR8UYtruAYgkEaKw", - "focus": 0.03291444940185092, - "gap": 22.41123918341509 - }, - "endBinding": { - "elementId": "949o_Sk--Ld6wo2FKS2fL", - "focus": 0.3424424357386611, - "gap": 18.21441840216653 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 5.244235010298666, - -828.6017850896292 - ], - [ - -6513.874528885584, - -840.6017236318519 - ], - [ - -6506.555695853329, - -109.5084639254269 - ] - ] - }, - { - "type": "arrow", - "version": 536, - "versionNonce": 1381900464, - "isDeleted": false, - "id": "7p9T0a-IxPmDdjBqC-0im", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -2431.0494403703915, - "y": 2212.9390628484234, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 65.55393790244402, - "height": 2.6221175051493337, - "seed": 1813227452, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "949o_Sk--Ld6wo2FKS2fL", - "focus": 0.03105274966501898, - "gap": 18.529917114855152 - }, - "endBinding": { - "elementId": "oojvRHlnGhZu1D9CQPzga", - "focus": -0.12230991534358376, - "gap": 24.077694044019935 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 65.55393790244402, - 2.6221175051493337 - ] - ] - }, - { - "type": "arrow", - "version": 513, - "versionNonce": 580223568, - "isDeleted": false, - "id": "ORyDG0-MmRvrIZpmyRPlC", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -2087.6406571569705, - "y": 2212.6715715579994, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 50.944797461869896, - "height": 1.8962903214384983, - "seed": 390025276, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "oojvRHlnGhZu1D9CQPzga", - "focus": 0.0884902586256845, - "gap": 14.264957239387115 - }, - "endBinding": { - "elementId": "QljoeT8sSPtX_l3e_a3My", - "focus": -0.002929144863462674, - "gap": 21.3458829019188 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 50.944797461869896, - -1.8962903214384983 - ] - ] - }, - { - "type": "arrow", - "version": 496, - "versionNonce": 490113712, - "isDeleted": false, - "id": "ZTgJCi82oC_3iuspj4sVs", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1734.0238949610475, - "y": 2211.1732186979134, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 44.95127170453295, - "height": 0.39793746135318603, - "seed": 534898876, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "QljoeT8sSPtX_l3e_a3My", - "focus": 0.12520801716349567, - "gap": 12.81708816620619 - }, - "endBinding": { - "elementId": "ZVlRo5ABA2pBBSuDikef_", - "focus": -0.015339576009995734, - "gap": 12.052632752984309 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 44.95127170453295, - -0.39793746135318603 - ] - ] - }, - { - "type": "arrow", - "version": 474, - "versionNonce": 1599125584, - "isDeleted": false, - "id": "glCxPNKW4x-VKTsz6vtbT", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1554.218808142915, - "y": 2346.0270338115133, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 966.4523988059578, - "height": 182.80190685529828, - "seed": 1406396860, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "ZVlRo5ABA2pBBSuDikef_", - "focus": -0.07708710629889753, - "gap": 22.632373153893866 - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -5.993525757336797, - 131.85710939342837 - ], - [ - -958.9605201885356, - 127.3619364961769 - ], - [ - -966.4523988059578, - -50.944797461869896 - ] - ] - }, - { - "type": "arrow", - "version": 654, - "versionNonce": 91337904, - "isDeleted": false, - "id": "XOgSlXTT9UnidMDOPb8Og", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1214.3809191534424, - "y": 2330.8770033653723, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 321.8378584718955, - "height": 158.4926510258515, - "seed": 1638355516, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "5EIpXEGwnVHQ2AJv0uPo3", - "focus": 0.013419094194996711, - "gap": 11.296368033082274 - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -3.169157500093206, - 158.4926510258515 - ], - [ - -321.8378584718955, - 152.50550795844356 - ] - ] - }, - { - "type": "arrow", - "version": 362, - "versionNonce": 188034640, - "isDeleted": false, - "id": "jYKNwDNwCT9HqtyU1cb6j", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -189.5943008389845, - "y": 2213.1732644742806, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 200.78225549331788, - "height": 2.996705720170625, - "seed": 1198322492, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "hCliSU94s9BGXBwt9V9Cz", - "focus": 0.13652783341717944, - "gap": 6.973477716774795 - }, - "endBinding": { - "elementId": "NbYnTiIlKnGn-vT1BgUXG", - "focus": -0.09890601847601484, - "gap": 21.94516117160569 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 200.78225549331788, - -2.996705720170625 - ] - ] - }, - { - "type": "arrow", - "version": 355, - "versionNonce": 1527571024, - "isDeleted": false, - "id": "xIDgdoUwo4XtG0resbUs1", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": -1764.6144487827762, - "y": 3511.2971303887534, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "width": 489.2747061718467, - "height": 1227.3857010239385, - "seed": 322643716, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "gtybl0KFWshqe39VLmwt0" - } - ], - "updated": 1714744133371, - "link": null, - "locked": false, - "startBinding": { - "elementId": "uYwEC6UHGHtBawFQ1yRXK", - "gap": 7.198465839105609, - "focus": -0.22902796241089074 - }, - "endBinding": { - "elementId": "5EIpXEGwnVHQ2AJv0uPo3", - "gap": 18.585891880479323, - "focus": 0.30026755265226734 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - 489.2747061718467, - -1227.3857010239385 - ] - ] - }, - { - "type": "text", - "version": 6, - "versionNonce": 1608092752, - "isDeleted": false, - "id": "gtybl0KFWshqe39VLmwt0", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1546.923087212966, - "y": 2875.104279876784, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 53.89198303222656, - "height": 45, - "seed": 508037200, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744119006, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "170", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "xIDgdoUwo4XtG0resbUs1", - "originalText": "170", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "diamond", - "version": 632, - "versionNonce": 1864033360, - "isDeleted": false, - "id": "5EIpXEGwnVHQ2AJv0uPo3", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1327.8488544719605, - "y": 2099.7016238671436, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 234.94540946863557, - "height": 219.45825162823473, - "seed": 1739695364, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "_S8sOHWq8blm1h1sEx5FL" - }, - { - "id": "GkT7kVIBQeOtiyApJWoB5", - "type": "arrow" - }, - { - "id": "XOgSlXTT9UnidMDOPb8Og", - "type": "arrow" - }, - { - "id": "loqm_U-HBXuGevymOPovB", - "type": "arrow" - }, - { - "id": "xIDgdoUwo4XtG0resbUs1", - "type": "arrow" - } - ], - "updated": 1714744053945, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 643, - "versionNonce": 1336953008, - "isDeleted": false, - "id": "_S8sOHWq8blm1h1sEx5FL", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1242.1124868460126, - "y": 2174.5661867742024, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 62.999969482421875, - "height": 70, - "seed": 1472095364, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "fontSize": 28, - "fontFamily": 1, - "text": "CMD\n(170)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "5EIpXEGwnVHQ2AJv0uPo3", - "originalText": "CMD\n(170)", - "lineHeight": 1.25, - "baseline": 64 - }, - { - "type": "arrow", - "version": 612, - "versionNonce": 2027543120, - "isDeleted": false, - "id": "GkT7kVIBQeOtiyApJWoB5", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1426.2952407614466, - "y": 2218.0916328676894, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 81.63091741904873, - "height": 0.31509507133432635, - "seed": 673838852, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "ZVlRo5ABA2pBBSuDikef_", - "focus": 0.09756821397048676, - "gap": 16.685638806401826 - }, - "endBinding": { - "elementId": "5EIpXEGwnVHQ2AJv0uPo3", - "focus": -0.08652515996484812, - "gap": 19.061169403173878 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 81.63091741904873, - 0.31509507133432635 - ] - ] - }, - { - "type": "ellipse", - "version": 274, - "versionNonce": 2114171568, - "isDeleted": false, - "id": "tJ7jGw7ATFSp-lsgemQ-N", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -996.0676706647207, - "y": 2141.7387252141434, - "strokeColor": "#2f9e44", - "backgroundColor": "#a5d8ff", - "width": 160.38420152212487, - "height": 162.24351160477877, - "seed": 138158980, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "q1oh912rgTL2L9sgr4Mf1" - }, - { - "id": "loqm_U-HBXuGevymOPovB", - "type": "arrow" - }, - { - "id": "z8rBoA7QcvEpK40GcZt3l", - "type": "arrow" - }, - { - "id": "9kzbQWa7MJj_5DR5ThIqF", - "type": "arrow" - } - ], - "updated": 1714744053945, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 362, - "versionNonce": 457838672, - "isDeleted": false, - "id": "q1oh912rgTL2L9sgr4Mf1", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -958.479987822252, - "y": 2177.107648277993, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 84.80007934570312, - "height": 91.78217821782177, - "seed": 796201732, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "fontSize": 36.712871287128706, - "fontFamily": 1, - "text": "wake\n128", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "tJ7jGw7ATFSp-lsgemQ-N", - "originalText": "wake\n128", - "lineHeight": 1.25, - "baseline": 83 - }, - { - "type": "arrow", - "version": 689, - "versionNonce": 576314544, - "isDeleted": false, - "id": "loqm_U-HBXuGevymOPovB", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1101.1914558592762, - "y": 2222.5701808265217, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 81.63091741904873, - "height": 0.31509507133432635, - "seed": 1469898372, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "5EIpXEGwnVHQ2AJv0uPo3", - "focus": 0.11590339842350911, - "gap": 3.944580631035052 - }, - "endBinding": { - "elementId": "tJ7jGw7ATFSp-lsgemQ-N", - "focus": -0.005239228598300345, - "gap": 23.492870687665373 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 81.63091741904873, - 0.31509507133432635 - ] - ] - }, - { - "type": "arrow", - "version": 749, - "versionNonce": 776177232, - "isDeleted": false, - "id": "z8rBoA7QcvEpK40GcZt3l", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -837.1915168944327, - "y": 2182.769452133614, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 81.63091741904873, - "height": 0.31509507133432635, - "seed": 1080590084, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "focus": -0.49794847999276115, - "gap": 7.927550336249354, - "elementId": "tJ7jGw7ATFSp-lsgemQ-N" - }, - "endBinding": { - "focus": 0.34018933388207834, - "gap": 15.640594201247026, - "elementId": "4HG3lkfSICKQoMrMvMSOL" - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 81.63091741904873, - 0.31509507133432635 - ] - ] - }, - { - "type": "arrow", - "version": 239, - "versionNonce": 1821841072, - "isDeleted": false, - "id": "9kzbQWa7MJj_5DR5ThIqF", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": -937.4520888181194, - "y": 2324.1819359493093, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 552.7872994620591, - "height": 1145.3787284288592, - "seed": 1197231108, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "5goX2SHWrF5szxWrULsTJ" - } - ], - "updated": 1714744131315, - "link": null, - "locked": false, - "startBinding": { - "elementId": "tJ7jGw7ATFSp-lsgemQ-N", - "focus": -0.3061850989969431, - "gap": 22.51240484911591 - }, - "endBinding": { - "elementId": "nTdCg9YES9bcUbYEdQyta", - "focus": -0.5568391113905278, - "gap": 8.886552655038898 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - -552.7872994620591, - 1145.3787284288592 - ] - ] - }, - { - "type": "text", - "version": 4, - "versionNonce": 354861136, - "isDeleted": false, - "id": "5goX2SHWrF5szxWrULsTJ", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1245.3097278069613, - "y": 2874.371300163739, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 62.927978515625, - "height": 45, - "seed": 1307499184, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744129957, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "128", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "9kzbQWa7MJj_5DR5ThIqF", - "originalText": "128", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 354, - "versionNonce": 125855408, - "isDeleted": false, - "id": "t2mJ36gZcTfYuLR0nvMGj", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": -1164.5690481075685, - "y": 3492.775508481896, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "width": 455.9462874412552, - "height": 1178.1947013396725, - "seed": 473248038, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "XiVZdOHhCQ3Yu2DPnpc4P" - } - ], - "updated": 1714744269767, - "link": null, - "locked": false, - "startBinding": { - "elementId": "IyEMitLQCn9ktYumohw1w", - "gap": 20.604103413635933, - "focus": -0.02650605165516595 - }, - "endBinding": { - "elementId": "4HG3lkfSICKQoMrMvMSOL", - "gap": 24.57298092007626, - "focus": 0.14283553907316254 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - 455.9462874412552, - -1178.1947013396725 - ] - ] - }, - { - "type": "text", - "version": 7, - "versionNonce": 1550217296, - "isDeleted": false, - "id": "XiVZdOHhCQ3Yu2DPnpc4P", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -990.4698820480737, - "y": 2881.17815781206, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 107.74795532226562, - "height": 45, - "seed": 1982803120, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744269144, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "160-21", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "t2mJ36gZcTfYuLR0nvMGj", - "originalText": "160-21", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 808, - "versionNonce": 514748592, - "isDeleted": false, - "id": "ek9xtHVfDe-Hf4jNvTJNN", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -560.1913948241202, - "y": 2166.7695436863482, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 81.63091741904873, - "height": 0.31509507133432635, - "seed": 1832311428, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "4HG3lkfSICKQoMrMvMSOL", - "focus": -0.49574628264975046, - "gap": 24.389481639407776 - }, - "endBinding": { - "elementId": "hCliSU94s9BGXBwt9V9Cz", - "focus": 0.6448940097529402, - "gap": 13.48370518338399 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 81.63091741904873, - 0.31509507133432635 - ] - ] - }, - { - "type": "arrow", - "version": 317, - "versionNonce": 1088876112, - "isDeleted": false, - "id": "0ov9dJFlVufiTGN_Batn_", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 473.3990782659803, - "y": 2372.4307801988393, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 1326.5799781303176, - "height": 1179.563456471902, - "seed": 1631563558, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "T80QU0AXIqb-YK4ex70mT" - } - ], - "updated": 1714744366477, - "link": null, - "locked": false, - "startBinding": { - "elementId": "R0nMXwKFeadrft0ZosQ-f", - "gap": 19.678294237402497, - "focus": 0.011726209379353184 - }, - "endBinding": { - "elementId": "uobFuAsT0NWnRMntfj5nA", - "gap": 21.65509953072869, - "focus": 0.17074031393058853 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - -1326.5799781303176, - 1179.563456471902 - ] - ] - }, - { - "type": "text", - "version": 4, - "versionNonce": 1712376496, - "isDeleted": false, - "id": "T80QU0AXIqb-YK4ex70mT", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -211.1669041463465, - "y": 2939.7125084347904, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 42.55198669433594, - "height": 45, - "seed": 392620624, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744364801, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "161", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "0ov9dJFlVufiTGN_Batn_", - "originalText": "161", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 563, - "versionNonce": 404904016, - "isDeleted": false, - "id": "IjtzlmGeUZNzHhtL5Sx4-", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -1755.218199559816, - "y": 3739.806878950265, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 193.7277174788685, - "height": 2.793981468968923, - "seed": 1401878607, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744201193, - "link": null, - "locked": false, - "startBinding": null, - "endBinding": { - "elementId": "hxutjfIhgXhW_6SJNpt1Z", - "gap": 14.791649712458025, - "focus": 0.06393838495605775 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -193.7277174788685, - 2.793981468968923 - ] - ] - }, - { - "type": "rectangle", - "version": 548, - "versionNonce": 1944398928, - "isDeleted": false, - "id": "hxutjfIhgXhW_6SJNpt1Z", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -2210.9596321469216, - "y": 3621.0661105201343, - "strokeColor": "#e03131", - "backgroundColor": "#ffc9c9", - "width": 247.2220653957791, - "height": 231.99996948242196, - "seed": 1388910191, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "id": "IjtzlmGeUZNzHhtL5Sx4-", - "type": "arrow" - }, - { - "type": "text", - "id": "pmGje4fSxRgG95Nu-6IzQ" - } - ], - "updated": 1714744201192, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 599, - "versionNonce": 1442918992, - "isDeleted": false, - "id": "pmGje4fSxRgG95Nu-6IzQ", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": -2188.958561912411, - "y": 3647.0660952613453, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 203.2199249267578, - "height": 180, - "seed": 1029337231, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744213336, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "no response\n-21\nlost syn\n-22", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "hxutjfIhgXhW_6SJNpt1Z", - "originalText": "no response\n-21\nlost syn\n-22", - "lineHeight": 1.25, - "baseline": 173 - }, - { - "type": "rectangle", - "version": 287, - "versionNonce": 191225424, - "isDeleted": false, - "id": "_xAS16LWhNy5Gw6c3Abgu", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1310.0891352586232, - "y": 1973.3864053239558, - "strokeColor": "#e03131", - "backgroundColor": "#ffc9c9", - "width": 157.88906521267364, - "height": 190, - "seed": 828001200, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "LhTNzIQ3McbWToRZS31Wk" - }, - { - "id": "w48z_r3CFcohLuEU1P684", - "type": "arrow" - } - ], - "updated": 1714744053945, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 279, - "versionNonce": 757722800, - "isDeleted": false, - "id": "LhTNzIQ3McbWToRZS31Wk", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1339.3356774474796, - "y": 2000.8864053239558, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 99.39598083496094, - "height": 135, - "seed": 1098208688, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "Lost \nSync\n-2", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "_xAS16LWhNy5Gw6c3Abgu", - "originalText": "Lost Sync\n-2", - "lineHeight": 1.25, - "baseline": 128 - }, - { - "type": "rectangle", - "version": 355, - "versionNonce": 95786064, - "isDeleted": false, - "id": "r8QgYWHtt1ERGS9dITEB-", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1272.5891352586236, - "y": 1775.8863290300105, - "strokeColor": "#e03131", - "backgroundColor": "#ffc9c9", - "width": 256.22219509548574, - "height": 189.16664123535156, - "seed": 322910640, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "DTpFAXniLPN2v6ENsjj-p" - }, - { - "id": "ksZH5o4yNx8HLY0yxGFbD", - "type": "arrow" - } - ], - "updated": 1714744053945, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 398, - "versionNonce": 156801200, - "isDeleted": false, - "id": "DTpFAXniLPN2v6ENsjj-p", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1283.052260699433, - "y": 1802.9696496476863, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 235.2959442138672, - "height": 135, - "seed": 881623984, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "Too many re-\ntry\n-4/-9", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "r8QgYWHtt1ERGS9dITEB-", - "originalText": "Too many re-try\n-4/-9", - "lineHeight": 1.25, - "baseline": 128 - }, - { - "type": "arrow", - "version": 403, - "versionNonce": 1218349648, - "isDeleted": false, - "id": "L50BGCy0awqTIQHghyt9z", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 2, - "opacity": 100, - "angle": 0, - "x": 1472.29062882681, - "y": 2534.0973451402456, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 607.666625976562, - "height": 1110.3332519531245, - "seed": 1534128676, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "lMCaitAoU9MIWzybhfH--" - } - ], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "focus": -0.14120177941968304, - "gap": 5.736758801065733, - "elementId": "gKny8cgFf1eK8KNEfsu1X" - }, - "endBinding": { - "focus": -0.4894460396284771, - "gap": 7.328161982185463, - "elementId": "jj_yHGzqEfb3EVrV7IAkE" - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - -607.666625976562, - 1110.3332519531245 - ] - ] - }, - { - "type": "text", - "version": 17, - "versionNonce": 2116158128, - "isDeleted": false, - "id": "lMCaitAoU9MIWzybhfH--", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1145.885324871732, - "y": 3066.7639711168076, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 45.14398193359375, - "height": 45, - "seed": 1650371559, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "211", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "L50BGCy0awqTIQHghyt9z", - "originalText": "211", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 818, - "versionNonce": 1861795920, - "isDeleted": false, - "id": "SPFSlS5fEkc_qQBkM9eLq", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 2334.3758187878966, - "y": 2535.584703759284, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 702.2942235865171, - "height": 1100.4878762984686, - "seed": 688223260, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "-h8D-LdmAqoy7Z6XE0ewo" - } - ], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "elementId": "3ZsaM9HAI9xyZdhjU5G6O", - "focus": -0.3040666851117252, - "gap": 12.657522916518118 - }, - "endBinding": { - "elementId": "RJxANj4RhJ_aciZMjF5IY", - "focus": -0.6129293531506963, - "gap": 24.601348832702257 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - -702.2942235865171, - 1100.4878762984686 - ] - ] - }, - { - "type": "text", - "version": 20, - "versionNonce": 707669168, - "isDeleted": false, - "id": "-h8D-LdmAqoy7Z6XE0ewo", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2017.9474306863476, - "y": 3090.0973044501416, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 61.01997375488281, - "height": 45, - "seed": 16289948, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "212", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "SPFSlS5fEkc_qQBkM9eLq", - "originalText": "212", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 537, - "versionNonce": 2023503440, - "isDeleted": false, - "id": "U2EvPXeHbkdhv1IajJrxn", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 2577.2203239280425, - "y": 3793.0965001415034, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "width": 9.821008887962762, - "height": 1378.4508223351522, - "seed": 504576292, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "cS0N1k5dk3sOmQa2I3DTC" - } - ], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "focus": 0.22693106397956136, - "gap": 14.687558147681067, - "elementId": "Mdil-sv7Ec0Ncq-XpaH48" - }, - "endBinding": { - "focus": -0.06954320217056414, - "gap": 7.030070750862535, - "elementId": "k4wYSdGSsCo2g1t0Te4BW" - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - 9.821008887962762, - -1378.4508223351522 - ] - ] - }, - { - "type": "text", - "version": 17, - "versionNonce": 388306608, - "isDeleted": false, - "id": "cS0N1k5dk3sOmQa2I3DTC", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2544.5468439970236, - "y": 3081.3710889739273, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 75.16796875, - "height": 45, - "seed": 1360557220, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053945, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "200", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "U2EvPXeHbkdhv1IajJrxn", - "originalText": "200", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 506, - "versionNonce": 710995024, - "isDeleted": false, - "id": "O4MRTCghp9QBztgDBgQdC", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 1360.5536572613762, - "y": 3775.318688455305, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "width": 379.82100888796276, - "height": 1372.8951481001259, - "seed": 1621826028, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "V-QvlDWfBZWDgmbPNv7tC" - } - ], - "updated": 1714744053945, - "link": null, - "locked": false, - "startBinding": { - "focus": 0.15784142807548998, - "gap": 22.74902368404603, - "elementId": "4uG_zS5TMGWF1M74StXiw" - }, - "endBinding": null, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - 379.82100888796276, - -1372.8951481001259 - ] - ] - }, - { - "type": "text", - "version": 22, - "versionNonce": 1504624816, - "isDeleted": false, - "id": "V-QvlDWfBZWDgmbPNv7tC", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1495.3861740344591, - "y": 3064.7044477385757, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 60.155975341796875, - "height": 45, - "seed": 914947180, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053946, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "210", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "O4MRTCghp9QBztgDBgQdC", - "originalText": "210", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "ellipse", - "version": 291, - "versionNonce": 1847517776, - "isDeleted": false, - "id": "drdcmV3L0ddqrWjMto2HA", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2140.646627596518, - "y": 2384.9118050376096, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 139.3841404869686, - "height": 139.3841404869686, - "seed": 1147122921, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "skRaGtOsOJag3_-_vAeWM" - } - ], - "updated": 1714744053946, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 348, - "versionNonce": 829631152, - "isDeleted": false, - "id": "skRaGtOsOJag3_-_vAeWM", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2178.9769845316673, - "y": 2422.047170865775, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 63.16395568847656, - "height": 65.553937902444, - "seed": 1850421193, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053946, - "link": null, - "locked": false, - "fontSize": 26.221575160977604, - "fontFamily": 1, - "text": "buf\nclear", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "drdcmV3L0ddqrWjMto2HA", - "originalText": "buf\nclear", - "lineHeight": 1.25, - "baseline": 59 - }, - { - "type": "arrow", - "version": 812, - "versionNonce": 850270288, - "isDeleted": false, - "id": "bLoyDI7x4qG_GKX6CeMT3", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 3050.3758187878966, - "y": 2567.584703759284, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 278.29422358651755, - "height": 1123.1546243453436, - "seed": 855051044, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053946, - "link": null, - "locked": false, - "startBinding": { - "focus": -0.18472349081490505, - "gap": 19.486204168028564, - "elementId": "OkLwDHyZTul2qE2aBhHEy" - }, - "endBinding": { - "focus": -0.3540243231205051, - "gap": 7.917677151524252, - "elementId": "NYb28wO65_lvUaT1Cockk" - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - -278.29422358651755, - 1123.1546243453436 - ] - ] - }, - { - "type": "arrow", - "version": 867, - "versionNonce": 689591472, - "isDeleted": false, - "id": "ObbXkkUi4RX8Luqzn8JMg", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 3230.3758187878966, - "y": 2579.584703759284, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 37.705776413482454, - "height": 1089.8211282515936, - "seed": 2041902628, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053946, - "link": null, - "locked": false, - "startBinding": { - "elementId": "ZIqLdTRRThlw8lHcE4onP", - "focus": 0.03856242982066085, - "gap": 19.285846282459815 - }, - "endBinding": { - "elementId": "iBZRtI2xGc5MgU0R2emqB", - "focus": -0.25188128440851404, - "gap": 20.588958897294617 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - 37.705776413482454, - 1089.8211282515936 - ] - ] - }, - { - "type": "arrow", - "version": 647, - "versionNonce": 1273400912, - "isDeleted": false, - "id": "-3xsQ8CUpzKqR3rQuEvHY", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 3869.220323928042, - "y": 3689.0965001415034, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "width": 314.17899111203724, - "height": 1250.4508223351522, - "seed": 1943232309, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "2IXeqGDbLQ3GdBBwzZ2ga" - } - ], - "updated": 1714744053946, - "link": null, - "locked": false, - "startBinding": { - "focus": 0.19743598335435658, - "gap": 13.064102674735437, - "elementId": "I1nmKrm-fWZatTXwyA29D" - }, - "endBinding": { - "focus": 0.34544705344176796, - "gap": 16.91334043405756, - "elementId": "8rA9xpm9xmRmEM21VM0lU" - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - -314.17899111203724, - -1250.4508223351522 - ] - ] - }, - { - "type": "text", - "version": 22, - "versionNonce": 910966448, - "isDeleted": false, - "id": "2IXeqGDbLQ3GdBBwzZ2ga", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3681.098838320754, - "y": 3041.3710889739273, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 62.06398010253906, - "height": 45, - "seed": 1089187989, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053946, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "180", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "-3xsQ8CUpzKqR3rQuEvHY", - "originalText": "180", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "rectangle", - "version": 311, - "versionNonce": 1386234960, - "isDeleted": false, - "id": "idBlXd68348ilwf1GeNIr", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3728.338880945471, - "y": 1929.5667387793594, - "strokeColor": "#e03131", - "backgroundColor": "#ffc9c9", - "width": 157.88906521267364, - "height": 190, - "seed": 2000803100, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "YJUsRewdxJ1m5V334YrRQ" - } - ], - "updated": 1714744053946, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 319, - "versionNonce": 1504766128, - "isDeleted": false, - "id": "YJUsRewdxJ1m5V334YrRQ", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3739.1534391865735, - "y": 1979.5667387793594, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 136.25994873046875, - "height": 90, - "seed": 1795215772, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053946, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "Checksu\nm -10", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "idBlXd68348ilwf1GeNIr", - "originalText": "Checksum -10", - "lineHeight": 1.25, - "baseline": 83 - }, - { - "type": "rectangle", - "version": 265, - "versionNonce": 78854736, - "isDeleted": false, - "id": "Pw8tqcnRVRBvkr7aqbhd8", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1985.860989235402, - "y": 3990.173510581504, - "strokeColor": "#e03131", - "backgroundColor": "#ffc9c9", - "width": 211.22219509548637, - "height": 280, - "seed": 782587884, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "uFsm7wNyZQ8nqtC5IPOp_" - }, - { - "id": "VwKQnf_zVtP_2qxBcKzUw", - "type": "arrow" - } - ], - "updated": 1714744053946, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 316, - "versionNonce": 1055937200, - "isDeleted": false, - "id": "uFsm7wNyZQ8nqtC5IPOp_", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2006.4941204745514, - "y": 4062.673510581504, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 169.9559326171875, - "height": 135, - "seed": 109804140, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053946, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "Memory \nallocation\n-6", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Pw8tqcnRVRBvkr7aqbhd8", - "originalText": "Memory allocation\n-6", - "lineHeight": 1.25, - "baseline": 128 - }, - { - "type": "arrow", - "version": 118, - "versionNonce": 620195920, - "isDeleted": false, - "id": "VwKQnf_zVtP_2qxBcKzUw", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 2105.799847513522, - "y": 3852.3874131269513, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 5.154140784180072, - "height": 124.32776073037712, - "seed": 624914284, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [], - "updated": 1714744053946, - "link": null, - "locked": false, - "startBinding": { - "elementId": "dwSyGncFb0E5Cqk0pkmyg", - "gap": 13.50713308055471, - "focus": -0.12731120776534016 - }, - "endBinding": { - "elementId": "Pw8tqcnRVRBvkr7aqbhd8", - "gap": 13.458336724175524, - "focus": 0.2319588358641433 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - 5.154140784180072, - 124.32776073037712 - ] - ] - }, - { - "type": "arrow", - "version": 565, - "versionNonce": 1555201200, - "isDeleted": false, - "id": "fNlhld2BS4snfpZ0Sn2La", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 1833.0536572613762, - "y": 3452.818688455305, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "width": 10.178991112037465, - "height": 1582.8951481001259, - "seed": 418498516, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "aa-hRQKnGiw7dxWn3bQd8" - } - ], - "updated": 1714744053946, - "link": null, - "locked": false, - "startBinding": { - "elementId": "0XOIdMglnzJdbVbiI4UlR", - "focus": -0.3518684023567377, - "gap": 2.120682289950004 - }, - "endBinding": { - "elementId": "Dy1HFFDMmS5fyBx8k3h7k", - "focus": -0.08452651399850455, - "gap": 4.043157064792169 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - -10.178991112037465, - -1582.8951481001259 - ] - ] - }, - { - "type": "text", - "version": 27, - "versionNonce": 1531242064, - "isDeleted": false, - "id": "aa-hRQKnGiw7dxWn3bQd8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1789.5161789172716, - "y": 2638.871114405242, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 76.89596557617188, - "height": 45, - "seed": 688587561, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053946, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "222", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "fNlhld2BS4snfpZ0Sn2La", - "originalText": "222", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "rectangle", - "version": 364, - "versionNonce": 345990832, - "isDeleted": false, - "id": "yveyjSE9qA0izb8K6Q7fq", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 648.360989235402, - "y": 3897.673510581504, - "strokeColor": "#e03131", - "backgroundColor": "#ffc9c9", - "width": 86.2221950954861, - "height": 83.08332655164895, - "seed": 125543276, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "fnXvu4aURmwd_clUbqKTb" - } - ], - "updated": 1714744053946, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 331, - "versionNonce": 343120976, - "isDeleted": false, - "id": "fnXvu4aURmwd_clUbqKTb", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 673.112093802188, - "y": 3916.7151738573284, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 36.71998596191406, - "height": 45, - "seed": 361518572, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053946, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "-9", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "yveyjSE9qA0izb8K6Q7fq", - "originalText": "-9", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 948, - "versionNonce": 1398739120, - "isDeleted": false, - "id": "gyPcyFeW-J2407VPWQOUM", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "angle": 0, - "x": 3024.220323928042, - "y": 3631.5965001415034, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "width": 468.32100888796276, - "height": 1817.9508223351518, - "seed": 643667460, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "y1KCIJ3BwpKUJUiddHDVo" - } - ], - "updated": 1714744053946, - "link": null, - "locked": false, - "startBinding": { - "elementId": "xcVSmTB-gt0cN1YieTegp", - "focus": -0.21481161526658266, - "gap": 25.040926322905463 - }, - "endBinding": { - "elementId": "LdRxYtp-vyX2QeDnxXSrS", - "focus": 0.46953066807727273, - "gap": 26.877644598323087 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "triangle", - "points": [ - [ - 0, - 0 - ], - [ - 468.32100888796276, - -1817.9508223351518 - ] - ] - }, - { - "type": "text", - "version": 26, - "versionNonce": 1650168400, - "isDeleted": false, - "id": "y1KCIJ3BwpKUJUiddHDVo", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3219.9328455839377, - "y": 2700.1210889739277, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 76.89596557617188, - "height": 45, - "seed": 1362489830, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053946, - "link": null, - "locked": false, - "fontSize": 36, - "fontFamily": 1, - "text": "222", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "gyPcyFeW-J2407VPWQOUM", - "originalText": "222", - "lineHeight": 1.25, - "baseline": 38 - }, - { - "type": "arrow", - "version": 628, - "versionNonce": 815364784, - "isDeleted": false, - "id": "1pwwcSYOnzOIeKOG9nO85", - "fillStyle": "cross-hatch", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3016.1144922650083, - "y": 3893.561912657721, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "width": 2472.357177734374, - "height": 607.982177734375, - "seed": 1567446097, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714744053946, - "link": null, - "locked": false, - "startBinding": { - "elementId": "xcVSmTB-gt0cN1YieTegp", - "focus": -0.05915173079298335, - "gap": 24.43516247655255 - }, - "endBinding": { - "elementId": "ViWZZy9Q7c0UYyERaQ5M0", - "focus": -0.2156363294749259, - "gap": 26.26847258068483 - }, - "lastCommittedPoint": null, - "startArrowhead": null, - "endArrowhead": "arrow", - "points": [ - [ - 0, - 0 - ], - [ - -14.071568080356883, - 551.9821602957591 - ], - [ - -2472.357177734374, - 541.7678397042409 - ], - [ - -2468.5001046316956, - -56.00001743861594 - ] - ] - } - ], - "appState": { - "gridSize": null, - "viewBackgroundColor": "#ffffff" - }, - "files": {} -} \ No newline at end of file diff --git a/.vscode/examples/Untitled-1.ipynb b/.vscode/examples/Untitled-1.ipynb deleted file mode 100644 index 2c311a1..0000000 --- a/.vscode/examples/Untitled-1.ipynb +++ /dev/null @@ -1,925 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import serial\n", - "import serial.tools.list_ports\n", - "import numpy as np\n", - "import time\n", - "import matplotlib.pyplot as plt\n", - "from IPython.display import clear_output, display\n", - "import pandas as pd\n", - "plt.style.use('dark_background')" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found USB devices:\tUSB VID:PID=303A:1001 SER=34:85:18:8E:88:FC LOCATION=1-4.2:x.0\n", - "Pulser Likely at:\tCOM14\n", - "Found USB devices:\tUSB VID:PID=1A86:55D4 SER=554C028876 LOCATION=1-4.1:x.0\n", - "Ambit Likely at:\tCOM23\n" - ] - } - ], - "source": [ - "Ambit_Port = \"\"\n", - "Pulser_Port = \"\"\n", - "ports = serial.tools.list_ports.comports()\n", - "for port, desc, hwid in sorted(ports):\n", - " if hwid[:4] == \"USB \":\n", - " print(f'Found USB devices:\\t{hwid}')\n", - " devices = hwid[4:].split(\" \")\n", - " if len(devices) > 1 and len(devices[0]) > 9:\n", - " if devices[0][-9:] == \"1A86:55D4\":\n", - " print(f'Ambit Likely at:\\t{port}')\n", - " Ambit_Port = port\n", - " if devices[0][-9:] == \"303A:1001\":\n", - " print(f'Pulser Likely at:\\t{port}')\n", - " Pulser_Port = port\n", - "\n", - "if Ambit_Port == \"\": print(f'Ambit not Found')\n", - "\n", - "if Pulser_Port == \"\": print(f'Pulser not Found')" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "def _readline(ser: serial.Serial, timeout: int):\n", - " _t0 = time.time()\n", - " s = \"\"\n", - " while (time.time() - _t0 < timeout):\n", - " if ser.in_waiting > 0:\n", - " c = ser.read()\n", - " if (c > bytes([0])) and (c < bytes([128])):\n", - " if (c == b'\\n') or (c == b'\\r'):\n", - " if s == \"\":\n", - " pass\n", - " elif len(s) > 2:\n", - " return s\n", - " else:\n", - " s += c.decode()\n", - " \n", - " return s\n", - "\n", - "def _wait_for_line(ser: serial.Serial, target: str, input: str = \"\", timeout: float = 1.0, print_out = False):\n", - " if input != \"\":\n", - " ser.write((input + \"\\n\").encode())\n", - " t0 = time.time()\n", - " timeout_t = t0 + timeout\n", - " condition = False\n", - " while (condition == False):\n", - " _l = _readline(ser, 0.25)\n", - " if target in _l:\n", - " condition = True\n", - " if print_out:\n", - " if _l != \"\": print(_l)\n", - " if time.time() > timeout_t:\n", - " break\n", - "\n", - " return condition\n", - "\n", - " \n", - "def _parse_data_line(l:str)->dict:\n", - " ret = {}\n", - " if len(l) > 5:\n", - " if (l[:2] == \"T:\") and (\",\" in l) and (\":\" in l):\n", - " items = [n.split(':') for n in l.split(',') if \":\" in n]\n", - " for i in items:\n", - " k = i[0]\n", - " v = i[1]\n", - " if len(k) > 0:\n", - " if v.isdecimal():\n", - " ret.update({k: int(v)})\n", - " if (v.count(\".\") == 1) and v.replace(\".\", \"0\").isdecimal():\n", - " ret.update({k: float(v)})\n", - "\n", - " return ret \n", - "\n", - "\n", - "def Trigger(ser: serial.Serial) -> dict:\n", - " ser.write(b\"G\")\n", - " ser.flush()\n", - " _ret = _parse_data_line(_readline(ser, .5))\n", - " return _ret\n", - "\n", - "def Ambit_actinic(ser: serial.Serial, v:int)->int:\n", - " if (v > 255): return -1\n", - " ser.write(bytes([65, v]))\n", - " ser.flush()\n", - " return v\n", - " \n", - "\n", - "def DAC_set(ser: serial.Serial, ch:int, v:int):\n", - " ret = False\n", - " if (ch == 2) or (ch == 4):\n", - " if (v > -1) and (v < 4095):\n", - " ret = _wait_for_line(ser, target = \"Done\", input = f'DAC,{ch},{v},\\n', print_out=False)\n", - " if ret == False: \n", - " print(\"DAC_set error\", ch, v)\n", - " return -1\n", - " return v\n", - "\n", - "def DAC_ONOFF(ser: serial.Serial, ch:int, onoff:bool):\n", - " if onoff:\n", - " if (ch == 2) or (ch == 4):\n", - " _wait_for_line(ser, target = \"Done\", input = f'LED_ON,{ch},\\n', print_out=False)\n", - " else:\n", - " if (ch == 2) or (ch == 4):\n", - " _wait_for_line(ser, target = \"Done\", input = f'LED_OFF,{ch},\\n', print_out=False)\n", - " else:\n", - " _wait_for_line(ser, target = \"Done\", input = f'LED_OFF,0,\\n', print_out=False)\n", - "\n", - "# plot autoscale\n", - "def autoscale(data: list, axis:plt.Axes):\n", - " if len(data) == 0: return\n", - " if len(data) == 1: \n", - " axis.set_ylim([data[0]-1, data[0]+1])\n", - " return \n", - " l, h = axis.get_ylim()\n", - " x, d = np.min(data), np.max(data)\n", - " r = d - x\n", - "\n", - " _l, _h, a = l, h, False\n", - " if (x < l) or (l < x - r):\n", - " _l = x - 0.05 * r\n", - " a = True\n", - " if (d > h) or (h > l + r):\n", - " _h = d + 0.05 * r\n", - " a = True\n", - " if a and _l != _h:\n", - " axis.set_ylim([_l, _h])\n", - "\n", - "def print_protocol(protocol:list)->None:\n", - " _act_ambit, _act_blue, _act_red, act_blue, act_red = -1, -1, -1, 0, 0\n", - " _timer = 0\n", - " _blue_on_time, _red_on_time = 0, 0\n", - " for _l in protocol:\n", - " if _l[0] == 'M' and len(_l) == 4: # Run measurement \n", - " if (_act_ambit != _l[3]): # Set ambit actinic\n", - " if _l[3] > 4 and _l[3] < 255: \n", - " _act_ambit = _l[3]\n", - " else:\n", - " _act_ambit = 0\n", - " #print(f'{_timer:<12.3f}Set Ambit Actinic to {_act_ambit}')\n", - " if (_l[1] < 1): continue # no data\n", - " for _ in range(_l[1]):\n", - " _timer += _l[2]\n", - " print(f'{_timer:<12.3f}Meas {_l[1]}\\t Interval: {_l[2]:.3f} sec \\t Take {_l[1] * _l[2]:.2f} seconds\\tAct:{_act_ambit:>3d}\\tRed:{act_red:>3d}\\tBlue:{act_blue:>3d}\\t')\n", - "\n", - " if _l[0] == 'A' and len(_l) == 3: # Change Actinic\n", - " if _l[1] == \"B\":\n", - " if _l[2] > 0:\n", - " if _act_blue != _l[2]: \n", - " _act_blue = _l[2]\n", - " if (_act_blue > 200 and act_blue < 200): _blue_on_time = _timer\n", - " print(f'{_timer:<12.3f}Set Blue Actinic: {_act_blue}')\n", - "\n", - " print(f'{_timer:<12.3f}Blue Actinic ON')\n", - " act_blue = _act_blue\n", - " else: \n", - " print(f'{_timer:<12.3f}Blue Actinic OFF')\n", - " act_blue = 0\n", - "\n", - " \n", - " if _l[1] == \"R\":\n", - " if _l[2] > 0:\n", - " if _act_red != _l[2]: \n", - " _act_red= _l[2]\n", - " if (_act_red > 200): _red_on_time = _timer\n", - " print(f'{_timer:<12.3f}Set Red Actinic: {_act_red}')\n", - " print(f'{_timer:<12.3f}Red Actinic ON')\n", - " act_red = _act_red\n", - " else: \n", - " print(f'{_timer:<12.3f}Red Actinic OFF')\n", - " act_red = 0\n", - "\n", - " if _l[0] == 'W' and len(_l) == 2:\n", - " print(f'{_timer:<12.3f}Sleep for: {_l[1]} sec with \\tAct:{_act_ambit:>3d}\\tRed:{act_red:>3d}\\tBlue:{act_blue:>3d}\\t')\n", - " _timer += _l[1]\n", - " if _l[0] == 'P':\n", - " print(f'{_timer:<12.3f}Update plot')\n", - "\n", - "\n", - "\n", - "\n", - " if (act_blue > 200) and (_timer - _blue_on_time > 5): print(f'\\t\\t-------Blue Actinic @ {act_blue} for too long-------')\n", - " if (act_red > 200) and (_timer - _red_on_time > 5): print(f'\\t\\t-------Red Actinic @ {act_red} for too long-------')\n", - " return" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Protocol is a list of Lines\n", - "#### Each Line is a command to run, also a list [\"Keyword\", augments,,,,]\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.000 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "2.250 Meas 250\t Interval: 0.005 sec \t Take 1.25 seconds\tAct:254\tRed: 0\tBlue: 0\t\n", - "2.450 Meas 20\t Interval: 0.010 sec \t Take 0.20 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n" - ] - } - ], - "source": [ - "## \"M\": Measurement:\n", - "# [\"M\", Number of points, Interval in second, Ambit actinic 0-255]\n", - "# NOTE there are 3 Actinic light, one from Ambit and two from external controller (Blue)\n", - "# This command controls Actinic on Ambit, ideally for Saturation Flash. Intensity is ~ 3000uE @ 254. Depends on real working distance. \n", - "\n", - "Example_protocol = [\n", - " ['M', 10, 0.1, 0],\n", - " ['M', 250, 0.005, 254],\n", - " ['M', 20, 0.01, 0]\n", - " ]\n", - "print_protocol(Example_protocol)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.000 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "1.000 Set Red Actinic: 50\n", - "1.000 Red Actinic ON\n", - "21.000 Meas 20\t Interval: 1.000 sec \t Take 20.00 seconds\tAct: 0\tRed: 50\tBlue: 0\t\n", - "21.000 Set Blue Actinic: 50\n", - "21.000 Blue Actinic ON\n", - "41.000 Meas 20\t Interval: 1.000 sec \t Take 20.00 seconds\tAct:200\tRed: 50\tBlue: 50\t\n", - "41.000 Blue Actinic OFF\n", - "42.000 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 50\tBlue: 0\t\n", - "42.000 Red Actinic OFF\n", - "43.000 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n" - ] - } - ], - "source": [ - "## \"A\": External Actinic light:\n", - "# [\"A\", \"B\" or \"R\", Intensity (0 - 1000)]\n", - "# \"B\" = Blue LED\n", - "# \"R\" = Red LED\n", - "# The PAR is ~ Intensity x 10, you need to measure it in your setup\n", - "# !!!!NOTE: Due to hardware limitation, All LED should no be on for long time. \n", - "# For Red and Blue External, Suggested limitations are:\n", - "# Intensity | Duration (s)\n", - "# <50 | No limit\n", - "# 50 - 100 | <2min\n", - "# 100-200 | <10s\n", - "# 200-500 | < 2s\n", - "# 500+ | <0.5s\n", - "\n", - "\n", - "Example_protocol = [\n", - " ['M', 10, 0.1, 0],\n", - " ['A', \"R\", 50],\n", - " ['M', 20, 1, 0],\n", - " ['A', \"B\", 50],\n", - " ['M', 20, 1, 200],\n", - " ['A', \"B\", 0],\n", - " ['M', 10, 0.1, 0],\n", - " ['A', \"R\", 0],\n", - " ['M', 10, 0.1, 0]\n", - " ]\n", - "print_protocol(Example_protocol)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.000 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "1.000 Set Red Actinic: 50\n", - "1.000 Red Actinic ON\n", - "21.000 Meas 20\t Interval: 1.000 sec \t Take 20.00 seconds\tAct: 0\tRed: 50\tBlue: 0\t\n", - "21.000 Set Blue Actinic: 50\n", - "21.000 Blue Actinic ON\n", - "41.000 Meas 20\t Interval: 1.000 sec \t Take 20.00 seconds\tAct:200\tRed: 50\tBlue: 50\t\n", - "41.000 Update plot\n", - "41.000 Sleep for: 10 sec with \tAct:200\tRed: 50\tBlue: 50\t\n", - "51.000 Red Actinic OFF\n", - "52.000 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 50\t\n" - ] - } - ], - "source": [ - "## \"W\": Wait:\n", - "# [\"W\", Wait (seconds)] \n", - "## \"P\": Update ploat:\n", - "# [\"P\"] \n", - "# Update plot will take an unknown amount of time (100ms to 1s?), and protocol will pause during updating. Only update figure at non-timing-critical step. \n", - "\n", - "Example_protocol = [\n", - " ['M', 10, 0.1, 0],\n", - " ['A', \"R\", 50],\n", - " ['M', 20, 1, 0],\n", - " ['A', \"B\", 50],\n", - " ['M', 20, 1, 200],\n", - " ['P'],\n", - " ['W', 10],\n", - " ['A', \"R\", 0],\n", - " ['M', 10, 0.1, 0]\n", - " ]\n", - "print_protocol(Example_protocol)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# Here are some simple protocols for calibration and testing:\n", - "\n", - "ambit_actinic_test = [['M', 5, 1, 250]] # Calibrate the real PAR using ambit actinic: 250 = 2400uE\n", - "Blue_actinic_test = [['A', \"B\", 250], [\"W\", 5]] # Calibrate the real PAR using Blue LED: 250 = 2800uE\n", - "Red_actinic_test = [['A', \"R\", 250], [\"W\", 2]] # Calibrate the real PAR using Red LED: 250 = 3200uE\n", - "signal_offset_protocol = [['M', 50, .1, 0]] # Get_offset. Put no chlorophyll, measure the baseline, and substract if from latter measurements\n" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "10.000 Meas 100\t Interval: 0.100 sec \t Take 10.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "11.000 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "12.250 Meas 250\t Interval: 0.005 sec \t Take 1.25 seconds\tAct:254\tRed: 0\tBlue: 0\t\n", - "12.450 Meas 20\t Interval: 0.010 sec \t Take 0.20 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "14.450 Meas 20\t Interval: 0.100 sec \t Take 2.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "24.450 Meas 100\t Interval: 0.100 sec \t Take 10.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n" - ] - } - ], - "source": [ - "## protocol blocks\n", - "# It might be easier to write protocols with small blocks\n", - "# Each block can be tested seperatedly and merge together\n", - "\n", - "# e.g. Here's a saturation flash block\n", - "Sat_Flash = [ \n", - " ['M', 10, 0.1, 0],\n", - " ['M', 250, 0.005, 254],\n", - " ['M', 20, 0.01, 0],\n", - " ['M', 20, 0.1, 0]\n", - " ]\n", - "\n", - "# To merge it in a large protocol, use Python list append:\n", - "Protocol_Eg1 = [\n", - " ['M', 100, .1, 0],\n", - " *Sat_Flash,\n", - " ['M', 100, .1, 0]\n", - " ]\n", - "\n", - "print_protocol(Protocol_Eg1)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "60.000 Meas 60\t Interval: 1.000 sec \t Take 60.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "61.000 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "62.250 Meas 250\t Interval: 0.005 sec \t Take 1.25 seconds\tAct:254\tRed: 0\tBlue: 0\t\n", - "62.450 Meas 20\t Interval: 0.010 sec \t Take 0.20 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "64.450 Meas 20\t Interval: 0.100 sec \t Take 2.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "65.450 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "65.450 Set Blue Actinic: 10\n", - "65.450 Blue Actinic ON\n", - "69.450 Meas 20\t Interval: 0.200 sec \t Take 4.00 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "69.450 Update plot\n", - "129.450 Meas 60\t Interval: 1.000 sec \t Take 60.00 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "130.450 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "131.700 Meas 250\t Interval: 0.005 sec \t Take 1.25 seconds\tAct:254\tRed: 0\tBlue: 10\t\n", - "131.900 Meas 20\t Interval: 0.010 sec \t Take 0.20 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "133.900 Meas 20\t Interval: 0.100 sec \t Take 2.00 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "135.900 Meas 10\t Interval: 0.200 sec \t Take 2.00 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "135.900 Update plot\n", - "136.900 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "138.150 Meas 250\t Interval: 0.005 sec \t Take 1.25 seconds\tAct:254\tRed: 0\tBlue: 10\t\n", - "138.350 Meas 20\t Interval: 0.010 sec \t Take 0.20 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "140.350 Meas 20\t Interval: 0.100 sec \t Take 2.00 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "142.350 Meas 10\t Interval: 0.200 sec \t Take 2.00 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "142.350 Update plot\n", - "143.350 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "144.600 Meas 250\t Interval: 0.005 sec \t Take 1.25 seconds\tAct:254\tRed: 0\tBlue: 10\t\n", - "144.800 Meas 20\t Interval: 0.010 sec \t Take 0.20 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "146.800 Meas 20\t Interval: 0.100 sec \t Take 2.00 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "148.800 Meas 10\t Interval: 0.200 sec \t Take 2.00 seconds\tAct: 0\tRed: 0\tBlue: 10\t\n", - "148.800 Update plot\n", - "148.800 Blue Actinic OFF\n", - "158.800 Meas 50\t Interval: 0.200 sec \t Take 10.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "159.800 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "161.050 Meas 250\t Interval: 0.005 sec \t Take 1.25 seconds\tAct:254\tRed: 0\tBlue: 0\t\n", - "161.250 Meas 20\t Interval: 0.010 sec \t Take 0.20 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "163.250 Meas 20\t Interval: 0.100 sec \t Take 2.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "173.250 Meas 50\t Interval: 0.200 sec \t Take 10.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "173.250 Update plot\n", - "174.250 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "175.500 Meas 250\t Interval: 0.005 sec \t Take 1.25 seconds\tAct:254\tRed: 0\tBlue: 0\t\n", - "175.700 Meas 20\t Interval: 0.010 sec \t Take 0.20 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "177.700 Meas 20\t Interval: 0.100 sec \t Take 2.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "202.700 Meas 50\t Interval: 0.500 sec \t Take 25.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "202.700 Update plot\n", - "203.700 Meas 10\t Interval: 0.100 sec \t Take 1.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "204.950 Meas 250\t Interval: 0.005 sec \t Take 1.25 seconds\tAct:254\tRed: 0\tBlue: 0\t\n", - "205.150 Meas 20\t Interval: 0.010 sec \t Take 0.20 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "207.150 Meas 20\t Interval: 0.100 sec \t Take 2.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "257.150 Meas 50\t Interval: 1.000 sec \t Take 50.00 seconds\tAct: 0\tRed: 0\tBlue: 0\t\n", - "257.150 Update plot\n" - ] - } - ], - "source": [ - "Sat_Flash = [ \n", - " ['M', 10, 0.1, 0],\n", - " ['M', 250, 0.005, 254],\n", - " ['M', 20, 0.01, 0],\n", - " ['M', 20, 0.1, 0]\n", - " ]\n", - "\n", - "Protocol_1 = [\n", - " ['M', 60, 1, 0],\n", - " *Sat_Flash,\n", - " ['M', 10, 0.1, 0],\n", - " ['A', \"B\", 10],\n", - " ['M', 20, 0.2, 0],\n", - " ['P'],\n", - "\n", - " ['M', 60, 1, 0],\n", - "\n", - " *Sat_Flash,\n", - " ['M', 10, 0.2, 0],\n", - " ['P'],\n", - "\n", - " *Sat_Flash,\n", - " ['M', 10, 0.2, 0],\n", - " ['P'],\n", - "\n", - " *Sat_Flash,\n", - " ['M', 10, 0.2, 0],\n", - " ['P'],\n", - "\n", - " ['A', \"B\", 0],\n", - " ['M', 50, 0.2, 0],\n", - "\n", - " *Sat_Flash,\n", - " ['M', 50, 0.2, 0],\n", - " ['P'],\n", - "\n", - " *Sat_Flash,\n", - " ['M', 50, 0.5, 0],\n", - " ['P'],\n", - "\n", - " *Sat_Flash,\n", - " ['M', 50, 1, 0],\n", - " ['P'],\n", - " \n", - " \n", - "]\n", - "\n", - "print_protocol(Protocol_1)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "NEW Name Here Ready\n", - "Start\n", - "Run\n", - "Stop\n" - ] - }, - { - "data": { - "image/png": "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q6uL//mf/4k777wz9uzZM7K+devWWLhwYfzpn/7pmHOeffbZmD59eqxatWpk7eabb44f/ehHMXv27Dhx4sSYc+rr6+Oyyy4b+bpUKkVPT080NDREf3//hW4XAAAAqlIqlaKvr++idmhFT8CHh4fj0KFD0draOmq9tbU1uru7xz3nM5/5TPz2t78dtXb27NmI+OjJ+XiGhoaiv79/1AAAAMBUVvFb0Ds7O+O+++6Ltra2mD9/fnR2dkZzc/PIW8o7Ojpi165dI8f/4Ac/iNtvvz2+9rWvxbx586KlpSW2bdsWP/7xj+O99967eFcCAAAAl7DaSk/YvXt3XH755fHQQw/F7Nmz4/Dhw7FixYo4fvx4RETMnj07mpubR47ftWtXlEqlWL9+fTz++OPx61//Ov71X/81/uZv/ubiXQUAAABc4ir6HfDJMhHvvQcAAIDzmfTfAQcAAACqI8ABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACBBVQHe3t4ex44di4GBgSiXy7FkyZKPPb6+vj6+853vxC9+8YsYHByMo0ePRltbW1Ub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BwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIUFWAt7e3x7Fjx2JgYCDK5XIsWbLkvMeuXLkyurq64v3334/f/OY30d3dHcuXL696wwAAADAVVRzgq1atiq1bt8amTZti0aJFsX///ti7d2/MnTt33OOXLVsW//Iv/xIrVqyIxYsXx759++IHP/hBLFy48JPuHQAAAKaMmogoKjnh4MGD8dprr8W6detG1o4cORJ79uyJBx988IK+x+HDh+OFF16Ib3/72xd0fKlUir6+vmhoaIj+/v5KtgsAAAAVm4gOregJeF1dXSxevDi6urpGrXd1dUVLS8sFfY+ampoolUrxwQcfnPeY+vr6KJVKowYAAACmsooCvLGxMWpra6O3t3fUem9vbzQ1NV3Q93jggQdixowZsXv37vMes2HDhujr6xuZnp6eSrYJAAAAl5yq/ghbUYx+13pNTc2YtfHcddddsXHjxvjKV74Sv/zlL8973ObNm6OhoWFk5syZU802AQAA4JJRW8nBJ0+ejDNnzox52j1r1qwxT8V/16pVq+Kpp56KO++8M1555ZWPPXZoaCiGhoYq2RoAAABc0ip6Aj48PByHDh2K1tbWUeutra3R3d193vPuuuuueOaZZ+Luu++Ol156qbqdAgAAwBRW0RPwiIjOzs547rnnolwux4EDB+KrX/1qNDc3x44dOyIioqOjI+bMmRP33HNPRHwU388++2z85V/+ZRw8eDCuuOKKiIgYGBiIvr6+i3gpAAAAcOmqOMB3794dl19+eTz00EMxe/bsOHz4cKxYsSKOHz8eERGzZ8+O5ubmkePXrl0bdXV1sX379ti+ffvI+jPPPBNtbW0X4RIAAADg0lfx54BPBp8DDgAAQKZJ/xxwAAAAoDoCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABFUFeHt7exw7diwGBgaiXC7HkiVLPvb4ZcuWRblcjoGBgXjrrbdi7dq1VW0WAAAApqqKA3zVqlWxdevW2LRpUyxatCj2798fe/fujblz5457/DXXXBMvvfRS7N+/PxYtWhQdHR2xbdu2uP322z/x5gEAAGCqqImIopITDh48GK+99lqsW7duZO3IkSOxZ8+eePDBB8ccv2XLlrj11lvjhhtuGFl78sknY8GCBdHS0nJBr1kqlaKvry8aGhqiv7+/ku0CAABAxSaiQ2srObiuri4WL14cW7ZsGbXe1dV13pi+6aaboqura9Tayy+/HGvWrIna2to4c+bMmHPq6+vjsssuG/m6VCqN+k8AAACYSBPRnxUFeGNjY9TW1kZvb++o9d7e3mhqahr3nKampnGPr6uri8bGxjhx4sSYczZs2BAbN24cs97T01PJdgEAAOATmTlz5uQ8AT+nKEa/a72mpmbM2u87frz1czZv3hydnZ0jX5dKpejp6Yk5c+Z4Czp/sNznfBq4z/k0cJ/zaeA+59Pg3H3+wQcfXLTvWVGAnzx5Ms6cOTPmafesWbPGPOU+58SJE+MePzw8HKdOnRr3nKGhoRgaGhqz3t/f73/g/MFzn/Np4D7n08B9zqeB+xwqU9FfQR8eHo5Dhw5Fa2vrqPXW1tbo7u4e95wDBw6MOX758uVRLpfH/f1vAAAA+ENU8ceQdXZ2xn333RdtbW0xf/786OzsjObm5tixY0dERHR0dMSuXbtGjt+xY0dcffXV8fjjj8f8+fOjra0t1qxZE4899tjFuwoAAACYAopKp729vXj77beLwcHBolwuF0uXLh35Zzt37iz27ds36vhly5YVhw4dKgYHB4tjx44Va9eurej16uvri4cffrior6+veK/GTJVxn5tPw7jPzadh3Ofm0zDuc/NpmIm4zyv+HHAAAACgchW/BR0AAAConAAHAACABAIcAAAAEghwAAAASHDJBHh7e3scO3YsBgYGolwux5IlSz72+GXLlkW5XI6BgYF46623Yu3atUk7hepVcp+vXLkyurq64v3334/f/OY30d3dHcuXL0/cLVSn0p/n57S0tMTw8HC8/vrrE7xD+OQqvc/r6+vjO9/5TvziF7+IwcHBOHr0aLS1tSXtFqpT6X1+9913x09/+tM4ffp0vPvuu/H000/HzJkzk3YLlVm6dGl8//vfj56eniiKIm677bbfe87FatBJ//Puq1atKj788MNizZo1xfz584snnnii6O/vL+bOnTvu8ddcc03x3//938UTTzxRzJ8/v1izZk3x4YcfFrfffvukX4sx55tK7/Mnnnii+Ou//uviC1/4QnHdddcVmzZtKj788MNi4cKFk34txpxvKr3Pz01DQ0Nx9OjR4p//+Z+L119/fdKvw5iPm2ru8z17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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "## This block run the protocol\n", - "# Change the protocol want to run:\n", - "run_protocol = Protocol_Eg1\n", - "# Signal offset when no exist\n", - "OFFSET = 4000\n", - "# Set the Serial ports for devices:\n", - "# If auto port detection doesn't work, put in Serial ports manually.\n", - "\n", - "ser = serial.Serial(Ambit_Port, 115200, rtscts=False) # This is Ambit\n", - "try:\n", - " ser_pulser = serial.Serial(Pulser_Port, 115200, dsrdtr= True, rtscts=False) # This is external LED\n", - "except:\n", - " ser.close()\n", - " raise ConnectionRefusedError\n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "#\n", - "fig, axis = plt.subplots(1,1,figsize = (12,6))\n", - "START_TIME = time.time()\n", - "try:\n", - " _wait_for_line(ser, target = \"NEW Name\", input = \"hello\", print_out=True)\n", - " if not _wait_for_line(ser, target = \"Start\", input = \"ww\", print_out=True):\n", - " ser.write(b'E\\n')\n", - " raise NameError(\"Function not started\")\n", - " \n", - " if not _wait_for_line(ser, target = \"Run\", input = \"\", print_out=True):\n", - " ser.write(b'E\\n')\n", - " raise NameError(\"Run not started\") \n", - "\n", - "\n", - " _act_ambit, _act_blue, _act_red, act_blue, act_red = -1, -1, -1, 0, 0\n", - " _data = []\n", - " for _l in run_protocol:\n", - "\n", - " if _l[0] == 'M' and len(_l) == 4: # Run measurement\n", - " \n", - " if (_act_ambit != _l[3]): # Set ambit actinic\n", - " _act_ambit = Ambit_actinic(ser, _l[3])\n", - " if (_l[1] < 1): continue # no data\n", - " for _ in range(_l[1]):\n", - " _ts = time.perf_counter()\n", - " _ret = Trigger(ser)\n", - " if _ret != {}: \n", - " _ret.update({\"ACT\":_act_ambit,\"Blue\":act_blue,\"Red\":act_red}.copy())\n", - " _data.append(_ret)\n", - " _elapsed = time.perf_counter() - _ts\n", - " if _elapsed < _l[2]: time.sleep(_l[2] - _elapsed)\n", - "\n", - " if _l[0] == 'A' and len(_l) == 3: # Change Actinic\n", - " if _l[1] == \"B\":\n", - " if _l[2] > 0:\n", - " if _act_blue != _l[2]: _act_blue = DAC_set(ser_pulser, 2, _l[2])\n", - " DAC_ONOFF(ser_pulser, 2, True)\n", - " act_blue = _act_blue\n", - " else: \n", - " DAC_ONOFF(ser_pulser, 2, False)\n", - " act_blue = 0\n", - " \n", - " if _l[1] == \"R\":\n", - " if _l[2] > 0:\n", - " if _act_red != _l[2]: _act_red= DAC_set(ser_pulser, 4, _l[2])\n", - " DAC_ONOFF(ser_pulser, 4, True)\n", - " act_red = _act_red\n", - " else: \n", - " DAC_ONOFF(ser_pulser, 4, False)\n", - " act_red = 0\n", - " \n", - "\n", - " if _l[0] == 'W' and len(_l) == 2:\n", - " time.sleep(_l[1])\n", - "\n", - " if _l[0] == 'P':\n", - " _plot_data = pd.DataFrame(_data)\n", - " axis.clear()\n", - " axis.plot(_plot_data['T']/1000, (_plot_data['S'] - OFFSET) / _plot_data['R'])\n", - " display(fig)\n", - " clear_output(True)\n", - " \n", - "\n", - "\n", - " \n", - "\n", - " DAC_ONOFF(ser_pulser, 0, False)\n", - " Ambit_actinic(ser, 0)\n", - "\n", - " if not _wait_for_line(ser, target = \"Stop\", input = \"E\", print_out=True):\n", - " ser.write(b'E\\n')\n", - " raise NameError(\"Run not Stopped\")\n", - " \n", - "finally:\n", - " ser.close()\n", - " ser_pulser.close()\n", - "\n", - "\n", - "data = pd.DataFrame(_data)\n", - "data.attrs['OFFSET'] = OFFSET\n", - "data.attrs['START_TIME'] = START_TIME\n", - "data.attrs['protocol'] = run_protocol" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'OFFSET': 4000,\n", - " 'START_TIME': 1738691730.8549724,\n", - " 'protocol': [['M', 100, 0.1, 0],\n", - " ['M', 10, 0.1, 0],\n", - " ['M', 250, 0.005, 254],\n", - " ['M', 20, 0.01, 0],\n", - " ['M', 20, 0.1, 0],\n", - " ['M', 100, 0.1, 0]]}" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data.attrs" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
TSRFBACTBlueRed
0579251553929561549000
11038160183951185888000
22038189194461043868000
33048192185951066851000
44048205182491081842000
...........................
492242978479182371523836000
493243988489182411444836000
494244988466182411365832000
495245988486182431373840000
496246988468182421331833000
\n", - "

497 rows × 8 columns

\n", - "
" - ], - "text/plain": [ - " T S R F B ACT Blue Red\n", - "0 5 7925 15539 2956 1549 0 0 0\n", - "1 103 8160 18395 1185 888 0 0 0\n", - "2 203 8189 19446 1043 868 0 0 0\n", - "3 304 8192 18595 1066 851 0 0 0\n", - "4 404 8205 18249 1081 842 0 0 0\n", - ".. ... ... ... ... ... ... ... ...\n", - "492 24297 8479 18237 1523 836 0 0 0\n", - "493 24398 8489 18241 1444 836 0 0 0\n", - "494 24498 8466 18241 1365 832 0 0 0\n", - "495 24598 8486 18243 1373 840 0 0 0\n", - "496 24698 8468 18242 1331 833 0 0 0\n", - "\n", - "[497 rows x 8 columns]" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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EZGdYoghIiIiu8QQQ0RERHaJIYaIiIjsEkMMERER2SWGGCIiIrJLDDFERERklxhiiIiIyC4xxBAREZFdYoghIiIiu8QQQ0RERHaJIYaIiIjsEkMMERER2aXrbhVrlUpl6yoQERFRO1nze/u6CTGmi1BQUGDjmhAREZGlVCoVqqurLXqPDIDQNdWRXt++fS2+ANeiUqlQUFCAgICATj82meO1lhavt3R4raXDay2tzrreKpUKhYWFFr/vummJAdChC9Be1dXV/B9CIrzW0uL1lg6vtXR4raVl7fXu6Hs5sJeIiIjsEkMMERER2SWGmGvQarVYuXIltFqtraty3eO1lhavt3R4raXDay0tW1/v62pgLxERETkOtsQQERGRXWKIISIiIrvEEENERER2iSGGiIiI7BJDzDXExcUhJycHdXV1yMjIwLhx42xdpW5txYoVEATBbCsqKmpWpqCgALW1tfj5558xbNgws9eVSiU2bNiAy5cv48qVK/j2228REBBgVsbLywtbtmxBRUUFKioqsGXLFvTs2bPLP58tjR8/Htu2bUNBQQEEQcD06dOblZHq2gYFBWHbtm24cuUKLl++jLfffhsKhaLzP7SNXOtab968udn3PC0tzawMr3X7/PWvf8X+/ftRVVWF4uJifP311xg0aFCzcvxud472XG97+34L3FreYmNjBa1WKyxcuFAYMmSI8I9//EOorq4WgoKCbF637rqtWLFCOHr0qNCnTx9x8/X1FV9funSpUFlZKcycOVMYPny48NlnnwkFBQWCh4eHWOb9998X8vLyhDvuuEMICwsTdu3aJWRmZgpyuVws8/333wtHjhwRIiMjhcjISOHIkSPCtm3bbP75u3K78847hddff12YOXOmIAiCMH36dLPXpbq2crlcOHLkiLBr1y4hLCxMuOOOO4T8/Hxhw4YNNr9GUl3rzZs3C99//73Z97xXr15mZXit27clJycL8+bNE4YNGybccsstwvbt24Xz588Lbm5u/G7b6Hrb2ffb9he1u27p6enC+++/b7bv2LFjwurVq21et+66rVixQsjMzGz19cLCQmHp0qXiz0qlUigvLxcWLVokABA8PT0FrVYrxMbGimX8/f0Fg8EgREdHCwCEIUOGCIIgCCNHjhTLjBo1ShAEQRg0aJDNr4EUW0u/WKW6tnfeeadgMBgEf39/sczs2bOFuro6QaVS2fzaSHGtN2/eLHz99detvofXuuObr6+vIAiCMH78eHEfv9vSXm97+n6zO6kVCoUCERERSElJMdufkpKCMWPG2KhW9mHgwIEoKChATk4OPvvsM/Tv3x8A0L9/f/j7+5tdU51Oh19//VW8phEREVAqlWZlioqKkJWVJZYZPXo0KioqsH//frHMvn37UFFR4bB/N1Je29GjRyMrK8usm3DHjh1wdXVFREREl37O7mTSpEkoLi7GyZMnsWnTJvTu3Vt8jde640zdDWVlZQD43e5qf7zeJvby/WaIaYWvry+cnZ1RXFxstr+4uBhqtdpGter+9u3bh4cffhhTp07FY489BrVajb1798Lb21u8bm1dU7VaDa1Wi4qKijbLXLp0qdm5L1265LB/N1JeW7Va3ew8FRUV0Gq1DnP9k5OTMXfuXEyePBnPP/88RowYgZ9++glKpRIAr7U14uPjsXv3bmRnZwPgd7ur/fF6A/b1/b6uVrHuCoIgmP0sk8ma7aMmP/zwg/jnrKwspKWl4ezZs5g3bx7S09MBdOya/rFMS+X5dyPdtXX065+UlCT+OTs7GxkZGbhw4QLuvvtufP31162+j9e6be+++y5uueWWFh+g4He787V2ve3p+82WmFaUlJTAYDA0S4N+fn7NkiO1rra2FkePHsXAgQNx8eJFAGjzml68eBEuLi7w8vJqs0yfPn2anat3794O+3cj5bW9ePFis/N4eXlBqVQ69PW/cOECBg4cKP7Ma22ZDRs2YNq0abj99ttRUFAg7ud3u2u0dr1b0t2/3zYfWNRdt/T0dOG9994z25ednc2BvRZsSqVSyMvLE1599VUBaBig9+KLL4qvKxSKFgfo3XfffWIZtVrd4oCxESNGiGVGjhzJgb0SXVvTYDy1Wi2WiY2NvW4HP7Z0rf+4eXt7C3V1dcJDDz3Ea92B7Z133hHy8/OFkJCQFl/nd1va6/3HrZt/v21/QbvrZnrEesGCBcKQIUOE+Ph4obq6WggODrZ53brrtnbtWmHChAnCDTfcIIwcOVLYtm2bUFlZKV6zpUuXCuXl5cKMGTOE4cOHC59++mmLj0rm5uYKkydPFsLCwoSdO3e2+Oje4cOHhVGjRgmjRo0Sfv/99+v+EWt3d3chNDRUCA0NFQRBEJ599lkhNDRUfORfqmtreizyxx9/FMLCwoTJkycLubm519VjqG1da3d3d2Ht2rVCZGSk0K9fP2HixIlCamqqkJeXx2vdge29994TysvLhQkTJpg90uvq6iqW4Xdbuutth99v21/U7rzFxcUJ586dEzQajZCRkWH2GBq35ptp/gatVivk5+cL//nPf4ShQ4ealVmxYoVQWFgo1NXVCb/88oswfPhws9ddXFyEDRs2CCUlJUJNTY2wbds2ITAw0KxMr169hMTERKGyslKorKwUEhMThZ49e9r883flNnHiRKElmzdvlvzaBgUFCdu3bxdqamqEkpISYcOGDYJSqbT5NZLiWru6ugo//PCDUFxcLGi1WuH8+fPC5s2bm11HXuv2ba2ZN2+eWTl+t6W53vb2/ZY1/oGIiIjIrnBgLxEREdklhhgiIiKySwwxREREZJcYYoiIiMguMcQQERGRXWKIISIiIrvEEENERER2iSGGiIiI7BJDDBEREdklhhgiIiKySwwxREREZJcYYoiIiMgu/X/CU1uJi50o1wAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(data['T'], data['S']/data['R'])" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/.vscode/examples/Untitled-1_temp.ipynb b/.vscode/examples/Untitled-1_temp.ipynb deleted file mode 100644 index 082b984..0000000 --- a/.vscode/examples/Untitled-1_temp.ipynb +++ /dev/null @@ -1,685 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "import serial\n", - "import serial.tools.list_ports\n", - "import numpy as np\n", - "import time\n", - "import matplotlib.pyplot as plt\n", - "from IPython.display import clear_output, display\n", - "import pandas as pd\n", - "plt.style.use('dark_background')" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found USB devices:\tUSB VID:PID=1A86:55D4 SER=554C028532 LOCATION=1-4.2\n", - "Ambit Likely at:\tCOM10\n", - "Pulser not Found\n" - ] - } - ], - "source": [ - "Ambit_Port = \"\"\n", - "Pulser_Port = \"\"\n", - "ports = serial.tools.list_ports.comports()\n", - "for port, desc, hwid in sorted(ports):\n", - " if hwid[:4] == \"USB \":\n", - " print(f'Found USB devices:\\t{hwid}')\n", - " devices = hwid[4:].split(\" \")\n", - " if len(devices) > 1 and len(devices[0]) > 9:\n", - " if devices[0][-9:] == \"1A86:55D4\":\n", - " print(f'Ambit Likely at:\\t{port}')\n", - " Ambit_Port = port\n", - " if devices[0][-9:] == \"303A:1001\":\n", - " print(f'Pulser Likely at:\\t{port}')\n", - " Pulser_Port = port\n", - "\n", - "if Ambit_Port == \"\": print(f'Ambit not Found')\n", - "\n", - "if Pulser_Port == \"\": print(f'Pulser not Found')" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def _readline(ser: serial.Serial, timeout: int):\n", - " _t0 = time.time()\n", - " s = \"\"\n", - " while (time.time() - _t0 < timeout):\n", - " if ser.in_waiting > 0:\n", - " c = ser.read()\n", - " if (c > bytes([0])) and (c < bytes([128])):\n", - " if (c == b'\\n') or (c == b'\\r'):\n", - " if s == \"\":\n", - " pass\n", - " elif len(s) > 2:\n", - " return s\n", - " else:\n", - " s += c.decode()\n", - " \n", - " return s\n", - "\n", - "def _wait_for_line(ser: serial.Serial, target: str, input: str = \"\", timeout: float = 1.0, print_out = False):\n", - " if input != \"\":\n", - " ser.write((input + \"\\n\").encode())\n", - " t0 = time.time()\n", - " timeout_t = t0 + timeout\n", - " condition = False\n", - " while (condition == False):\n", - " _l = _readline(ser, 0.25)\n", - " if target in _l:\n", - " condition = True\n", - " if print_out:\n", - " if _l != \"\": print(_l)\n", - " if time.time() > timeout_t:\n", - " break\n", - "\n", - " return condition\n", - "\n", - " \n", - "def _parse_data_line(l:str)->dict:\n", - " ret = {}\n", - " if len(l) > 5:\n", - " if (l[:2] == \"T:\") and (\",\" in l) and (\":\" in l):\n", - " items = [n.split(':') for n in l.split(',') if \":\" in n]\n", - " for i in items:\n", - " k = i[0]\n", - " v = i[1]\n", - " if len(k) > 0:\n", - " if v.isdecimal():\n", - " ret.update({k: int(v)})\n", - " if (v.count(\".\") == 1) and v.replace(\".\", \"0\").isdecimal():\n", - " ret.update({k: float(v)})\n", - "\n", - " return ret \n", - "\n", - "\n", - "def Trigger(ser: serial.Serial) -> dict:\n", - " ser.write(b\"G\")\n", - " ser.flush()\n", - " _ret = _parse_data_line(_readline(ser, .5))\n", - " return _ret\n", - "\n", - "def Ambit_actinic(ser: serial.Serial, v:int)->int:\n", - " if (v > 255): return -1\n", - " ser.write(bytes([65, v]))\n", - " ser.flush()\n", - " return v\n", - " \n", - "\n", - "def DAC_set(ser: serial.Serial, ch:int, v:int):\n", - " ret = False\n", - " if (ch == 2) or (ch == 4):\n", - " if (v > -1) and (v < 4095):\n", - " ret = _wait_for_line(ser, target = \"Done\", input = f'DAC,{ch},{v},\\n', print_out=False)\n", - " if ret == False: \n", - " print(\"DAC_set error\", ch, v)\n", - " return -1\n", - " return v\n", - "\n", - "def DAC_ONOFF(ser: serial.Serial, ch:int, onoff:bool):\n", - " if onoff:\n", - " if (ch == 2) or (ch == 4):\n", - " _wait_for_line(ser, target = \"Done\", input = f'LED_ON,{ch},\\n', print_out=False)\n", - " else:\n", - " if (ch == 2) or (ch == 4):\n", - " _wait_for_line(ser, target = \"Done\", input = f'LED_OFF,{ch},\\n', print_out=False)\n", - " else:\n", - " _wait_for_line(ser, target = \"Done\", input = f'LED_OFF,0,\\n', print_out=False)\n", - "\n", - "# plot autoscale\n", - "def autoscale(data: list, axis:plt.Axes):\n", - " if len(data) == 0: return\n", - " if len(data) == 1: \n", - " axis.set_ylim([data[0]-1, data[0]+1])\n", - " return \n", - " l, h = axis.get_ylim()\n", - " x, d = np.min(data), np.max(data)\n", - " r = d - x\n", - "\n", - " _l, _h, a = l, h, False\n", - " if (x < l) or (l < x - r):\n", - " _l = x - 0.05 * r\n", - " a = True\n", - " if (d > h) or (h > l + r):\n", - " _h = d + 0.05 * r\n", - " a = True\n", - " if a and _l != _h:\n", - " axis.set_ylim([_l, _h])\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [], - "source": [ - "ser.close()" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABAMAAAH5CAYAAAD0j/f2AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOydebwcVZ32vze592ZPLpCVJYQtAWQ1wASMoEgA0QHBGRFcGEYdAUcdGccBx2UcBd7RERREeMdhHF7GdUYRdVjiwkBkEQOykwCBsAQSEpKbPblZ7vvHqXPr9Omq6qru6u7q7uf7+fSnqquqq053V50656nn9ztdwCBCCCGEEEIIIYToGIY1uwBCCCGEEEIIIYRoLBIDhBBCCCGEEEKIDkNigBBCCCGEEEII0WFIDBBCCCGEEEIIIToMiQFCCCGEEEIIIUSHITFACCGEEEIIIYToMCQGCCGEEEIIIYQQHUZ3swsQxZFHHsmKFSuaXQwhhBBCCCGEEB3ClClT+OMf/9jsYjSMwokBRx55JA899FCziyGEEEIIIYQQosN44xvf2DGCQOHEAOsIeOMb3yh3gBBCCCGEEEKIujNlyhQeeuihjuqDFk4MsKxYsYJXXnml2cUQQgghhBBCCCHaDiUQFEIIIYQQQgghOgyJAUIIIYQQQgghRIchMUAIIYQQQgghhOgwJAYIIYQQQgghhBAdhsQAIYQQQgghhBCiw5AYIIQQQgghhBBCdBgSA4QQQgghhBBCiA5DYoAQQgghhBBCCNFhSAwQQgghhBBCCCE6DIkBQgghhBBCCCFEhyExQAghhBBCCCGE6DAkBgghhBBCCCGEEB2GxAAhhBBCCCGEEKLDkBgghBBCCCGEEEJ0GBIDhBBCCCGEEEKIDkNigBBCCCGEEEII0WFIDBBCCCGEEEIIIToMiQFCCCEaxqHAj4BZzS6IEEIIIUSHIzFACCFEw/gL4D3AuU0uhxBCCI9JwHuB2c0uiBCiUXQ3uwBCCCE6h1HBdERTSyGEEKKEfTFK7UjgQGAKcDuws5mFEkLUGzkDhBBCNIyeYColWgghCsIbgfdjhICVwbJjgNObViIhRIOQGCCEEKJhSAwQQogG0QecBLwPOAzo8tZ3AfMwnf5hwCPA9ZjELoPAEcA0YAzGLaBegxBtR6bL+oILLuCRRx5h7dq1rF27lnvvvZdTTz11aP2ZZ57J7bffzsqVKxkcHOTwww/PvcBCCCFaF4kBQgjRAMYBHwXmAgcAZwEfAd4FvA3YB/hz4E3B9ncCNwM7gKeAx4LlZwB/jcklcGRjii6EaByZxICXX36ZSy65hKOOOoqjjjqK3/72t9xyyy0cfPDBAIwZM4Z77rmHSy65pC6FFUII0dpYMWB4U0shhBBtzhGYJC2rgAXAVmD3YPmbgfOAg4HtwE+Au7zP34XJFzCVMNnLPnUusxAtyoUXXshzzz3H5s2bWbhwIXPnzo3ddurUqXzve99j0aJF7Nixg6uuuipyu7POOosnnniCLVu28MQTT/Cud72rZP0Xv/hFBgcHS16vvvpq5rJnejjzy1/+suT95z73OS688ELmzJnDk08+yX/+538CsPfee6feZ29vLyNGhKmkxo4dm6VIQgghWgg5A4QQos50YfIAgBECHgHuB96AqYQnAfsH638MvBixj9eBe4CjgcXA4cD0+hVZiFblPe95D9/4xje46KKLuOeee/joRz/KbbfdxsEHH8xLL71Utv2IESNYuXIll112GZ/61Kci9zlnzhx+9KMf8fnPf56bb76ZM888kx//+MfMnTuXBx54YGi7xx9/nJNOOmno/Y4dO6r6DoPVvIYNGzZ49tlnD27ZsmXwoIMOKlm39957Dw4ODg4efvjhFffzxS9+cTCK3Xffvapy6aWXXnrpVdzXL2FwEAa/W4Cy6KWXXnq15WsfBvlHBrmEQXoStutKub8eBvlCsM8JBfh+eulVp9fuu++euR96//33D377298uWfbkk08OXn755RU/e+eddw5eddVVZct/+MMfDt56660ly2677bbB73//+0Pvv/jFLw7+8Y9/rPk7Z04Fcsghh7B+/Xq2bt3K9ddfz5lnnslTTz2VdTdDXHHFFYwfP37oNWvWrKr3JYQQotgoTEAIIeqMdQU8BmxL2G4w5f62AdZ9vFe1hRKidRg7dizjxo0bevX29kZu19PTw+zZs5k/f37J8vnz53PcccdVffxjjz22bJ933HFH2T4POOAAli1bxnPPPccPfvAD9tkneyxPZjFg8eLFHHHEEcyZM4frrruOG2+8kYMOOijzgS0DAwOsX79+6LVhw4aq9yWEEKLYdHtTIYQQEewGHA/8JXCyt25f4EPAURGfGwXYZvmDOZbHup0VKiA6gMWLF7Nu3bqh16WXXhq53cSJE+nu7mbFihUly1esWMHUqVOrPv7UqVMr7vP3v/89H/zgBznllFP4yEc+wtSpU7n33nvZddddMx0rc3ts27ZtLFmyBIAHH3yQo48+mk9+8pNccMEFWXclhBCiw1DOACGEqMBk4ALCR3bTgSXB6wjgTzH2qr2CbW/HJPsDk/G/G3gFWJ5jmV4C5iBngOgIZs2aVZKMb+vWrYnbDw6W2my6urrKlmWl0j5vv/32ofnHH3+c++67jyVLlnDeeefFJiWMoub2WFdXV0kCQCGEECIOiQFCCFGBQzBCwGvAGmAWcCrmSb8d0fslYE/gGIyL4L8wIwYcHaz/Q85leiWYTsp5v0IUkA0bNrB+/fqK261atYrt27eXuQAmT55c9mQ/C8uXL8+8z02bNvHYY49xwAEHZDpWpjCByy67jLlz57L33ntzyCGH8JWvfIW3vOUtfO973wNgl1124fDDDx8aanDWrFkcfvjhTJkyJVOhhBBCtCfKGSCEEBWwNv8FwM3ARkwn3AoBvwP+HfgRMADsB3wUOAPYBdgMPJ5zmWwUbzegZ4BCAMYx/+CDDzJv3ryS5fPmzePee++ter/33Xdf2T5PPvnkxH329vZy0EEHZR5eMNPDmSlTpnDTTTcxbdo01q5dy6OPPsqpp57Kr3/9awBOP/10/uM//mNo+x/96EcA/OM//iNf+tKXMhVMCCFE+yFngBBCJDAR0/HfATyNedr/W0xoAMBDwK+D+UUYUeAcjAiwS7D8YZITB1bDtuDVA4wOyiWE4Morr+Smm25i4cKF3HffffzVX/0V06dP5/rrrwfg8ssvZ4899uC8884b+szhhx8OmESFkyZN4vDDD2dgYGAoKf83v/lN7r77bj7zmc9wyy23cMYZZ3DSSScxd+7coX187Wtf4xe/+AUvvvgikydP5nOf+xzjx4/nxhtvzPwdmj6Mg/uqZkgHvfTSSy+9WuP1JGZowTsKUBa99NJLr8K95mKG8Hufs6yLQd7FIGcwyPCIz4xkkCMY5M8Y5IMMMq5OZftUULY9CvA76aVXHV7V9kMvvPDCweeff35wy5YtgwsXLhx885vfPLTuu9/97uCdd95Zsn0Uzz//fMk27373uwefeuqpwa1btw4++eSTg2eeeWbJ+h/84AeDy5YtG9y6devgyy+/PPjf//3fgwcddFDm79wVzBSG3XffnWXLlrHHHnvwyiuvVP6AEEKIluEZYH/Mg663NbksQghRKLqAj2HcAb8g39EA8uCjwDTge5jKXIg2oxP7oZmHFhRCCCGqRTkDhBAihkMwQkA9Yv7zYFMwHd3UUgghckRigBBCiIahnAFCCBFBF3BCMH8vxYzJ3xhMJQYI0TZIDBBCCNEwMokBw4CDgfF1K44QQhQD6wrYBDzQ5LLEIWeAEG2HHs4IIYSoD5Mw2a2fDhdlEgNOA44C1gP/GkyFEKIdOS6YFtUVABIDhGhD5AwQQgiRP33AXwLnAvuEi0tyBnQlfP5YjBAAMA54L9CbbxGFEKIQjMYk5gMzdGBRkRggRNshMUAIIUQ8o4EDKO+4jwbeAVwKXAi8HWPpH4Pp6f8ZMCrY9k/Cj/UE++meBHwWOAUY6e17P+DkYP4eTAN0D4y4oJABIUS7MT2YvkbY4S4itmxjmloKIUSOKExACCFENGOBD2Gs/ncCdwG7ArODl+3ETwlettO/HvM0f0uwzUxgAjAA3ZvNJt07MTaBY4EjgGXAy5hY2dMw4sODwK+AJzAOg6nAR4AbgVV1+L5CCNEM9g6mLzS1FJWRM0CItkNigBBCiHJ6MR3wXYL3x2Os/4cTespeBX6D6dTPCF5TMEIAwM0YgWBf4K0w7HUYtsCs6n4duAnjDJgM7B+85mLuTOuB+cF+XgG+E5RnCnBO8H5Lfl9XCCGahsQAIUSTkBgghBCinNOA3TFDSa3CNFaPDNY9C/wBkxhwMFj2VDAdjbG8bgWeB3ZixIAjoGcbEIgBwwGWANcDe2GyaB8bTME4AtwkWmuB/4dxBuwGvA/YgRENbsWMyy2EEK1GL8b1BPBiMwuSAisGjMK4twYTthVCtAQSA4QQQpTSixnmCuDHQD9wHqbD/SsYuxQ+j3HrP+l/dhOwyHn/DPBfwGnQ49xxhmZ3Yp6GvQD8ERMy0A08GlGujcAPMbkD9nKW7wX8EhNvuyHYpxBCtAJ7YdxWa4B1TS5LJawY0IURBIqc30AIkQqJAUII0W4MBw7FdLDXVNjuIIy9/37COPwDMHeH1wltq1eHH3sP8BngVEzUQEWeAJ6HHmfjyJvPTipn0l6OESjeBCwNCrAr8H5nHxswjoE1mASEr6YppBBCNIFWCREAU79uxggBo5EYIEQbIDFACCHaiS7gLOANGMvpv8dsdwgmY7/Nzj8D+L/ANuDAYNlT5R8DkwsQ4DBgHsbRX5FN0HNf+Lamm8+zwQuMiHEqZvjCcRiBY3zw2gOYhXEmPB2zr4OB1RiRQQghGk0riQFgBAArBgghWh6JAUII0U68EyMEgIndH4t5Um7pBt6LSdYHxpY6DBOrfxImad/MYF2MGDDCmf80KcUATJ5By/CUn6nIVuCWYL4L00C1YsDRmO/5XuC/KY1pGA6cjnEWrAG+mVeBhBAiJd0Y0RJaSwzYDYkBQrQJwypvIoQQoiU4FDPk306MTR7Cp/yWt2E6yNuA32Ls/zcH6/4E4xYYgREJXok+jCsGnBwcNg2uGFAXJXoQk1fgVWAx8H3gYcyd7q3ewd9HGOOwC2rYCiEazx6Eo6esbnJZ0qIRBYRoKyQGCCFEEZkIfAp4e4bPHBZMfwf8Pph3xYB9MRn7wVjn7wa2Y7L6PxAs/5NguojYTNEjvPcXpyxe3cUAn53AbRjhYxJmdAQIhzvcStiwnVr2aSGEqC/Tg2nRRxFwkRggRFshMUAIIYrGcODdmOD8VBn6MD30fYL5Rwkt/vsAI4P17wqW2WEBXeYDjzjvFxGLFQPuDKbnAtNSFLHhYgCYDr/9LodhRkp4U/D+VkwSQoApjSqQEEIEtFq+AAjFgDFNLYUQIickBgghRNF4K2HveiQmMV4l7AgAq4LX65ih9oYDb8TkERiPsaLOj/j8dky4wM2Y8IHn4g9lxYAFwasX+HiKItYlZ0AarMhxBGbUgdGY3+gxwsSBcgYIIRrJMMIhUltRDJAzQIi2QGKAEEI0m2mYrPddmHh+++R6IJhOrvD5YYSB+27Sv3uD6VxMLgGABzG2+TgewYQPJGDFgK3A14P5C6j8oMgXA7oqbJ8bz2FyIIwktOX+LyaMYEXwXs4AIUQjmYqpTDdjhNtWQWKAEG2FRhMQQohmMgY4H/N4fTkmmV0XsBDT2DoYE+++JOKzu2Ke+h+BGTUASsWARzFCwMRgX4OYp+E14ooBvwCewRgTzge+lfC5Hu/9cIwhoe7sBG4iDKPYQDiygHUGTAoKtKMRBRJCdDw2ROBFYvOzFJKNwVRigBBtgZwBQgjRTI7DCAEQPilaikl8Z58W+c6AKZhs+J/AdPbt8IG3UzoCwE7CwH6C/a6rvciuGLATuDJ4/ymSbyq++txQNXolJkniA5QOMbgW82RuOEY0EUKIRmBdSq0UIgByBgjRZsgZIIQQli5M9v0XCZ9+1JPRwNHB/M2YTv4E4JeYJ9Qrg3WTnM8cgkku2IV5mvQs8BBmKL2dEcd4EvP0eyqlCQJrwBUDAG4EvoxJ0H8m8JOYz0U5AwrBCmAG5jdakbypEELUTBelzoBWQmKAEG2FxAAhhLDMAU7B+N6/V8N+xgHvxDzxuY9SC+hwjAAwM5jvxTzNj+qoW2eAFQOGA/MwDcmnMIkA11QoyyDmu+wNPJHxe8TgiwGbgeuAzwN/S3oxoDA3ICsGKG+AEKIR2NCtAeDVJpclK1YMGIGpxBsS6yWEqBeFaYsJIURT6cVY7gH2w3To11e5rzdjEgLOwnQyb8b0mKcBfwbs5m1/V8x+VmMcAiMxIwHMwjgH1mF63GkbYeuBx1OXviK+GAAmV8BngGMxOoc/ciEUWAzQiAJCiEZiXQEv03p5Sgac+R4kBgjR4ihngBBCgHlab9Phu9n5szICk9APTCNvJiaz3miMvX83THz/bzHW/j8E0yh2YIYIBCMwvDmYX0BTG2BRYsBrhG7XuND7wooBGlFACNFIrBjQavkCwISj2ZC0wlTiQohq0WUshBC9hMP5PY/JOn8Y4dB8WTgi2N9rwE+BczEJAC/EuA02Yh6jb0m5v0eBkwhzC6zF5AhoIlFiABjzA8ComM8VNmdAfzAdQ5iLQQgh6kUriwFgxOhe1IsQog2QM0AIIWZjnty/DvwXpqEzldLEfWnoxfjkwWStX46J19+KEQLAhASkFQIAfhfsY1Xw/rc03VYaJwbYrzUy5nOFdQZsc+YLUyghRFvShwn72gEsa25RqsY601RfCtHy6DIWQuTDCOBgTKf6Xlrr6eobg+m9mORIL2DyBuxNmNE/DadgGnr9hAkBVwA/wjgE1gAPVlG+Z4AlGEFhbRWfz5m8nAGFuQG5IRc9lIoDQgiRJ3sF01do3bpGYoAQbYMuYyFE7RyDyXJve3urMdnuW4G9MA6AAcIkey9hxIDpwMKU+5mJcRgMYhIGuo2854BvBseo9qn+TgohBEAoBgx4y60Y0HLOgEHM/zKcAhVKCNGW7BJMswjNRcPe31RfCtHy6DIWQtRGD6EQsB1Tq0yjdcSAI4Ppk4SPum0mvL3KNy9jV4wj4IDg/X1Ex4FWOzJBAekNpnFhAi2XMwBM43Y45YUUQog86Qum/U0sQ63IGSBE26DLWAhRG/tjOlCrgfuB0zAJ86phD+A9mM70T3MpXch4TBhDF+bJ/8uYXu0hwXo3Kd/LmCfxu5A8xOC+wJ8T9n4XY2L625y2CxMANW6FEI2hL5j2N7EMtaL6Uoi2QZexEKI2Dgymi6htiLYpwPsxPcnDMB32P9RcOsMw4ByMY8HyJOZpcC8mOd+LzroBzHeZhgkVeCJin0cDbw/2/RJwC2GSvzan7RIIQmh7lTNACFFP+oJpfxPLUCsSA4RoG3QZCyGqZxgmVh6MGGBjIHfBdLL9oHKXHmC3YH4v4C0YIWA95mn8yZhh/vLoYB+N6dhvxrgOZmJcApb7Iz7zYvCZvSgVA4ZhRAA71N/DwC9oeob/RiJngBBCVEEXMCGY729iOWpF9aUQbYMuYyFE9eyD6fltxDwdHwTWYSz5kzF2+yiGAR+hPJzgFeD/Yaz3+wF/jXli+1/A01WWcSxwYjD/a0w2/8nAm4NyPIzJ1u/zEvAnGGeAyzyMEDAI/AozAkEHMZww1j+rM8C/4RQuZwDoriiEqB/jMRXfDlo7j4zEACHahmHNLoAQooU5Kpg+STiU4GvB1A0VGE3p4+IDMR1y2yBaDtwKfBfTo7yFsKHUA7wL4xbIShfwDsyj7JcJ8wK8BvwEIzJECQEQhg1MJcyY10s4DOFP6TghAEJXALSpM0BhAkKIetEXTNfSWsPv+qi+FKJtKFRbTAjRQvQBs4L5B5zlKzBJBa0YMBa4KJi/AXgdODZ4/zvgzoh9rwOuwvQq34+x658BfI9sDah5wEEY0eF/Mn52HcbG2YcJFVgCHIrpDa8CHsuwrzaibcUAOQOEEPWmL5j2N7EMeSBngBBtg5wBQojqOAZTgyyhdLxkm0RwerD+bRhnwGjgfcC7MZ3r7SQnCNyJCT/4Caajtj/G2p+Wo4HjgvmfAa9m+KzluWBq8yJYJ8SDVeyrTbBiwE7C9qClpRMI6kmXEKLe9AXTNc0sRA5IDBCibZAYIITIjmuX95PvLcV03qcCZwNHBss3ALtinq6DidXfkOJYq4Dbg/kTMSJEJUZgXAFg8gRU+xR/UTA9EOMwmIZpBD1c5f7agLjkgZDdGaCcAUKIjqIvmPY3sQx5IDFAiLZBl7EQIjuHYx7/vg48661bh3mafzZhGMEjwN2Y8IB1mJh9/3NJPIjJwHw8cBrGWXArYe/T5xCMYLESE4pQLc9hRkSYAJwVLLsv4bgdQJIYIGeAEEIk0BdM+5tYhjyQGCBE2yBngBCdxkhMp3zvKj/fhcmyD/B7ouPwFwHzg/kBzNP514FfYkSBRZR7zCvxW0x+gZ0Yd8HHMGJDDyaEYF/CGm12MK3Vzr+dULTowYQa/G+N+2xx8nQGFKodKWeAEKLe2GEF1za1FLUjMUCItkGXsRCdxFEYq/1oTCf9GrIPb7QfMBHzGPjhhO3uwzR41lZxjDjuwmT/fxdmNIJzMMkBrd98Q/CaimmsPJLDMZ8CDg72d3NwvA5GzgAhhKiS0cF0Y1NLUTsSA4RoG3QZC9EpHAS8M5jfibHRvw2TXC8L1hXwR4ygkMSTGfedhleAfwVOAN6EEQL6Md9nbPACkycgDzv/Exhx4UXCYRM7GOUMEEKIKugiVEq3JG3YAqi+FKJt0GUsRKcwJ5j+AfPE/MPAEcH7ZSn3MRE4ABMa8ECFbevJduA3QRl6gNWYnuVewXQn8FJOx9oJ/CqnfbUBbRsmIGeAEKKeuJapVs87I2eAEG2DcgYI0QlMxuQI2ImJ2X8Z82Qf4JQM+7GZ/BdTjKGR1mOEADD2/aWYoQ6fJ3tOApGKtg8TKFShhBBtg1VJBzD34lZG9aUQbYPEACE6gaOD6VOE8fu/wdzQpwNTUuxjJMZJACZxoOhI2tYZYG2vcgYIIeqBVUlb3RUAEgOEaCMkBgjRCRwUTN3s+huAp4P5w1Ls40hMXP4KzJN30ZHk6QwoVM4ANW6FEPXEqqStni8AVF8K0UZIDBCi3dkFk1RvByYJnsujwfRQTHKjOLoIQwTkCuhoanEG+O3GQrUj5QwQQtSTdnQGqL4UouWRGCBEu7NXMH2F8jj6Z4BNwHhgdsI+9sWICpsIBQTRkeThDChkImo96RJC1BOrkraTGKD6UoiWJ5MYcMEFF/DII4+wdu1a1q5dy7333supp55ass0Xv/hFli1bxqZNm7jzzjs5+OCDcy2wECIjVgyIyq6/A3g4mH8n8AFgQsR2+wfTp1Bivg4njTMAogWBHm+7QrUj5QwQQtSTdhlWECQGCNFGZBIDXn75ZS655BKOOuoojjrqKH77299yyy23DHX4P/OZz3DxxRfz13/91xx99NEsX76cX/3qV4wdO7bCnoUQdSNJDAD4LXAX5ua+H2bIQT+h4L7B9LncSydajLRiQFSogC8GKGeAEKJjkDNACFFAMokBv/zlL7ntttt45plneOaZZ/jc5z7Hhg0bmDPHDGD+N3/zN1x22WXcfPPNPPHEE5x33nmMHj2ac889ty6FF0JUYARmWEEwwwlGsR24E7gWkxxwHPAXhA6BsRhxYBAlDuwAjgL+DzA6Zn2SGLCDsI2Y5AywD8YK1Y4sZOyCEKJtkDNACFFAqs4ZMGzYMM4++2zGjBnDfffdxz777MO0adOYP3/+0DYDAwPcddddHHfccbH76e3tZdy4cUMvuQiEyJE9MFf5GsIhBeNYA3wXk1tgFHAGJnHgPsH6VzE5A0Rb80Xg74F3xKxPEgMgOYlgocMElBBLCFFP5AwQQhSQzGLAIYccwvr169m6dSvXX389Z555Jk899RRTp04FYMWKFSXbr1ixYmhdFJdeeinr1q0bei1evDhrkYQQcVQKEfDZAvw3MIAJDTgZODpYpxCBjsDKseNi1lcSA5KSCBZaDJAzQAhRT9rRGSDxVIiWJ7MYsHjxYo444gjmzJnDddddx4033shBBx00tH5wcLBk+66urrJlLldccQXjx48fes2aNStrkYQQcWQVAwBWA9bgcywwPZh/Jq9CiSJj+8IjYtbn6QwoZM4ANW6FEPWgHZ0BULCKXAiRlczPQLZt28aSJUsAePDBBzn66KP55Cc/yT//8z8DMHXqVJYvXz60/eTJk8vcAi4DAwMMDAwMvR83Lu55lBAiM7sH07h8AXEsBPbECAHPA4uAF3Islygsti/cG7M+D2dAoXMGDAteO5tYFiFE+9FOzoBtznw3JmGMEKIlqTpngKWrq4sRI0bw/PPP8+qrrzJv3ryhdT09PZxwwgnce++9tR5GCJGV4YRZ4Pqr+PzPgKuBXyBXQAfRSGdAocQA90mX3AFCiLxpkjOgBzid6FGDq8bt/BeqIhdCZCXTJXzZZZdx22238dJLLzFu3Dje+9738pa3vIVTTz0VgG984xt89rOfHRpt4LOf/SybNm3i+9//fl0KL4RIwAZ/76A9bImiIdTqDGhpMWAQkzSzm/gvKIQQWemiac6Ac4AbgauAi/Pc8TZMpV6oilwIkZVMl/CUKVO46aabmDZtGmvXruXRRx/l1FNP5de//jUAX/3qVxk1ahTf/va32WWXXfj973/PySefzIYNG+pSeCFEAlYM0OUnMlCrMyBLAsHChZpuxxRSzgAhRJ64FWKDxfk9g+m0vHds60uJAUK0NJku4Q9/+MMVt/nSl77El770paoLJITICYkBogoa4QwoZM4A0JMuIUR9sGLAAA3PR2IPnbvGqaSrQrQFNecMEEIUFIkBogrq6Qyw+y5kmACocSuEqA9NHEnAHrpuYkDhKnIhRBYkBgjRrkgMEFVQrwSCNkk/FNgZoMatEKIeNHEkAVsXx7m9qkb1pRBtgcQAIdoViQGiCvIaWtAXA9ynUoXNGWCHy5IzQAiRJ010BtQ9TEBigBAtjcQAIdoViQGiCvJyBvhhAm5DVM4AIURHUQBngMQAIUQUEgOEaFckBogqqFcCwShnQOHakHIGCCHqQQFyBihMQAgRhcQAIdoViQGiCuqVQPDoYLocOQOEEB1GE50BChMQQiQhMUCIdkVigKiCejkDzgymPyNsQypngBCiI5AzQAhRUCQGCNGOjCDs0EgMEBlIcga8Adg1mM/iDBgGvCuY/ykFbkMWtmBCiJbGVogaWlAIUTAkBgjRjlhXwFbCp51CVMAd/s9/ivQe4AFgNPAM8HTMPqKcAccCU4E1wP9S4DakvVYKVzAhREtjK8R2ChNQfSlEWyAxQIh2RCECogrcNt0Ib/l3MELAHcBxxGtMUUML2hCBXwafK6wYYAumMAEhRJ4UwBmgMAEhRBS6hIVoRyQGiCpw+8Buw3EsMD6YP4P4EAGIHlrwrGD602C6I5gWLmdAYQsmhGhp2tEZIDFAiLZAzgAh2pFxwVRigMhAnDNgdDDdTrIQAOVhAvsD+wTL73D24x+vEBQ2s6EQoqUpgDNAYoAQIgqJAUK0IzbL2+qmlkK0GHHOgDHBdGOKffgJBKcE05cJ28GFbUPKGSCEqAdNHFqw7mECCqsSoqWRGCBEOzIxmL7e1FKIFiPOGZBFDPCdAbsE0zXONhIDhBAdw3CgK5jfnrRhfVCYgBAiCYkBQrQjuwVTiQEiA25jMSpMoBpnQJQYUNg+ty2YGrdCiLxwK7odsVvVhW7C6kwJBIUQUUgMEKLd6AEmBPOrmlkQ0Wq4bbqoMIFNKfbR0s4A5QwQQuRNE8UAd1QXd+jYXChsRS6EyILEACHaDZsvYBNNSVYkWpc8wgT8oQVbSgworGVBCNGy2IpuBzDY2EOP9N7nGipQ2IpcCJEFXcJCtBKjMPkAJmJ6Wc8AL3nbKF+AqJI8Egj6QwtKDBBCdDS2PmmwKwBKnQFg6vVKI8KkprAVuRAiC7qEhSg6uwMnA5MIe2WW2cBVlCYlUr4AUSX+DaEXGCBbzgArBozAWM+UM0AI0dEUSAyQM0AI4aMwASGKztHADEIhYC2wBNgQLDvU296KAcoXIDLiNxStOyBLzgB35KwRyBkghOhwmigGKExACFEJXcJCFB17N78TuBfYFrw/DuMYmAP80dlezgBRJf4NYQSh5gTZcgaAeSrVUmKAEggKIfKmQM6AXEcUsG2RwlXkQogsyBkgRNGxd+/VhDdfgIcwHu4pGOeARTkDRJXEOQOyhAnsIDxNW04MkDNACJE3BRID5AwQQvhIDBCi6Nge2YC3fAvwSDA/J5iOwTgJBjHigRAZiHIGQDZnAJQmEWzJnAGFK5gQomWxFev2xK3qgh8mkKszQGKAEG2BxAAhik6cGADwQDA9INjOhgj005SGh2ht8sgZAKXDC7akM6BwBRNCtCxyBgghCozEACGKTpIYsBLT8R8O7IXyBYiayNsZ0EfYGG0pMUDOACFEXrS7GJDrToUQjUZigBBFJ0kMAFgaTPcG9gvmNZKAqAK/TWfFgCw5AyAUA6YF0x3Aemd9YcUAJRAUQuRNgUYTqEuYwDDUmxCihdHlK0TRSSsGHAEcgskX8HBdSyTaFL9z7ocJpBUDbJjA7sG0H3NaWgr7AL6wBRNCtCzt7gyAAiq7Qoi0SAwQosgMI7x7VxIDxgfTR4DldSyTaFvinAFZcwbYKJXZwXSNt76wzgCJAUKIvJEYIIQoMBIDhCgy7p07TgzoD15gbs6/rWN5RFuTlzPgN8H0zGDa761325CF6ncrgaAQIm/adTSBQVRnCtEGSAwQosjYO/cOkp8qPBNM7wPW1bVEog34LHBjxPK4BIJZcwb8TzAdG0zjnAFQMDHAjYHtamZBhBBtQ7s6A6DANi8hRFokBghRZCrlC7D8Gvg+cgWIVFwCfBAzIqVLpaEF04oBjwIvOe99McBtExeqDekWrFAqhRCiZWnXBIIAO4OpehNCtCy6fIUoMmnFgK3A05RmaRMiBvu0aLy3vNLQgmlzBgDc6swnOQMkBggh2pp2dgbYNoecVEK0LBIDhCgyacUAIVIynLADPs5bF+cMyBomANWLAcOAiRmOkyuFtSwIIVqWdhYD5AwQouXR5StEkZEYIHJmhDPviwFRzoBeZ3kWMeA3GMMKlIsBroHFfwD/r5jBMA7LcKxc0YgCQog8aecwATkDhADgwgsv5LnnnmPz5s0sXLiQuXPnxm47depUvve977Fo0SJ27NjBVVddFbndWWedxRNPPMGWLVt44okneNe73lXTceOQGCBEkZEY0NYcApzV4GMmiQFRzoAxzvssYQIbMaksAF6IWL8tmPoCxMmYtvNsmoS1LUgMEELkQRNHE5AzQIj68573vIdvfOMbXHbZZRx55JEsWLCA2267jb322ity+xEjRrBy5Uouu+wyHnnkkcht5syZw49+9CNuuukmDj/8cG666SZ+/OMfc8wxx1R93CQGi/TafffdBwcHBwd33333ppdFL72a/jqSQf6RQc4pQFn0yv31RxgchMEDGnjMacExB2Hwo966zzrrBmHw72Bwz2B+axXHmgqDH4LB4RHrNgX73ctZNsE59t8363/5DOaam9j880MvvfRqg9fbMXXKWxt/7PmU1ukfz/sYfxN8tz0K8DvrpVcOr2r6offff//gt7/97ZJlTz755ODll19e8bN33nnn4FVXXVW2/Ic//OHgrbfeWrLstttuG/z+97+fy3Hdl7Q8IYqMnAFtzZRg2sgY+azOgGryBViWAzcQ7Y6NGpHqYGd+chXHywWFCQgh8iTHMIFe4K+B/VJub8MEbP2t0QSESMfYsWMZN27c0Ku3N/rq6enpYfbs2cyfP79k+fz58znuuOOqPv6xxx5bts877rhjaJ95HleXrxBFxvbcJAa0JfbvbWSuuqw5A7IOK5iWqD73Ic78FJqELZgSCAoh8iBHMeAM4BrgX1Jub8ME1gZTjSYgRDoWL17MunXrhl6XXnpp5HYTJ06ku7ubFStWlCxfsWIFU6dOrfr4U6dOTdxnnsdVc0eIIiNnQFtjO+a5N9BSHBMqOwPqKQZEOQNcMUDOACFEW5CjGLBHMJ2VcnsrBqwPpsoZIEQ6Zs2axauvvjr0fuvWrQlbw+DgYMn7rq6usmVZSbPPPI4rMUCIIiMxIBVfAc4EjgXWNbksWWiGM8DNLl3JGeCGCWRJHpiGKDHgDc5808QAJRAUQuRJjmLALsF075Tb2/re3hc1moAQ6diwYQPr16+vuN2qVavYvn172dP4yZMnlz21z8Ly5csT95nncaXlCVFkJAak4s8x8eZHNrsgGRhG2BFuVpjAeG9dkZwBTQ8TkBgghMiDHEcTsGLAaGBSiu2tM8CKAXIGCJEv27Zt48EHH2TevHkly+fNm8e9995b9X7vu+++sn2efPLJQ/vM87hyBghRZCQGpMI2cNwK7QPAJzCOgZcbXqLKuJ3yooQJ2N9va7CdO7RgvXMGTKRUAJiEedhUm8muCiQGCCHyJKMzYBpwDvDvQL+3bldnfgawkuR60hcD5AwQIn+uvPJKbrrpJhYuXMh9993HX/3VXzF9+nSuv/56AC6//HL22GMPzjvvvKHPHH744YBJVDhp0iQOP/xwBgYGeOqppwD45je/yd13381nPvMZbrnlFs444wxOOukk5s6dm/q4aZEYIESRkRiQiqgn7O8HjgJOBP5fw0tUGbdRVrQEghuD7RrpDLAhAi8C0zHt512B13M+bkWUQFAIkScZxYC/Bz4ZfOxr3rpdnPm9MQkFL8Lc656L2JcfJiBngBD58+Mf/5jddtuNL3zhC0ybNo3HH3+c0047jRdffBGAadOmMX369JLPPPzww0PzRx11FO973/tYunQp++yzD2CcAe9973v5yle+wpe//GWWLFnC2WefzQMPPJD6uGlRc0eIIiMxIBVRzoBub13RaJYzIClngC3HRkxHvJE5A2yIwB+BscHxJ9MEMUA5A4QQeZJRDNgnmO4esc4VA2YA/xDMfwL4G2/bYYTOgP5gqtEEhKgP1113Hdddd13kuvPPP79sWVdX5YvmJz/5CT/5yU+qPm5apOUJUWQkBqQiSQzI3RaZE64YUERngN22Uc4AKwY8AdjUN03JG6AwASFEnmQUA2w6sF0i1rnL9nHm1/obEgoBEIoBud8P5QwQouXR5StEkZEYkIoebwqt5QwoihjgOgOgsTkDbJjA48BrwXxTRhSQGCCEyJMqcgZAZTFgjjO/KmLbMc583cIE5AwQouWRGCBEkZEYkIqonAGt5AwoSphAlDPAJqxak3M5fGfA/sF0MQURAxREJ4TIgwyjCXQROqIqiQFHOPNR9xAb4rWRsAmhnAFCCB9dvkIUGYkBqWj1nAHNcgaM9I4d5QywYsDqnMvhigHdhA3glwnFAIUJCCFangzOAJurBcrFgDGUittuA3405bj5XmwTQqMJCCF8JAYIUVS6kBiQklYXA5o1tCCUugOinAG7BfN5J/JzxYBpmJvRAGaoLJszoCnOACUQFELkSQYxYKoz74sB9n2UwWBUxDI3xGtbMC9ngBDCJ9Ple8kll/DAAw+wbt06VqxYwc0338zMmTNLtpk8eTLf/e53WbZsGRs3buS2225j//33j9mjECIW9669tWmlKDxun61VwwSa5QyAUjHAdwbUUwxwH8DvGcy/gnnQVIgwAYkBQog8yCAGTHPm48SA14Hl3rpKzgArBsgZIITwySQGnHDCCVx77bXMmTOHefPm0d3dzfz58xk9OqyGfvazn7HvvvtyxhlncOSRR/LCCy/w61//umQbIUQK7F17J6liDTuVnpj5VnIGNFIMGOm9T3IGNCpMYI9g/uVgKjFACNE2VCkGjKa0827FgDXAUu9zUc6AqDABOQOEED6Z2qBvf/vbS96ff/75rFy5ktmzZ7NgwQIOOOAAjj32WN7whjfw5JNPAnDRRRfx2muvcc4553DDDTeU7bO3t5cRI8Jm8dixY6v5HkK0HwoRSEWUAODOt4IzoChhAo10BrhigM0NsMw71m40ASUQFELkSZVhAmAEgBXOPBgx4EVKRxOoR5jA32OGLLw+aSMrBsgZIETLUpOWN2HCBABWrzbPjGynfsuWLUPb7Ny5k4GBAebOnRu5j0svvZR169YNvRYvXlxLkYRoHyQGpKKSGJBHR/s7wP/JYT8uRQwT8J0BfYSNzHqKAb4zwB6/KX4yOQOEEHliK9aMzgAwdbAlyRmQdwLByZh73jVUuD/ZMAE5A4RoWWq6fK+88koWLFjAE088AcCiRYtYunQpV1xxBX19ffT09PD3f//3TJs2jWnT/CrOcMUVVzB+/Pih16xZs2opkhDtg8SAVEQJAO58rWLAVODDwN8lbHMSMCnjfosoBvjOANv43AZsyLkcUTkDrDOgqWKAEggKIfJiGOFT8xThflHOAIs7zOtSb7u8nQG2HN2UChJlyBkgRMtTtRjwrW99i8MOO4xzzjlnaNn27dt597vfzcyZM1mzZg2bNm3iLW95C7feeis7dkRLogMDA6xfv37otWFD3k1OIVoUiQGpqJQzoNYwAdtZHkZ0p/1NwK+Af82432aFCWTJGWDJ2xUAyc6ATcFUzgAhREvj1iNVOAN2iZhfAzzjbZc2gWDae40rbu8auxVyBgjRBlT1QOrqq6/m9NNP5/jjj2fZsmUl6x566CGOPPJIxo8fT29vL6tWreL+++9n4cKFuRRYiI5BYkAq6h0m4GYx6ab84c6MYBrtfYqnFZwBlnqLAb4zwIoBozAPnAZpIBIDhBB5kVEMsE/kt2Lq6l2AwzFP+a0YsBr4LfBpzFP7z5E+gWBacTy1GKAEgkK0PJkv32uuuYazzjqLE088kaVLl8Zut27dOlatWsX+++/PUUcdxS233FJLOYXoPCQGpCIuTMB2bGt1BrhiQJSwYDvTWY+TpzPgQ8BTwH4ZjmvbcGmcAXmPJAChGNAD7B7M+84AiG7k1hUlEBRC5IUVAwYJK90ErKhss2dNAe4EFgBvDZatCXb3deCuYFmUM6CWMAF3JJdUzgCFCQjRsmRq7lx77bWce+65nHHGGaxfv54pU0wO6LVr1w4lDfyzP/szVq5cyYsvvsihhx7KN7/5TX72s5/xq1/9Kv/SC9HOSAxIRSOdAVH7GpuwLok8nQHnAgcCxwNLKmxrwwRWAxNpXpiA7XNPJfwtXg2mm53txlAqDtQd5QwQQuRFhpEERgETgvmngMOAeYSOgIOD6RrnM7audEXTUcHyqDCBapwBu8RuhZwBQrQBmS7fiy66iL6+Pu666y6WL18+9Dr77LOHtpk2bRo33XQTixYt4uqrr+amm24qySsghEiJxIBUZB1asJdyq3wSfpiAj+1MN1MMsE9u0vRf7XFXBdNmhwnsHUyXEzZYBwkbuQ3PG6AwASFEXmQYScCGCGwGXgjmT4zYzhUD3JAqgPOA9cBZRIcJ1C1ngJwBQrQsmdqgXV2Vr/ZrrrmGa665puoCCSECJAakIi6BYE/EMoAHMU+bZ5IqufOQ1TJqX5CPM6BW98JuwTSpQp8MvOYcdyXGTdDsMIEZwXSZt34jpoErMUAI0bLYeiTDSAKvEnb4bX39GHBoMB/lDLD15HHBIedQW5iAcgYI0Tno8hWiqEgMSEWUG8Ct2NzGzwjgEGAfTCxmGhrpDJgJXEqpAJGGSmLAXwErgA+SzhmwhdIHWfV0Bljr61JvfdNGFFDOACFEXmQIE7D5ApZT2uEHM7ztfZin/k86y31ngK3Px9CgMAE5A4RoeSQGCFFUJAakIipMwO3HuY0fd1i9CaSjETkD7Ge/AFwOnJlhPyMJG31x/dfjgulhhL9BlBhgP78dk83aUs+cAbaheb+3vuligJwBQohaySAGRDkDwAgADwJvwQgGbn3s5gzoolQMcJ0BdQsTkDNAiJZHl68QRUViQCoqiQG+M8AyPuX+0zoDahlNwO63L5imFSqgtKEW13+1T5xGkc4ZsI3S066eYQKWe7z3cWLAWOqMEggKIfIiB2fAPcHHBygP4XKTq46kPs4A5QwQor2RGCBEUbF37W2JW3U8UWECcc4AtwOetsPdiJwB/sgHWfblNtTinAFRYsDKYNosZ4ArBmwFHvLWR4kB78c8JftAHcozhJwBQoi8yMEZsCDhM+7IK6OJFwNccfedGAdaXBU3nDD0DOQMEKLd0eUrRFFxe2YilqikgWmcAXmFCeSRM8B+ttd7nwa30RYnBuweTEeRHCYQ5wyotxjwIKXiA0SLAW8Lpm+qQ3mGkBgghMiLDKMJHBBMX6ZUDLg74TM7CevqUYSONz9MwH2mcBUmN83pMfvczXuvnAFCtDcSA4QoKvbq3Jm4VcdTbZhANWJAvRMIRokZlfCdAX45egkbd0lhAl2UJr52O+f1CBNw28b3Rqy3YoDrzJgRTCfXoTxDKIGgECIvUo4mMAL4k2D+PkyowGagH/hDhUO4SQQrhQlAmDz3tJj9+fWrnAFCtDe6fIUoKvbqTPFEoZPJkkDQFwPegBnP+UMJ+0+bQHA42SrUKDGgVmfAXhj7/82ED2qmOeuTxAD3N2u0MyBKDLCxsa4zYEYwbYgYIGeAEKJWUoYJ/AnGtbUceBpT/50AHE+5a8rHHV4wSgzwnQF2m1Nj9mfzBawLpsoZIER7IzFAiKIiZ0AqKuUMSHIGnAhMB85I2L/7ZDrJGeAfqxL1CBOYjfle7wI+HixzxYAxzr5tzoBuTCPU/W6uM2AjlRuj1VBJDPDDBLoxYgfUWQxQAkEhRF6kFANOCKZ3Ocv+ADyW4hC2rhxHWF+6YQKbMH1235ywJ3BoxP6sGLA4mHaTkLhVzgAhWh5dvkIUFYkBqaiUMyBuaMHxhPGVScPXJTkDuqicYDCOeiQQdMvyf4BZlIoBfc68a/0f5x3TFQPqESJgjwGwBFgRsd4XA/YkbFdPKt88P+QMEELkRQ1iQFqsM8CtF/sI63Rbl0blIn57xDK7nxeALcF8rDtAzgAhWh6JAUIUFYkBqaglZ4BrqYwjKWfAaEor0bycAVnC1V1ngPs9RgHXEyYPhFIxYBOwIZgfR3yYQD1CBMDEwkJ8cixfDJjhrOsj+1COqbGN9mGogSuEqI0UYkAPcGwwX4sY4DqmXMeaDbmKGqU4SQxYSZjIMFYMkDNAiJZHl68QRcU2IiQGJFJLzgA383IcSc4A3zpZFGfAs8H0TZgn6pa+YLoT82R+ffDeFwN2Un9nwE3AJ4DPxaxPEgOgju4At9GuJIJCiFpIMZrA0Zh6biXwZBWHsHXllIh12wkdAVHOgDcR3gctrhhg6385A4RoX9TUcZkJ7EGYeWU0xle8GjPw672UV+ijgX2CaRemh9EL/I+zzX7E16Q7KB1gu4uwck3LcIwkPBXzj27CZKDR+PStjZwBqaglZ4B9Mp4UJpAUBjDOe1+rGFBrzgBb1qcwuRB6MXkE/PW2o78WE0YwgTCh4IA3rZczYD1wTcL6SmLAZGBZzmUCSgNrh6N6VAhRPSlGE7AhAklDCCYR5QywbHTm3arsdUydPws4Cfipsy5KDIgdXlDOACFaHokBLgcCb4xYPhU4GPg9oRjwnmBZFNuB251tjwf2jtl2C6ViwFlEZ3QBIxL8H8KW/DEYoWE/yv/JNcDVwWf2x4whEydI/Ax4OJg/AHgfpoLfGLw2OPOPA68E284C5gETg/cDmIw3f8D8BranZbd1H1H63EMYnLY/5vcaDL7rZu/1OuGNdSbmcefS4PPdwWftd72T8Gb1Bkxv6BXCNLku7rLRwCHAccHnXwiO8XJQhk3OtuOIV8V3UHo3fmswXRbsz/a4hmF6blucbe3N9STMXX5l8JkXgBeJ9vztDuyL6eW9QnlPbjTmN+rC9CKnYvzkO4L9LyL6CcYYzLWxHfMbvEp042YYpicbl3GuG3gzcBTm97a/6/LguOtjPmc5AvM4/nGGfOaVnAH26w5SLgbYvzxzmEAvMJCfGFBLAkF/aEEwp+iLmEtpTsRn7N+zAlPtTcX8FRD+rXabmsSALsIfsNJ/6+GPJrCPt75uSQRd8a1ReQOmY+qnYZgkCosadFwhRH1JESbw5mBaTYgAJDsD3KaK22TYANyGaZq9ncpigJwBQrQvEgNcnse0hDcFr42Y2nMiprUc94R2OWGLeSOmo+RWjCso7Ri7uFJtF6YjF8drlHayjiAMCN4UHHdLsGwxYSW9kwpjw0QwDNPT8Xs7qwjFgG2EQgCYnsybgtcO4J8J7z77Y7xwcTxI2BHeJ9hHHNcRZhzbj3Bw3ij+15mfBRyWsO2XCW/Yp3rb7gocGcw/A3zPWfdx4gOYXwC+67w/jrCnN4j5PQcwd/FHgF8E6/bE9FbBdBTApFLfC9NyuDnYHkxv7giMKDLVO/564N8JA//OxAg+UWwHLnfenxrsswsjsLjZ954Gvh/M74rp3E8PvkcP5pp4AXNOLiHsCL4HI+CA6YFPI+ytbsWIXfa8fQfm3F2K+Y3eQCjWrScUA0Yz1OLp2QU4Bbr7MYJcQE+wixHOthNGw9qxwGswuhvTIvpF+BlOhp4J0Ptfzn52D36XTcAPy8MEet+GudZfx/T2D8WIiM8HG4wE/hzYBr0/Y+ic7x4LvBN6fmy+c48t9EGYOmUU5r/dDfN/jATuAF4tdQZYtvTA0m3msvMvYYAtI4AzYfk9wEsw7cjgu/0PbAuUE3vprt6H8Bpbj7n+15tteJGwDrPqhq2j9sf8prsF23/dKcAZwXeybMecz8+Ei2wjdsxo4DiY8QjhEAjAZN9FtQ9GoLX19+agbJMw5+WvKQ2A9R91dWF+492D8nRjGvKTMD7aLZjz2d4HeoL9LnPKMSk45lqvbF2YutIpPzOCYxxMqQh9NKaV/nvK6Q7WW3F5KXC/s/4IzH+yGnNy7ua8nsIIeZZxwFsoV8KeBB4N5kdg6qJ+ylWhaZh7kq0zJ2LqrB2YcyRKrLTsQWl9YtkefNa9L87A/A+DQRnWOuuc6xkw/509rzYE5UvjtNsLc63uwNRb6wjvc2DqrQFMvbYseHVjFKlNlMbSjMWcB1FPStcSWnCscL0Ncz/bijnPJgafXel91ymUZtqcQHj/fc75nlYley2YdmHq/IOD73Cr9713x/yX9v94FVhAfHtnBOb/68L8Tq97207C/Pabyz9axhGY82tE8N1eCY6f5rPNpgvzO4zA/PbLiBbBU4gB9jlJNSECkOwMcC8P97LagDkV/obyIQbdU6gsZ8AUTF1oz800zoAJhPeuVZSe10KIpiMxwOUxosdxeSZi2S3ALzEVfKVxt26tsN4yiPHNxlWq/s35Mczd42nCGz+YCtd9tPgc8N+YhmPUDX7A2/armBvYGEzDxk5He8d5GdMhfBXzO+yJeYpteyd7YTqCEAotcbi/4YuEybtGYhp37sttKCzFNIj2ImwwvoppVOyktCG4JNjfNJJ94QSfXY9pFK3GNEj3JloU2kH8jd7f9oHgO+yD6Yy4gc9uWV8nFJQeDT63m1OOF5xtd8UIAmB+42cx/5kNG3FvvDudslr3wAbM+TLMK+8MSsWFV4PP7EmpP3sMRuRwmep89ieE19W9wfL5wfedEbx2pbRTBeY3mkip2GNdGovDRd17MHSNdo8ADoTuF0uLMyQGHIQRnjDt7nFBQ23MDsofPe8NY70OY/fRGNHjj+Z9mTNgJuWPZ3YQigH7YgQsPGfAcFPu3uD7d4P5nc4ingOIFQO2HmrEgLhxqbaONMd79XmMGDAaumeYdfYytX2hJYcSnl8+3yRsLR6H8bvuwDQWbSdzJ+V+/v0oD1Q9BHN+PQL81gkTmAjMhRkPmPdP7wozV8PkEzAdiKeCDfcgWXD8nTN/OKE3N4qtmD/Bdr6PCZZvwRTMhoQNo1REnBvsexum0WtfBwWf/Q/nGH9GqZr0CObHn+l8J4DzCIXBkZTWXa6TaDxwelCmAcoFys2EYsDBmB6A/x9AaR2/C/CBYP45zP1mB0aY2x/4F0Kh+2jCa3UH5Q3+7xDW3W8m/pzaDlxB+JueRKmrbBPme9t7wdXOunmUXsd2Wzv/b866d2POmW5Kf4djg+/6/5xlMyltLQ1gKpUu4AnACoZ7AR+K+V4A92FEPDD/4wecdVsoFUh+g7n/gKnzz8ecF1uD927d9BXCC/c4TCfbjgk6gvBa9AWd0ygdbgTM/zID076x29t77O6Y89s9t9YBVxHW3W8mvJZ9IWYz5jywHMpQfVjiiFwXvNz/yzr+hgdl3oMhhxYvEYZmjsS4G6PsXoPBtj9zlh1B/FPtdYRtGDAPCIYH2x9JONYpwXY3RezDigHjCR8oPEnY5pkGfauAbbBmFub+sVfwXbuAGwmvpeMwwrvH5l+bfU7x7+HAxjGY83cEDDzBkDC/sQfu3mZOkz2BQw+ExwIhbdJjwHZY+WfQ/yzwsJN89s2Y638xpu25R7B8F0y94ioaf4FRFtw6axBzv76P8L4Yx2jMubiW8B6yD/CnmPrBinNbMXXkXph73sPBtsMw52scLxGKc664PA7TNpsYTCdh7h82Gc9ITJ0O5n/aC/P/DmLqzwWEN9AphA/txmLu27befw34FeF1ZoXrHsI2uA1Z3o7pd1j+jNLswC5rgR8Qtu1nEm9/3EEo/oL5HwcwJ0YX5o+fGLy6KB0H+N2Y82kJpn04AXM923L9OJgOw9wb3fPa5QlMfQfmmj4S8z9PCrbdgfmfVmD+A3sf6yN0HU+hvEf7m2DfoiISA6qlHgNvQ2njrhL3xSwfpPypzOMp97mDsBVeydY7gLkZWJ723rs8SXrZezElnb1EnqK04ZzEI4RP0yvxM+/9s1EbBfxzyn2Cqfgt9sl4L+amtsZZt5mwwbsA85ToZaLLvwT4OaYD8ixhg3s4pqfoNg5+kKGsdxE+ZduI+W/tzdK1Ty8DFmI66a8E5d4H00iYRuk5vRTTgbQN/aSK+s5gP7tjbgjbML+FJ871OPvvXgf8PJg69AIbe2GkU+NN2Azjg3IMH4TeX3uXzQIY49WQPWuC7xr4OcsSCP4ec3OyDYMXGRIfAPP73GK+zwinDuneZMrdE/y+PWBuoC8Ehd+OOUfsk9gdZt9jiDalbB0GyydGrLDrB8zxXl1u3k97CnqCBsm2oAz/BNw9BW7bgukYDGJu5NMIzwtXBLPqhm3E7MQ8tb6L8vryV5QKlpMwnckJDDVahsSATdDzEOwR/KcP9Jq2zWS/w/sixglkG0+jgjK9jvnt3M7pBkzjxWct5hw93vku64Jt+4J9uh023/G1EfNf9WB+J7ej9YK37WuYOnZTUO6XguUjKb1mJlDq7FqL+V23Utq568XUBfsH8zsJn+i/TqkgcwTmHF1J+X1kuTNvz7vJGCHLda75Yqv9nUZhfivfjeY2AJcQChwuYygXKB7D/A/dwT7t/wvmd7YdQjDC7cjgWP62/oUy3injAKYu2on5z/ysmf8TbD8NIwbafW7AnHeWFZhrYg3RuSbce+oOzO81GvNbjAyOb8Pg3N/LitAHOct2YhrIvhC9Nvg+/kDzj1DqdrDbWhfEesz18lZMvbsP4fk1A3in87k1mPNvF8zvMolQRNqMqbvigsxHENYHj2POywFCV86uwT79hyKnUi5cWNwHDVuCY/uVs8UXRKyAFsWzlIoBp1F6/Q8E+xsH/NZZ/hZCd6O9Vx5E+P+9SPgbHAR9gVDZfwrl141bttER64FNwXedHOGC2dSHcWIB255n6BzcsN0U4U7MX/v2PeGxuTBsJ+wWiN0r94YNgRtlqC+5AXNuHkipoLc7Roxz23l9QZl3YK6pQUxdMjPYjxUDxgKfLC/70D3iPsL6axXhbzCJ8rBat32wd/jdI/kloRgwjVDIiwoP+4MzPythv7thwl4tB2AEzSj6MH+A5b1Eq/tQavEAc87FOX7t0w/LmykVrlw2UyoGnIr5ftsx5557/q2nVAzYDfP7+w+DoDT8eThwYszxoVSoGE38EBcHYc49KwZMi9nWUrchh9oPiQFCNIuNJIsMtjFYKYHgCqIHat9B6VO+rCSJLG4DdCfmpuryOPECVIJdsoQnSKXq9rwUzndvAh4qr9h6AAZghGO97h2ESU4ndcyTnhiwKKKz72V4KnMGPETpTdCnnyFXQUkCwW0w7KGwDdID5SEmEcS1G7Y8CM87naptlPa9t2425bT94WmroTvoANl29VrglhUYF0cabsdY8W1HfBPxgmKUY+F3mEZ8kCxgSAxYBXv93Pw2m4Engs7q5D9S2lB5kdKOWRI2t0kctnEzPCjX7zDX42TCH3It5d9vPuY36KP0ydIGShtRUPrk2cUXhP+L8MQYxFzrUS6rVZjwpXGYk2sN8dfasmD9nRHH8/d5PaazOpvQ9bIB0+B186HcQ9gI3oXyJ1HucZJ+f1/A/D1hyEQ35jftJnRfuN/RDfOxiXXjRmX5H8LG4kqSBf4/OvM25MOGEroMYFwNSS44y0bg/wbzozH/m5sPx+X3GJHqmOC4SzHiUVQoxp2YTHRTMNdH0jnzw4hlz2MEATekpB8j0G/GPHVdGiy3oov7O9yGuV6ixB4o/b/+GLF+JOYccDu23ZjOpBV5rTi+AXOe+Z3g/6JcrILyThIYkTvOGbDce/8s4fXfj/me6ylP/DwMyhK/bCR0vzllGL4SxgXv+1/GfK+XCYUlt475A5H5RDb3m+kuEWLAxq2Y/24HbHP+pw3BtrcRiAGPwlcnwa5bwmr19V/CxuAmMXQ5344RxGdj/vsJGCHH7dxbfkQYDmnPv92Cz/rOobhEOcspDa9aj3GX2BCmqYQ3h5coFQPGkvxgqd+Zn0ZpXbE6OO4qwnxNblntfjcSOgyGY0QRV2he7Wy7HeM6eg3zI+9KadttabC/zYR5u9ywZRe3/nLxncFg7ou+mGDxRctezH9mG1HbKXW5uef6nRgX0J6Y/3UAI569ROmDwUGM4PAy5v/062L3u23DnOPLgnLvwPzX1jWx1Nl2Gea3XUb4IMplDSIlEgOEKCoaTSAVPRHzkWIApR1wKH14NZrye4cvBvj7LRMLYsroM5zShw/2AWfW/cQ9GNg6CEudxpZ9uGu/v71nDokBzjFrSp6/ndDim5WNlDQKhsQAwpEElhK2nSbXM67YdljcP8l2qiphG5JuI7AWohwMSaynsqvrroz7XEvpk89KrKH6hlhSxkrrVEiDffIeR7VC6SClnROfNEKAj23wJ7GCUrEjCZu3oRqWU+4ge5Zo4drmcfBJcw7GsYXysKLthKEYafBdOElECSJx/HfMcr8Tfi+hI+wkzNPT+ygNVQqY4Aija39avr6EtUTG2yedOptWMfR/ujqIrWpvC6ZzX4PxPwgjclYD2xeH25Voe6sIw12OAN6FudZ+7h086vp7nXKBeSMm1MRngOj8Efb8eC5inUtc6G8UDxOe4xtIfmixMHhFsdR7n+SK9c/TtNc3ZKu/flV5kyFuxDRGRmHuZRuIz7vyDKH40kt8yOx2SjNUJrGR6GtyScSydWRzu4pYJAYIUVSsGJD2SXqH4o8c4C+DsKPtiwEuUSF1lTr7vjMgrSvNL0e3t++0FXOcM2ArpW2SVwgfGNv1UCoGuA8CikCcGGDbQFHJsnIjSgwQQog0bCFUXK2YH3Mf7wum6+M3qUiSLpqUQBDMw/zFhEMMHhIst3lJI8UAF9tRjHNXpGGQ5icVjMpz0qkMkJwANu4zomVJyv8phGgmcgakIqoTndYZ4BKVU9JvAOXlDIgSA/J0BtjE9/b+7Cfo9sWA3TCNQSiOs842Ql0x4HkaLAZILhdC1EKF0QT6gml/DYdIcgb4Duyo5TbH9dsxYesQPnCtKAakGU1ACFFodPkKUUS6kBiQkjRiQKOcAdWKAT1EhztUIskZMEjoQnyFUjHAPrRaTSgYvD+YuvmMmonrDLAj6b2InAFCiBaighhgQ9X6azhEWmeA+/DWzX1qQwXOIRz45BZvu7h8jLk4A4QQTUVigBBFxL2xSgxIJE2YQLXOgEo5A/IUA0Z479NgxQA/qb198r80mMY5AyAMwX5LMM0SXlhPbCN2GOHoVSsJw7VHUv7754aNlZAYIISohQY4A3wxwBUA4pwB7j3j7mA7K4j/D2HKBzkDhGh/dPkKUUTcTojEgETSJBC0zoCRxFONMyCvMAEoLVvWMAE/l5B98v9zTGPuTuLFABsqMCzYJiLHVVNwy2uHLe7HNHRtQ7Zu7gA5A4QQeWBvRg0ME1gesy4qgSCEQwxafhixXV1zBgghmorEACGKiHtlSgxIJK+cAc0IE3AbZK4zIWsCQV8MsJ39b2FGfrqHymIAwAKSR1hrJDsIy2KdATafQd1DBSQGCCHyoAnOgDgxIM4ZAGGowHqMM8AiZ4AQ7Y8uXyGKiHtlajSBRKoRA6KGVs8jgWDW0QTixICszgB/xDu3Q29Pn6icAVAqBhQlRMBiG7Ljg2l/MG2YGKAEgkKIWrBiQMwwLX3BtL+GQ/jOALdOTxMmAPB9zIh//0DpvSK1GCBngBAti5o6QhQRVwyIG+NVANUNLfgaMN3bphZnwPpgPqszYANhh7YaMcA6A5LEAEsaZ8CvUx63UWwkTLAFDXQGKGeAECIPCuQMiAsTsMc/JWLfVjQYhWmWlBkVbftEjxaFaFl0+QpRROyVKVdARdyO83DMA4pKzgDfVg/RzgDb2ewPpnHOgNURZUkiizNgf+CbwF4R+4kLE4hyPlQSA14DHokqbBPxn3j1B1OFCQghWoImiwFpnQFxuJ+PdAfIGSBEyyMxQIgiomEFU+N3wLtJ5wzwiWro7BdMF8ccyzoD1sSsj8OWYyvhQ+ionAG9wDPAJ4CLI/YTl0AwizPg1xhnwbcongnFFwPWBdNKYsB0amybSgwQQuRBA8SAtAkEXTHAdwbEsYWwGRIpBsgZIETLo8tXiCIiMSA1fse/m/JOebXOAF8MyNsZECcG2P18NqIsli6qFwNc58BSYCrw5fjiNg23IdtPeDksC6YzIz5zJfACNX4f5QwQQuRBA0YT8J0BbthYXJhAWmcAVMgbIGeAEC2PxAAhioh9miAxoCJpnAF2Gzt830pnne2M+w2dPsJ49acjjtVL6DhY4yxLgysG2Kc1vhgwCrjEWeZX1uMJT5NawgSKjC8GWO4Opm8m/M2HAd8BPhW8P7yWA8sZIITIgwY7AzZicti47y3VhAm4+5AzQIj2RJevEEVEzoDU1BomYJ+i+M4A+yT+VWBtxLHcYQWzOAN2B94WzCc5A3ahdChE3xJv8wVspLxhlyVMoMi4jdw1zvzjmP9tDDAH83//J/BhZ5tdqQElEBRC5EEDRhNw6/b1lAoAtYYJuNvKGSBEeyIxQIgiogSCqYkKCaiUQNC1Udp5v6FjxYAlhI0od782RGAzYWOskhjwdxgL+0eC9y8R7wzwxYlJ3nvb2X2d8tMkS5hAkYlzBkA48sE7gZ8A52BssN8MltckBsgZIITIgwY4A7YT3keSxICk0QSSsGKzP7oOIGeAEG2ALl8hioicAamJyhmQxRlgky35ne99g6krBkQ5AzbErPf5avDqBu4FLgL+hvCB0SjvO/jiRJwz4HXKHzq1S5iA22Bd462zYsDfAadjvt8ZwA3B8lzEAOUMEELUQoIY0E3Ywe6v8TC2fvfFgKgwAdeRlgY5A4Rob9TUEaKISAxITZacAUliQJwz4DnChlOUM2A9lcWAqZhOKxgB4JvOuqicBT3O+xXAFIxYMZrwSY8VA1ZT3rBrxzCBfm/db5z59RiHwN3AHsGyXagBNXCFELXSRaIYMMGZX1u+OhObMXlk0jgDsuQLAOUMEKLd0eUrRBGRGJCaanIGrHLWxTkDosIEopwBacSAU4PpA5QKARAdJgCmYQdGuLBP+t1QATdMoBPEAN8Z8BIwH5PT4W2ESQXd/A2RttY0qIErhKgVN8woQgzoC6bro1dnwtaV6zF14CPAHyjt+Nt7Ta5igIRTIVoeOQOEKCIaTSA1acIEejD9OttZ34RpNI0j3hnghgns5ezHYjuabphA3GgCbw+mt0Wsi0ogCOFTo40YQWA6JlTghWB5Lc6AdsgZAHAK5r92v/9mzPcbiRFMsjZ8gfC6kxgghKiWlGJAfw6HcsMEdgJvJNQ0LfY+lSVfgLu9nAFCtCe6fIUoInIGpCZtAkE3M/8W4BfAs8BDwTK3M95LKAA8R3QCQdcZYO2XUc6A4cDJwfytEesrOQM2EQ6FmMYZMEB5IxBa3xnQH7NNVOyrdQdUnTdAT7uEELXSQDHAdQaAqcL8+0C1YQJ2ezkDhGhP5AwQoohoNIHUpA0TcMWArcD7gvmZwdRt6MzA/AUbME/lo8IAopwBUWLAsZhG3ypgYcT6qASCEDoDNhE24twkgm4CQfc0ievot7oY4IcJJLEaM4Rj1WKAnnYJIWrFdfhFKLR9wbQ/h0O5zoA4bH2aNT+BdQZoNAEh2hOJAUIUETkDUpM2gaAVA3ZS+jTZNpBcMcANEYDoBIJpcwacFkxvJ/rvjEogCKVhArbzHuUM8MME4kIAWlEMcO2s/Rk+J2eAEKLp2Poj5j7eF0z7czhUGjHgDsxoK9/PuO9UOQMkBgjRskgMEKKISAxITVLOABs77joD/I6wbej0Esafu8kDofqhBbuAdwTzUfkC3H3H5QzYRNi5dcWAuKEF0zgDWjFnQFZnAMgZIIRoIrb+iIrbor5hAlGsAz5cxb5T5QyQcCpEy6KmjhBFRGJAamwH3I3bt2LAJmdZnBjgdjhth9wdVhDSDy3oJhD8c+BJ4DCMjf+OmPJXSiC4iXAoxKgwAd8Z0K5hAv0ZPmeFg6qHF5QzQAhRKw10BtyJ6bTfm8O+fOQMEKK90eUrRBHRaAKpsWKA7ey6zoDNzjZxYsBWwph72yGfEUytGJDVGTAH+DFwIKZjeiHmCX4UaZwBWRIItlOYgJwBQoiWJaUzIEvdFsfVmHvG/TnsyycxgaDqSiFaHl2+QhQROQNS43f8XTHAdiaTwgTc7Wxjxz5RXhVMkxIIRuUMuDCY/gwzJOB3EsqfJoGg7wwY7pRxNdkTCLZimEB/hs8pZ4AQouk00BkA9cs3nJhA0P1uqi+FaEkkBghRRDSaQCqGEf5USWKA6wyI6gjbxo59Om+H9VsXTCslEHRDFHYB3hO8v5zKwzhVSiAY5Qzoc7brhDCBAUrLX4k0YsAngXPiVupplxCiVmznuAE5A+pJqpwBIDFAiBZFCQSFKCJyBqTCfVJvO/lRYQK9mESCEN0R9hs7vhiQZWjBDwbHehj4Q6UvQHS+AUgWA2y+gH6MXuS2weKe+q8Dbgnmk5JMFYkXMOV+KuPnKokBU4BvYP67H0RtIGeAEKJWGphAsJ6kyhkA5vuqzSJEyyExQIgiIjEgFW7n3HUBZEkg6G5nnQG2I27HY67kDLApHnqAU4L571You2V7zHJ3aEEbJjAa0yBzhxUE09bcEZQj6an/u1KWqSisw+Rv2FRhO59KYoD9bcdifrMyA46cAUKIWmlwmEC9kDNAiPZGTR0hiojEgFS4nfNqEwhCfs6AXsxTZ4CnK5TdUkkM2BSUz36XSZQOK+jvp1VCANKyhuzfqZIYMNKZ93M1AMqQLYSonU50BgghWg5dukIUEY0mkAq3c247jLUkEBztbeuLAXHOAFcssGLAa6RjW8xy/zu4SQTdYQUt9ul2qyQHrCeVhhZ0BQB/FAdAYQJCiNppE2dAqtEEQPWlEC1KJjHgkksu4YEHHmDdunWsWLGCm2++mZkzZ5ZsM2bMGK655hpeeuklNm3axJNPPskFF1yQa6GFaHvkDEiFFQO2Udphr8UZMN5ZbhtB9ql7paEFewkz/qcVA+KcARYrBrh5A9xhBf39tJszoBqsSDKG8H93qSgGKExACFErCc6AbkJ3WX9jSlM19v44kvA5xRByBgjR8mS6dE844QSuvfZa5syZw7x58+ju7mb+/PmMHh02p6666ipOPfVU3v/+93PQQQdx1VVXcc0113D66afnXngh2haNJpAK2+nfRmlcf1QCwaTRBFxngLXnryNs5/jOgGGEnUh3NIFJhIKB7bxXIs4Z4JfNFQM6KUygGtYRXjpR7oDUYQJ60iWEqJaE0QQmOPPrylcXio3OfJk7QM4AIVqeTAkE3/72t5e8P//881m5ciWzZ89mwYIFABx77LHceOON3HXXXQB85zvf4aMf/ShHHXUUP//5z3MqthBtjpwBqYhyBlSTQDDKGeA20Nyn992UPk12nQH2s/0xx4kirTPADRPwEwi6+1GYgGmfrgEmYn6r5d56OQOEEHUnIUygL5i6wmVR2UqYoHYMEeLFTkrH+RVCtBQ1XboTJhhtc/XqsEn6u9/9jtNPP53dd98dgLe85S3MnDmTO+64I3Ifvb29jBs3bug1duzYyO2E6CgkBqTCigHbqewMSBpa0Ha448QA9+l9D6G9czum8+0/3U8bImD3kYScAdWRlERwVMz8EHIGCCFqJSFMoC+Y9jemJDWTakQB1ZdCtCQ1iQFXXnklCxYs4Iknnhha9olPfIInn3ySZcuWMTAwwO23385FF13EPffcE7mPSy+9lHXr1g29Fi9eXEuRhGgPJAakwnUGRIkBWZ0BbpjAWme97wxwkwfa47tkEQMqhQnYsr0aTGcQ7QywT5ckBhiSxAA3TEDOACFEXUhwBtjwpf4GFaVWEpMIavQVIVqaqi/db33rWxx22GGcc845Jcs/8YlPMGfOHP70T/+U2bNn87d/+7d8+9vf5m1ve1vkfq644grGjx8/9Jo1a1a1RRKifZAYkIq0OQMqiQHVOgM2RKyH2pwB/r5s2f4QTN+EnAFpSOsM0GgCQoi6IGeAEKIFyJQzwHL11Vdz+umnc/zxx7Ns2bKh5SNHjuTyyy/nzDPP5NZbbwXgscce44gjjuDTn/40v/nNb8r2NTAwwMDAwND7cePGlW0jRMdR8KEF3wocClzd5HJEhQm4OQPcMAHbgXeTIeEtG020GLCTMCyy3s6ATZQml7JiwAOYjv40wqdKyhkQjx1esJIzIDJMQM4AIUStJCQQ7Aum/Y0pSc3Ye2RkIK+cAUK0NJkv3WuuuYazzjqLE088kaVLl5as6+npobe3l507S3swO3bsYNgw1RJCpKbgowl8B/gmcEyTy1FpaEE3TGBiMB+V5T/KGbDW28YVG9xhBe3xXWpxBrhixQ7CkQq2AguDeduZlTMgHiuURI0mIGeAEKLupEgg2N+YktRMojNA9aUQLU0mZ8C1117LueeeyxlnnMH69euZMmUKAGvXrmXLli2sX7+e//3f/+VrX/samzdv5oUXXuCEE07ggx/8IBdffHFdvoAQbUnBwwT2CKZvxDyxrjcjgM8CtwAPOcuzDC1oxQC3A22JyhngZ0zeFuzHDROwzoABb9sVMd8jCl8M2BQzD7AAEyZgkRgQT01hAnIGCCFqpdPCBFRfCtGSZBIDLrroIoChYQMtf/EXf8GNN94IwHvf+16uuOIKvve977Hrrrvywgsv8A//8A9cf/31ORVZiA6gwGLAGMIn04c16JjvBL4AXAQcQtjZzpJA0IoBqyL2X2loQbz95+kM8D/rOgN8MeB3zvwOSt0L1kSiMAFD2gSCiaMJqHErhKiWNnIGpEogKGeAEC1JJjGgq6vylb5ixQr+8i//suoCCSEotBgw0Zk/vMHHnAj8O/CO4H3aoQUriQE2U/9+wIvBfJQzwO7LdwbkGSaQ5Ay4hzB3wRpKHzjJGVCKEggKIZqKnAFCiBZAl64QRaTAYsAkZ/4wGtNfchsgpwEXBPNRzoAeZ7ntTPcSljtKDHgE81PvAcwMlvk5A9ycBPV0BrgCgJ/ssB94PJhf7a2z+5EzwGCFmqiEV6Ni5odQ41YIUStt5AxQzgAh2hc1dYQoIgUeTcB1BowF9m3AMW0DxHbkvw4cQGnOgKgEgtYZAGGnMC5MYHEwPzuYxoUJRDkDBinN9ZhXAkHfGQBhqICf++C7GOfA3RmO3c7Y3y7qyb8bJiBngBCiLrShMyByNAGJp0K0NLp0hSgiBXYGTPTeNyJvgBUD/h/wa0wH7j8Jn+pWChPA2c5/4m+xiQmtDpMUJuAPLeiu3044rF0asoQJANwWTJ/xln8HmEu5Y6BTsf99VGdfCQSFEHWnDYcWlDNAiPZDTR0hikiBhxb0xYDDMU/Tv06YiT9v7NOIDcBfYDrbxwBfCpZXSiBoiRpJwPKg9z5uaMFurzwWKwa8RmTbL5akMIEoMeCXwPHAxzMcoxOxv11UGEDqBIJq3AohqqWNwgSS6lOJp0K0Nrp0hSgiLeAMsLHphwMLgYuBT9XpmPZpxEZgGXBh8H6/YOqKAb2ET/f9+PmoEAHLQ977rM4AO7xglhAByB4mAGaIQb98opSkMAE5A4QQdaeNwgTc+18ZEk+FaGnU1BGiiFQQA0Y0rCDlWDHgnmD6Rmfd1Dod04oB9kn8j4AlzvrthI0V96nvNsJOOiSLAQ977+PEgKgEgu76rGJAVmeASEdNYQJq3AohaiXGGdBDeE/rb1xpaiJRDJB4KkRLo0tXiCKSIAach3kifUYDi+NixYDfBtPpzroXatjvZ4AzY9ZZW7771Nydd50BrhjgigSQLAasBZ713rskJRC0ZYDanQESA/LBtbX6ffqKYQJq3AohaiXGGeCG07WKw0vOACHaFzV1hCgiCaMJHI+5IR9dxW77KO0IVYMVA54GXvLWVbvvfYB/Bv5vzHo3TMDihgDEiQHuKAOQnDMAwlCBnZQP61cvZ8B2b951MvhlEOlxhRT/vMzkDFADVwhRDTEJBPuC6ToKmRYoEnt/641aKfFUiJZGl64QRSTBGWCfKkTelBMYCzyPiTevBSsGrAIe8dZFPmXFJPq7k/gy2871JMLkfy5+mACkEwP8znWSMwBCMWA95WGe9XIGbPPm3fdyBlSPO5KE3+EfFTM/hHvdSQwQQlRDTJhAXzDtb1xJasbeR+UMEKL9kBggRBFJGE2gL5hmzRuwZ/DZI6sskiVJDIh8ygp8FHgLcETMereBsWvE+krOAHdoQbdzt4P0YQIQigH9EeuiEgjm7QwYQGJAXuwEtgbz/nnpCkaJCQRBd0khRHXEhAn0BdP+xpWkZpQzQIj2JeohnBCi2SQ4A/qCaVZngBUPhmM6s+sTto2jC9gtmM/iDLBP0sfFrHe/y26Ud6ijcgb4zgA/gaDtZGdxBtwJ/Ctwb8S6Ss4AG/v5coVj+Lidf4kB+bIJc97752XqMAHQ0y4hRHW0kTNAOQOEaF8kBghRROoQJuA6CfqoTgzoI6w0Xgf+N5hagSCqYzWM8Ml+WjHAp5qcAduddZZKYsB2jIshCrufcYQpHdzf8DPA2wgTK6bFdQZs895LDKiNTcAulJ+X7vvEBIKgp11CiOqQM0AI0QLo0hWiiNTRGeDuIys2RGAtpnGwEpgGXBAsjxID3GVxYoDbwEgSA1xb/lZn3g0TGO0sg2wJBJOw+9nFWeaKE/cA/0T2hFAKE6gfUcML+teNnAFCiLrQRs4A67CLbHfIGSBESyMxQIgikjCaQF8wrUUM2CV2q2TcfAGWbYRPyKOeso515sfH7DfJGdDjrE9yBjwfzNuhDqsJE0jC7s/+dhsoTzJYDUogWD/c4QWJmAdzEyzLvyFngBCiVio4A9Y0sCi1ImeAEO2LLl0hikiMM2AMoU2/mc4Av1Md9QTW4ooB1TgDxjjzSWLAH4ClzrJqwgSSsPuxCQ43xG2YkSRngIYWrA0rBrjnZdTwl2Uiltt419MuIUQ1VBhasL9xJakZ5QwQon2RGCBEEYkZTWCCM591NIF6OQMg+gmspVZngBUD/KfmvhgA8ENnme8M2EaY5K8afGdANTkXkvYLpqzKGZAfUSKVPUc3Ep43iaECuksKIaqhjcIE5AwQon3RpStEEYlxBvQ5853mDPCfxPtDC0K0GGAbMbW4Atz9uGECeaAwgfqRFCawOWb9EHraJYSohU5JIKi6UgguvPBCnnvuOTZv3szChQuZO3du4vbHH388CxcuZPPmzSxZsoSPfrQ0fXV3dzef//znefbZZ9m8eTMPP/wwp5xySsk2X/ziFxkcHCx5vfrqq5nLLjFAiCLSYmJAWmdANaMJRA0rCNHOgEeARcG87wyoJXmge4x6OwMkBuRHUpjAFpJFLD3tEkLURBs5AxITCKquFB3Oe97zHr7xjW9w2WWXceSRR7JgwQJuu+029tprr8jtZ8yYwa233sqCBQs48sgjufzyy7n66qs566yzhrb5yle+wkc/+lE+/vGPc/DBB3P99ddz8803c8QRR5Ts6/HHH2fq1KlDr0MPPTRz+XXpClFEYsQAN0ygmQkEV3rLozpdljRhAlHOgL8Cvkz0sIIQLQZA6A7I2xkQlUAwD+QMqB9R52WUMyAxTEBPu4QQ1SBngBAdwcUXX8wNN9zADTfcwKJFi/jUpz7FSy+9xIUXXhi5/QUXXMCLL77Ipz71KRYtWsQNN9zAv//7v/PpT396aJsPfOADXH755dx22208//zzXH/99dxxxx387d/+bcm+tm/fzooVK4Zeq1Zlb+1KDBCiiDTIGTAHeFeGfVQKE8jDGTARM5jCNcDngIOC5UligPt0/T+C8t0XvK9XmIByBhSfpJwBm0k+b5UzQAhRE52SQFDOANGGjB07lnHjxg29enujW909PT3Mnj2b+fPnlyyfP38+xx13XORnjj322LLt77jjDo466ii6u02a8BEjRrBly5aSbTZv3lwWfnDAAQewbNkynnvuOX7wgx+wzz77ZPqeoEtXiGISM7RgnzNfSwJBu5+bgZ8AU1Luo1KYQF45A/YkFAisySopZ4D7NP0FYA/gg8F7a2/Myxlgv4PCBIpPVPiKGyaQ6AxQA1cIUQttFCYgZ4DoNBYvXsy6deuGXpdeemnkdhMnTqS7u5sVK1aULF+xYgVTp06N/MzUqVMjt+/p6WHiRNPSvuOOO7j44ovZf//96erq4qSTTuKMM85g2rRpQ5/5/e9/zwc/+EFOOeUUPvKRjzB16lTuvfdedt11V7LQXXkTIURDcTsfCaMJ1BomMAaw1VQfsML/QASVnAHDgnINOOtcASDNaAK7Avs573cPpmnDBPCOn7czwNKIMIHNiFqoFCZAxPoh1MAVQtRCRJhAD2HoW39jS1MT9r6knAGiU5g1a1ZJMr6tW7cmbj84WGoB6urqKltWaXt3+Sc/+Um+853vsGjRIgYHB1myZAnf/e53Of/884c+c/vttw/NP/7449x3330sWbKE8847j6uuuqrCNwzRpStE0XCvyjqGCUxz3qfd16RgGucMgPKOVVZnQA9wmPPeljOLGODybDB9LGGbNPjHqLczwP++Ijs1hQmogSuEqIUIZ4Ar6Ncy1G2jsQJ75BNEhVSJNmTDhg2sX79+6DUwMBC53apVq9i+fXuZC2Dy5MllT/8ty5cvj9x+27ZtvP7660P7PfPMMxkzZgx77703Bx54IBs2bOD555+PLfOmTZt47LHHOOCAA7J8VV26QhSOBokBuzvvI61/Ht2E8fK+GLCdsFNbjRjgf5ejnfk0zoDtxPNlTN6BHydskwb/GPVyBtj9rs1p/51MTWECcgYIIWohwhnQF0zXUnZ7LzTufaqsvWC/n+pK0YFs27aNBx98kHnz5pUsnzdvHvfee2/kZ+67776y7U8++WQWLlzI9u2lrc2tW7fyyiuv0N3dzbvf/W5uueWW2LL09vZy0EEHZR5eUGKAEEWjAWLALmR3BtgIpJ3Amoj1ccMLZnUGABzlzFsxIG3OAJ+dhMMN1kKjnAHPAp8F/jqn/XcylcIE5AwQQtSNCGeAFdT7G1yUWkkUA+QMEB3OlVdeyYc//GHOP/98DjzwQK688kqmT5/O9ddfD8Dll1/OjTfeOLT99ddfz957783Xv/51DjzwQM4//3w+9KEP8S//8i9D2xxzzDGceeaZ7LPPPsydO5fbb7+dYcOG8dWvfnVom6997Wscf/zxzJgxg2OOOYb//u//Zvz48SXHSoNyBghRNBLEgLxyBowDpmfcl80XsLq8WIDpWI0n2RkwDBMv6T/l948/05m3oQnVhgnkRb2cAb4YAHBFTvvudKLCBEY663ZErB9CzgAhRC0kOAP6G1uSmpEzQIh4fvzjH7PbbrvxhS98gWnTpvH4449z2mmn8eKLLwIwbdo0pk8PW91Lly7ltNNO46qrruJjH/sYr7zyCp/4xCf46U9/OrTNyJEj+cpXvsK+++7Lhg0buPXWW/nABz7A2rWhb3TPPffkBz/4ARMnTmTlypXcf//9zJkzZ+i4aZEYIETRiBlJAPIbTQDgQGc+TZhAXPJASxpnABghwu/Ypzl+tWECeVEvZ8AgplM6POIYojaizkk7v4Xw99ZoAkKI3IkYWrAvmPY3tiQ1496byh4eyBkgBNdddx3XXXdd5Do36Z/l7rvvZvbs2bH7u/vuu3nDG96QeMxzzjknWyFj0KUrRNGwV2UFMaCHbEK8LwYc7MxncQbEiQFRT2GhXAyIGlEgzfF9McDN69rKzgB339HpaUS11BQmIGeAEKIWIsIE+oJpf2NLkguxwwuqrhSipZEYIETRsFfljvJVE7z3aZ6oW5LEgEY7A6ZTKgCkOX61OQPyol7OAHffcgbkS5QY4IYJJCYQlDNACFELbRQmAAligOpKIVoaXbpCFI2UzgDIljfAFwPcJ/RZnAErY9andQa8CXgO+EHE8TcRT7NzBtRTDJAzoD5EPfl3wwTizllAT7uEELUhZ4AQogWQGCBE0YgRA3opf+peixjg0ghngO14vR8TH3+4s439Hu5gKL4tP0kMaMQQTY0IE5AzIF+invzvFkzXEn/OAoqDFULUhpwBQogWQJeuEEUjRgxwQwRsBEFeYkAeOQPiLNdWDLAd/aO9/UHYuFjuLPOHA0wSA4ZTfxoRJiBnQL5EnZMHBdOnY9YPoQauEKIW2iiBIIT3qdgEgnIGCNGSqJkjRNGIGU2gL5j2EybPyzKigN12TcS6PBMIxjkDXvWWT3COG+UMeNjbPilnQCPEACUQbD38MIBuYP9g/imUQFAIUUciwgSsqL/W37YFsPcnOQOEaC906QpRNGISCPYF07WEN+VqnAHLI9alCROYFEyzOANGEX6dVyI+YwUGe/wkMcB3Brg/TyPGSHWdAVvJ19KvBIL1wZ6TwzHn2D6Ya2Yj8DJyBggh6khEmICta5Ly4xQV5QwQoj1RM0eIopHCGVCLGLAiYl29hhYcF0x3xhzX7jONM8AXA1wakTPA7ajn6QoAOQPqhdvgHk0YIrAI0z5XAkEhRN2IcAZYF1JbiQESToVoaRrxQE0IkYUYZ4C1F/YTJkFrpDOgmgSCNkRgI7Au4jPWbWCP/zKmQ7wTeNTbNqoD/i3gUOB/Y8qUJ26YQJ75Atx9yxmQL9swl9FwTIf/wGC5zUeRmEBQDVwhRC0kOAM2+9u2AHIGCNGeSAwQomgU0BkwkrBjn8UZYD+zgVIx4FVgGuXOgDXAWZhGxyrMT2DbU1HOgI9XKHeeuB31vMUAJRCsH5swDpVRhM6Ap5x1IGeAEKIORCQQbAdnQFl7QcKpEC2NLl0hikbMaAJ9wXQtYQLBLGKA3TZKDKjkDLBOhG1EP+GHZGfABsIO9GvAPcG87wwYAP4HmI9pX7jHSgoTaASuM0BhAq2D2+H3nQGJYQJq4AohqsCGx0Xdy1vZGRCbQFDCqRAtjZo5QhQN6wxICBOwN+VqRhNwwwRWBtNKokKlEAGo7AywT2NvwQgCEIoB9vi+Td6KAVtoTF6AJOrpDLC/XSs+LSo6rhgQ5wzQaAJCiDw4F3Pf+gC0rTNAOQOEaC906QpRNCo4A/rJL0zghZT7SSMGVHIG3AnMxFj7rQjhjybgPxm3wy/l/SS+GuqZQPCfgG9jfiORL1Zo2RcjqO0AnvXWjSTiZqgGrhAiI7OD6dEQmUCwlZ0ByhkgRHuiZo4QRSPGGdAXTKsZWrCL6DABKwZUChPIwxkA8AwmxMHux3cG+GKAdQY0O0QA6ptA8FfAxzAOCJEvVqSyjfQlhOeZ+3RupP9B28DVXVIIkRIruo+BsgSCXYT1TFs5A2xdOdxfIYRoBdTMEaJoxDgDosIE0ogB473t3OH7sjoDViZsU8kZ4GL34+cM8MMErDOgCGJAPZ0Bon7Y8/KNwXSRs24z4WU2Dg897RJCZMSKAWOhzBng3htb2RlQ1l6QcCpES6NLV4iiETO0YF8w7Se9GPBN4HXgGGfZKuDvgE8Tdsob5Qxwy+Dut9OdAaJ+2PPSigFPOesGCf/L8f4HFSYghMhIkjOg1cWAigkEVVcK0ZLo0hWiaFQYWtAdTaBSAsGjMeOHHu8sGwD+Bfg66UUF+wS/lpwBLlmdAUV4Ei9nQGtiz8u+YLrYW2/PsQnecjkDhBBZsffSMVCWQNAK5VtpfkLcaogNE7APLtSjEKIl0aUrRNHI0RlgO+MzgulWb727nz2AJ4CLIvaTtzPAigG7UZrPQM4AkTd+bO4S7709x+QMEELUSkmYgBfy18ojCUCKnAGqK4VoSXTpClE0YpwB1eQMsJ3xfYKpLwa4N/cTgIMxQyP55DGagMvrwbQbI3K0Ws4AiQGtg2/Hfd57L2eAECIvSsIEYpwBrRgiABIDhGhXdOkKUTQinAHDCZ9c9pNeDBgTTGcE0yRngO24j6GcLGKA6wywSdl8MWCAsBM2mfihBe/HPJG/N+G4jUJhAq2J+xRuAFjmrbfOgDIxQM4AIURGIsWANnMGKIGgEO1Fd7MLIIQImQus2WLs+q4zwLUwZxla0HbwpwfTJGeA3XYs5eQdJmD3NQGYFlEey28w370IT1IUJtCauA3vFymP1bWiVFmYgJwBQoiMRIYJtIkzQAkEhWhPdOkKURB2Ae4Efv0opvHgOAPsU8uNmE5pGjFgGGHjw9680zgDqhUDosZsTxIDbN6APSLK41KUhpOcAa2Je14+F7E+NkxAzgAhREaSEgi2izNAYoAQ7UWmS/eSSy7hgQceYN26daxYsYKbb76ZmTNnlmwzODgY+fr0pz+da8GFaDd2w1h1pg7A9LWUPMLsC6b9wTTNaAKjI5bFiQFJzoCxznHSOAPcYyeJATZvwBRnme8MKBJyBrQm7nnp5wuAhASCcgYIITIywpna9D+2Lml1Z4DEACHak0yX7gknnMC1117LnDlzmDdvHt3d3cyfP5/Ro8Nux9SpU0te559/Pjt37uQnP/lJ7oUXop1wb7CHvEaJM6AvmNqnmGmcAVFP+OPCBFxnwGhK+z/WFbCJ5EbMDqdc9glIGjHADRPYHrFdUZAzoDVxn8JFiQFyBgiRH5WGu2133O8/xt7Q2swZoJwBQrQXmXIGvP3tby95f/7557Ny5Upmz57NggULAFixYkXJNmeccQZ33nknzz8f1QyD3t5eRowIq8+xY6O6MEK0P74YcKvjDHBHEoB0YkBUIsA0YQLDMI0W22BJEyJg2RzsK40zYHUwneqVpagMYn6DcYQhDqL4VC0GqIErRCb2Ax4B/hW4uMllaRauGDB2W+A88hIIyhkghCgSNV26EyaY5tPq1asj10+ePJl3vOMd3HDDDbH7uPTSS1m3bt3Qa/HixbUUSYiWJY0zoD+Y5u0McMME/M9mEQP84QXTOANaRQwAOAWYR2gtF8VHYQJCNIbDMSL0sc0uSBMpcQbYe7iXQLBVnQFKIChEe1LTpXvllVeyYMECnnjiicj15513HuvXr+enP/1p7D6uuOIKxo8fP/SaNWtWLUUSomUpEwMScgbkJQZEOQP8z1oxIM3TcH9EgSzOgCLnC7A8BCxodiFEJtzRAxQmIET9sPewTr5k3HvyUJiAnAFCiAJT9dCC3/rWtzjssMOYO3du7DZ/+Zd/yfe+9z22bvW7ICEDAwMMDITPBMeNGxe7rRDtjHuDPWglDN8nNAf0BVPbcbFXVK1hAmmcAZOCaVZnwHjCCibKO9SKzgDRekxy5qPOYesMiA0TkDNAiFTYe9jwxK3am5IwAS9nQKs7AyQGCNGeVCUGXH311Zx++ukcf/zxLFu2LHKbuXPncuCBB3L22WfXVEAhOgX3Yhy5A/bbBE8H7+NyBiQla8rbGZA2ZwCYRo/93EZgS8S2ViCw20kMEPXgN8H0lZj1VmArCxOQM0CITEgM8MIEbO/ZSyDY6s4AJRAUor3IfOlec801nHXWWZx44oksXbo0drsPfehDLFy4kEcffbSW8gnRMfhq+yGOt74vmPYH07zDBNLkDHidyrjOgEoigr+/VggTEK3HYmAmcFDM+ooJBOUMECIV9n7UqWJAF6X3cT9MoNWdAcoZIER7kunSvfbaa3n/+9/Pueeey/r165kyZQpTpkxh5MiRJduNGzeOP//zP+ff/u3fci2sEO1MmRjgtBj6gmmWoQVtmID7xD3N0IJ487sE0+g0oaW4zoDdgvk4EcFfLmeAqBfPEJ/00S4fh3dDlDNAiEx0ujPAd+qNbVNngMQAIdqLTJfuRRddRF9fH3fddRfLly8fevmhAO9973vp6uriBz/4Qa6FFaKdSRIDqhla0Hbon3OWVRMmYI+9lsrYIrthAnHOAF9ckDNANAP3vC7JWCNnQEPoAvZvdiFELnR6AkH/fjwUJtAmzgCJAUK0J5ku3a6ursjXjTfeWLLdd77zHcaMGcO6dRqAS4i02BvsjqDzcYgTaN8XTPuDaRZnwDPOsjhnwAjCpxbuZ6E6MWAUlZ0B6ykVAOQMEM1ggDCnRUmogJwBDeHvMHXU+5pdEFEzcgaUMsZLIChngBCiiOjSFaIg2Bvs00EmswO2ho2qvmBaaTSBWZghAC8hfLr/rLM+zhngVwS1OgPGkC7xoOsOkDNANAsrW5ckEZQzoCFYV8ABTS1FsTgV+OtmF6IK4sSAGcCXCAXidqVMDLA3WG80gVYXAxITCKq+FKLlkBggREGwDanlweODbsLGQ18w7Q+mcaMJnIDphP85YYd+NeHT+TgxwKdaMcCWbxfChl+SGOC6BuQMEM0iMomgnAENwXYcy542djD/F7gG2LfZBclInBhwMfAF4PzGFqfhlOUMsDe1oLNsnQGtGiZQMYEgSAwQogVRM0eIgmBvsBucO61tPKTNGWA74PsTdug3AK8G83FhAj7VigG2c78b6UYhcNfJGSCaRaQYIOtrQ5AYUI7NXbFL4lbFI240gV2D6dQGlqUZlDkDvASC7eIMSBQDVF8K0XLoshWiIHQH04Fu2By8GYnpmNvGVb/dJpj6YoDtgI/HWDMBNhKOsZ7VGeC6E7KKAWmcAW6YgJwBolkoTKB52EaIxIAQW9+PTtyqeMQlELSidquJG1kpSyDYZs4AiQFCtCe6bIUoCPYGu214KAaMIgwRcBOdVRIDAA4LphuAB4P5Z0o3Zyel93GLFQPcJ6Vp0oHKGSBaEYUJNA85A8ppdTHAdwbY79HuYkBsmICcAUKIAqPLVoiCUCIGBG9GUR4iAPEJBN0ETdZqugH4PDAT+HnEcaOeyPtiwAZgR2zJQ+QMEK2InAHNw3Yck0ZG6TTaTQzoFGdApTCBdnEGxCYQBPUqhGhBdNkKURBsQ2r7sGhnQL+zbVwCwYmUsxHTkfddAZaoJ/K+GJAmRACinQFpEwjKGSCaRac6AybQ/CfycgaUY3+TUYlbFY9KYsCutDexowkEneVWdwbEJhAE5VgRooXRZStEQYhzBvQFy90OeZowAcuGCsd1n8jb+/mYYFqLGNDrLYtCzgBRBBITCLapM2A88CLwmyaXQ2JAOe3mDOjUMAHXGdCFyQEEre8MkBggRHuhy1aIghCXMyAqTKDSaAIuWcSAlcG0WmfAau/9JpKfgsgZIIpAZJhAmzsD9sJ838MqbVhnlECwHPubtKoY0OkJBG1I3dgBhjrJI53tWtUZIDFAiPZEl60QBWFIDOiOdgb0O9vanAHDnM91O9u6bKxwXLcTvjyYVisGbKM00WCSK8BfL2eAaBad6AywT29HJm5Vf+QMKMV9ql6rGNAL3AD8eY37yXI8iA8TGEs4ak47Yp0Ba4LpmAHKkgeCxAAhRLHQZStEQbCNpLjRBPqdbdc783Z9XDxmFmdArWIAlHbwk/IFgMIERDHoxJwBtsM2gubqHUogWEqeYsCxwF8CX6lxP2mpFCYA7e0OsGKAva+N2UbZsIJbiR7BpxWITSAIEgOEaGF02QpREFxnwJYIMcDtkO8gfPpgQwPchH0rnW0riQF5OgOgVAzI4gxQmIBoFp04moD7hNaPdW4kcgaU4naka00gaP/X6TXuJy2VEghCZ4kBbpiAFURaNV8AKIGgEO2KLlshCkKWoQWhNFkflIoBzwbzOwhDCuJwn8ivCKYjgvLIGSA6gU52BkBzQwUkBpSSpzPACj4jgUk17svnaOA4b1mUGDCMUrGpk8SA4YMwYruZt4JIq4YIQKlgXxbuITFAiJZFl60QBaEkZ0CFMAEoFwN2c5YvCeYruQIgWgwAM6JAvcWAzYSNIzkDRLNIdAa06V2yKGKAEgiWUg8xAPJ1BwwD5gO/pvTccf9Da6jx3Q3tLAZY+7wrco8J1Ph2cAa49+iy67XN60sh2hldtkIUBHtz3T4sdAaMJDpMAMJOt80VEOUMSCMGuDf41YROgrGEnaN6hQm428gZIJpFJycQBDkDikS9xIC9atyXSw/mvjQKGOctt9jv0UligHUGbAK2BPNjgxtquzkDYsUAP0ZECFF4JAYIURCyDC0I4dOHqDAB6wyoNJIAlHbCNxAKCGOpzhmwKmY+Dvs95AwQzcKe3704HWOFCTQEJRAsJc+cAfUSA9wyup3C3ohtfEGjE8SAAWBjICK2qzOg7HqVM0CIlkWXrRAFYUgMGFZ5aEFIDhO4F3PjfiTFcd0bvCsG5BEmIGeAaAU2ELZlh0IF5AxoCHIGlNIKYQLufnti5jvZGbAV2GDFgOAG2w7OgEEgSIGgMAEh2oh2HvJViJYiy9CCkJxA8DlgWsRnoohzBkyg/jkDAF4KpqsTtxKifgxihuu05/xrdiG0bePWvflLDCgOrRAmEOcMkBhg2ApsHAbshDHBDbYdnAFg2gvdyBkgRDuhy1aIglDiDIgIE4jLGeCLAa870x0pjus7A14O5vdOOHYSWcWAzwEXAv+d4RhC5I0NqRljF8gZ0BCUQLCUVhAD0jgD7P/aSWECtoO8lTBMoJ1yBkCYbHWCv0JigBAtiy5bIQpCSc6AoEW4C2FDvd/bPi5MIE0H3MV3Btjkg/vTmDCBl4DrCRMuCdEMbCNdOQMai5wBpdQrZ0CeYQJuGd0nxHIGGFwxwIYJtIszYE0wLfsfJQYI0bIoTECIguA6A2xLalqwbAflIwMkjSaQBV8MsMkHZ2GSCEJ9nQFCFAErBgx1XuQMaAhKIFhKvZwB04J9p3GLZdmvwgRCSnIGBJ1iGybQLs4AG863q79CYoAQLYvEACEKgju04I7ghjo1WLaO8EGlxR9NwE0gmAU3TGAjoTPgjc7ydaTnZeBhzBOEVn8KIjoH60wZ6rzIGdAQ5AwopV5iwHBgd8IcLbVQy2gCZZ3INqJkNIGg3hjbZjkDJAYI0X5IDBCiILhhAjuCltTkYFl/xPZumEA34ROXWpwBrhiwj7NsO+nZgRESfPFCiCLTac4AJRAsJvUSA8CECuQhBlTjDFgJTKJznAEb29wZoDABIdoHXbZCFISSBILBlWkbVP0R21sxYCSwZzC/kzCmLy3WGbAp+PxzhPd1yBYiYJEQIFqNMjFAzoCGoASCpfj/Sy2nny8G5JVEsJrRBJYF03YWA9wEgkNhAt7Qgq3uDLDtizJngI0/adP6Uoh2RpetEAWhZGhB78qM6pBvIHyqPyuYrqG0I58Guw+bk2ArYcMt7thCtBuxzoA2vUvWKgaMJR9roZwBpQz33tci1NRLDHD3G5dA0B9N4JVgmtd5U0SinAFjvQSC7eIMUJiAEO2DLlshCkKUM8DSH/MZ6w6wYkA1Cft8MQDCUAGQGCA6g04LE6hFDBgDLAXuyrEcw1GDBMrFgFpCBaLCBPKgGmfAq866dnUHlIgBwQ/Qbs4AiQFCtB+6bEUuHAYsAs5udkFamJKhBVOKAfbGPDOYViMG2DABiQGik1GYQHr2xOQqmZ1zOeQOyFcMsPuygm89nAH2P+uitOy+GLCB8F7S7mLAANAf1BtTgt5/uzgDNLSgEO2HLtsaGAdcQJjkrZO5BvN0+ofNLkgTGAW8IYf9lDgDvKeR/TGfsc6Amd77LEQ5A5Y48xIDRCdgG+lDHWM5A2Kx1vAR1G75bgUx4DTgO5QPk1cP6uEMWBpM65kzwP/v/NEENpPQkWwTXGfAH4OL6piVZtpuCQTlDBCifdBlWwPnA9cBf9fsghSANm0vp+I7wOPAMTXuJ8kZENcht53/Q4Ppa1UcV84AITrPGVDLaAJux29MjeVwf96iigGXAh8G3tqAY/liQC0ChP2Pnw+meYUJRDkDer1tfGfAJtpfDHATCD402gj7UzfD3mhoQSGgvH4TxUCXbQ3447t3MsubXYAmckAwnZm4VWVso2p7Fc6AqcH06SqOq5wBQsCWYFqUBIL1bjTl4QwAkxAur3IUVQyw50StQ/2loR7OACsGTCQfd4NbRnsu+P+dvWzcJ+LtLga4zoAtw+GPwY35TYQO0nZxBihMoHkcCPQ1uxBV8BeY9uTbmlwOUY4u2xqIuwl2Iiuc+UZYKYuE/b7jatxPLWEClkVVHPdBjDvgXmeZwgREpxHrDICGW59+CLwIjK/jMSqJAb3AP2Hywfi497xOEANsp3pE4lb5UA8xYBWwPpjfM2bbavYLChNwccUAhsF9QVzGFcC+wDrgoWYULEfsfzgBL0RIYkBDmA48AdzS7IJUwQkYJ9mbml0QUYYu2xqIuwl2Iq7anVdcYquQuxgwHLYNlg4RWClMwLK4iuPejbmxf91ZtoFQ4JEYIDqB2NEEoOFiwNuA3YGD63iMSmLA+4HPA49Q/vXr5QzwreZFIc4KXw/qIQZsA14K5vMIFciSM8ANE4h9qtwmuAkE6YL7A+XF/uafI/wNWpV+Z77PXSExoCHMwPzE+za5HNVg64hGOKxENnTZ1oCcASHub9DpYsAuVPcEaajhNgwYLBVY+mM+4zYstgHPVXFciLYuPhNM10SsE6LdSHQGNPhOaRtLZXG5OVJJDJjozL/TW9epzoBmiAF55AzYjnGaQD735yzOgE4NE2AY3OfYMP4IfLvxRcqdHYTtkZL/UWJAQ7D3hka4lPLG1p8SA4qHLtsaqLczYDzwFPDVOu0/T9xGUieLAeMpH3/7EOD4FPtxnQHsSCcGuM6AJZhGX15cBvwIuDXHfQpRVIriDOiiMWJApQSCbuPgU966TssZ0EwxYArwn8DpVezLFQOsMyCP+7NGE4jGTSBIF7zQB4vGm9//QkxHuh2w/2NJ/SQxoCHY+1PWPC9FQM6A4qLLtgbq7Qw4HJMo5Nw67T9POlkMsJXyOEzW4PGU2nv/B/gNlRv2bs4AdmYXA6oJEUjiduC9tL6tUYg0JIoBDbxTuk+C69lpquQMcMOe3orJY2CTpebpDGiF0QRsuZqRM+AM4H3AF6rYlxUDdpBvmECa0QT8BILtPprAcML/zooBdMGJbzWj/fy+WQWrA5EjCkgMaAjt4AzotLxirYAu2xqotzPAXjhTKP7QfZ0cJmAr53GEw2y5FfU0TOMpadQJt3EV5QxIkzOgmuSBQghDUcIE3KH6mhkm4IoBO4GzgccwHRu341fr0IJyBpTiiwH2flpN4r96hQmkGU2g08IE3Hv+AAzVGa+Oar97c2TuB4kBDcFeT70Uv1/gI2dAcdFlWwNZnAETMY2pLI0Ju99u6tsozINOdQa4DYBxhJWc/T26SCcaueu2RzgD0ogBeTsDhOgk7PU21DFuUphAUcQAO5LB3wNHYMKQRmDCnuqVM0AJBMsbZXsE0ylkF0uiwgTq5QzIEiZQ9PZMNbgdnM0Q1hmD5du2OnIGNA/3PKvVHfBWjGPlyBr3kxblDCguumxrIIsz4J8wNsuzM+zfbXhMjd2qGHSqGODanVxnAJjzotd7H4e7btswSpwB64mPNXQt/BIDhKieMmcAhA35Bt4p3YZSEcIE1mEcAU8G70eRX84A/2ctujOgGWEC7vk4zZk/HXgVeEvCvoqQM6BTwgRc0WMnhCf3zsjNWxrlDGgebn1Qa310LnAM8O4a95MWOQOKiy7bGsjiDNg9mE5L3KoUd79TMnyuGUgMKBcDRlD6uyQ9VSoRA4ZT4gxIGtpvG/AoxiHwaMXSCiHi2BJMS8QA28DtQGeAFQPs+PSuWJKXM8Dv+BZdDGhGmIDLHs78qZiHBCclbB8lBozFGxKuCjSaQDn2ut1oF8gZIOqA25GuNYmg/f/6atxPWuQMKC66bGsgixhgb4hZ4itbyRng/gbjCS2m7Y4vBriVXC+lym3SeWIbVzuBnZ4zoL9CGeYA+wEbKhVWCBFLpDOgCQ1ctw5p5mgCSWJApzkDGhkmkFYM6PWmUbhiwBZgZfC+1lCBNM4A+9/aNo8rBoyl9PxrB+z33GQXWDGgDZ0BzcwZcChwWn0PUWjydAbY/6+vxv2kxdYRSiBYPCQG1ECWMAF78mdRxFrVGQDZHBCtTBZnQJowgW1OAyKtGLCZZPeAEKIyiWJAUg8tZ9w6JMsT1EuBb2XYXs6A9DQzTMClFjEA8ksi6HbkkxIIur/XJkrvZe3mDihzBtjWdRs6A5oZJvBfmBGaZtT3MIUlT2dAo8UAOQOKi8SAGqjGGZDlImglZ4DfIGnFMVCrIS9nQMmwggA7QtuyOvpC1J9IMcAm62gBZ8AXgY8B+6TcvhYxIC9ngN/xLWICwS7CcjbbGeCOKFCNGJBX3oAoZ4BfjuGUXks2lt7ez1pdDPDPe4UJBNM615X2QVMjwlHfCzyIcV4WhXZwBkgMKB4SA2ogizPAnvxZwgRa2RlQxEZdPfAr5l2891lzBlTjDBBC1I693obj1L07nIUNotqcAbZhOCnl9u5XGkF5WgQ5AwxRT8HrSVpngP2/k8pk9+WLAbWGCaTNGWDbPdudMrRD3oBvYPL0HOwsi3UGKEwgV+w51YgRKT4EvBE4pQHHSovb5mxHZ8BITP6rn9e/OMJBYkANxDkDZgOXUHrDjHMGvB34SIX9Q/GdAf5v0Ag7ZRHwY59c0aaXKsIEHGeAxAAhGoc7lOfQdd0EMcC9R3STrrPt1i0TUx7H/0p+nW3zvqwLpo1wBkgMCH+TqBFkosIEku619QoTSDuagDuSgKUdxIA/wfz+RznL7HXbac6AvYBzoCEuqh7Cc7oRYoB14oxL3Kqx5DW04HDCOn5CDfvJgq0jkh6KnoDJC/EOGpq3t+ORGFADcTfBbwNXUJrlN04M+D7wr5Ta/yxuw0POgGLiiwGuaBMXJnAA5b+PXbfdcQY8Hcw+VWshhRAV2Ur4cGvoum5yzgBI1+h170FpnQF+Ajf3KdMowq9snQHuaAvu8bK43XxaIYGg+zs1MmdAVELYWnMG5BUmkMYZMIzSkQQs7SAG2O/lfodOcga4OQPuwrRjT7CqYR17FVEhVAdQv7DUIooBeTkD+mLm82A8cAvwHm+5W1fF1aVvDabDqE1oFtmQGFADUc6A4cBhwbzbMYwaTaCP8CKMevLv7rfozgC/QSJnQHSYwBxMJ/8673NlzoCdcC1GIf12PkUVQlTAdniHGllNzhkA6cQAt56p1hngNizdxq/t3HSqM8AtUyOdAfUUA/IcTSApgaA9l9tNDLDfK1EM6ABnQA9hjpKpA8FMA8WAwzHtqf+sw7EmENZtReqU5uUMcM/dvEf3OBE4HfiEt9ytI+JCBU505vtyLJNIRmJADdgT272I9idsVLkXW5QzwL2x90Xs373JT6LYf5Yt6zbvfbvjK7OVnAEzg/mZlGLPITdMYBB4nLZsSwhRSMqSCDY5ZwCk6zRV4wxIIwasJ6x/Njvb1StnQBHvG80KE1jvLLM2+5GE4lCanAFxYQJ7UJsFN23OgHYNE4iKW48dWrANb+CbCYVTy4gG5Axw28+7AEcH80fU4Viue6YdnQH+9ZdnqID9n/yHZb0R2/hleGOdyiSSKXL/svBEKeKHOvP2YhvubOteAHtGbOviOw7SPvFpBvb72acZneoMcBviUUML2u39z0U5A4QQjcW1wgNNCROoxhmQR86AODHA0qkJBN0yNStMYAWwMpi3DxGqcQa8itG3eqkt9DDraALt5gzo9DABCP9Hy4gGuKh8Z4B1Jexeh2O57fN2dwZAvk/hR3hTSyVnwPGU1i0SAxpHpsv2kksu4YEHHmDdunWsWLGCm2++mZkz/WeccOCBB3LLLbfQ39/PunXruO+++9hrr0YMBNJYohTxKDHA7fjFOQOiboz+zbXIeQPsb2Abj0V8wlMP/E69/xTJdwbE5Y6ISiAohGgsRXQGFFEM6KQwgSI4A9YAy4J5XwzIkkBwh7OfWkIFso4m0G5iQKeHCQD8BFgC3B+8b7QzYFdgRjA/ivwt5a4Y0AnOgL6Mnz8eWEQY4+9iy+XWTcMore/9tjOUhgiAxIBGkumyPeGEE7j22muZM2cO8+bNo7u7m/nz5zN6dHiJ7rvvvvzud79j0aJFvOUtb+Hwww/ny1/+Mlu2+Kai1sfejIcT1vuVxAC3oVdJDPBvrkXOGyBnQDlROQNsJSlngBDFI1YMyKGBezDpxleuJkzADylLQ17OADdRXFZaLYFgs8SA1cSLAWmcAa62nEcSwTSjCbjnRTuFCXQRHQpaNppAm9/LP44Ji7WJjkc0wEUVJwZA/u6AoooBRXEGnAHMwuQG8IkKYfLrh6h7oRUDbH0lMaBxZMoZ8fa3v73k/fnnn8/KlSuZPXs2CxYsAOCyyy7j1ltv5e///u+Htnv++edzKGrx6PHmB4BDnGWd5AzwxYBOdQa49FLa2E3lDEgaV0oIUVfq5Qw4EngI+BHw3grb2rqhH9NAq5czIGk0AX9YQYh3BoBxB2wmO63gDGhWAkFfDOgP5m27oZqcAVCbGDAJE66QNWeAe25EjlHfQrj3/E52Bli2BtNmOAPcOmt34Mkcj1XUMIGiOAPs+R5VZ0c5A/x6ym8DTyRMvn4PxnkgMaBx1HTZTphg/qrVq0313tXVxTve8Q6efvppbr/9dlasWMH999/PGWecEbuP3t5exo0bN/QaO7ZIl10yvuo1GtjPWWYvNvekz5JAUM6A4lNJDMiaM8AdWlAI0VjKxICcnnbZ2Nb9Ercy2EbWy8G0XkML5uUMgMqN5bh7V6slEGxWzoCkMIGsYoBNIpg1TOAS4DXgnZT+b502moCfxM7SaTkDLI0UA9x201RgmvNezoBs5CUGRNU/UTkDKjkD3hxMH8WEn4DEgEZS02V75ZVXsmDBAp544gkAJk+ezLhx47jkkku4/fbbOfnkk7n55pv56U9/yvHHHx+5j0svvZR169YNvRYvXlxLkRpGF+Xq+MGU/qBRzoDhhBdIpQSC9iKzD6eK6gwYRvi9Oz1ngMsIypVR2+CWM0CI4lGvMAF7r0jTeLN1gxUDsoYJ7EI6y19eOQOgPLTBZR4mcd0VKcpQRGdAVJjA+4Dj6nQ8+5sMEHa03DAB226oVgyo1hnwpmB6KPmMJpBG5CoilZwBnTCagIs9R0c22Bngt706QQzoobTObKYzwArAUfVPFmeA/T5WxF4CrK2yTKJ6qr5sv/Wtb3HYYYdxzjnnhDsbZnZ3yy238I1vfINHHnmEf/7nf+aXv/wlF1xwQeR+rrjiCsaPHz/0mjVrVrVFaij+ja+HMF/Aa8E0SgyA8CJImzPANgr3jNimCLgXeac6A7ZGrEtyBgynPMwElDNAiGbiDp8H5BYmYDtPaRpvtToDAHZL8ZmsYoA70kIWZ8AHgukhEetaQQzwwwT2xoxr/tM6Hc/Vg23HMq8EglC9GGDbHyOIzhmQ92gC/wDcTF1D0KvCfzJrv6PCBIKZBokBPvUUA4riV/a/fy3tbHtfGQimWZ/CJzkDosSAKGfAPcBjlLaHtxGGRMkZ0DiqumyvvvpqTj/9dN761reybNmyoeWrVq1i27ZtPPlkaeTOU089xfTp0aa0gYEB1q9fP/TasGFD5HZFI0kMuDuYJokBvZTaOZOcAfbXLB+3oRhEiQGd5gx4LWKd7wzoobTB7VbsttEmZ4AQzaNeYQJZxIBqnAH+/ShN3oA8nQFxjeXhwGnBfFTDtdUSCI4gbERPIQz/8DmKdMkio4gSA1YT3mOs0FNtzoBKYQJx/4HtHI0kn9EExpLsYPkb4F3A4Qnb5MlBwOQU2/ntOT8cVGEC9TtWo8SAcZR2REdTDFHKP/fyCBOw9UFfxs+nCRMYRniN+9tNx7ir7HU31AYmdAZIDGgcmS/ba665hrPOOosTTzyRpUuXlqzbtm0bf/jDH8qe7s+cOZMXXnihpoIWDf/EjhIDejGViF+BjaG84kpyBjweTFtJDOg0Z0CUGJDkDMCbLwsTaPMGhBBFxH36DeTuDEhTL9pGln2Cm8YZ4N+P0ogBSQkEk8QAvx6DeDHgWMLOa1SjsRWdAe5/ODti+7cBfwC+WeXxXDHA/uZrCO+tYzAPneOeyLskOQOmRXz2u8ByyvNOjCA8p+KcAWlHE+h35pOELnsdpE2IWQuTgUeAO1Js67fn7Hfo1JwBts4cYR0QDRYDbGc2TzHAum82OsuK4A7wv38eYQI2vXtfxs+ncQZAWF/69YM78lqvs347EgOaQabL9tprr+X9738/5557LuvXr2fKlClMmTKFkSPDv/5rX/saZ599Nh/+8IfZb7/9+NjHPsaf/umf8u1vfzv3wjeTKGfA/sH8QsKb7y5EOwNsZWPbmn0Rx7AX2aJgf2PJ3wqVB7ac2whvDHIGROcMiBtZQjkDhGg+9c4ZUI0zoJowgTRJBG1VYztqUWJA1GgCEI42YIlrKP+pMx8lhLRiAsFKYoB9FLJ3lcdzbwHWd7mUsGMyhvQjHESJAasI/8s9SjfnzZjzzQ/pcLcbQXQehbSjCewkbOy7YsBsjG34OIzYYT/bCDFgX0z5Z6TY1u+Q2eszVgxo83t5s50B9wbTPNvG1gXzPKGNvghiQD2cAfUQA0ZEzPvbHept44YJSAxoPJku24suuoi+vj7uuusuli9fPvQ6++yzh7b52c9+xgUXXMBnPvMZHnvsMT784Q/z7ne/m3vuuSf3wjeTKGeArSz6KY2LSxIDng2mfYQhZu4+wdxc7AXruwOK8ATelnOAsOIsQrkagW1Ap3UGuA1uOQOEKBb1GlqwGmeAFQPGUTkhYC1hArbzEjW0YFTOAAgbabbRFtdQfqcz36rOAP+3d5MlRokB9rep9ru4YsBfYASVhwj/p15Kf++sOQMgPm+A3ZffZnHFgJGkcwbEhQlAdN6A92KEgPd5x690LlczRKKP7dCnEaPiwgTKxIAOEfaHxADHGTAS4wry27S1EiUG2J7FtAzHO5rkpID2nHqZsA4sQhJB/9xrBWdAnFi4v7eNwgSaSyYxoKurK/J14403lmz33e9+l5kzZzJ69GiOPPJIfv7zn+da6CIQJQa4N4MkMWAM4c31iWA6nPLKxn3ibsdYcMWArwbHOShLweuALaeb/biIT3jqQaUwAT9ngJwBQhSXeucMGEFyg3UE4U35FWd5pbwBfn2bxRkQJQZEhQkMEgoCViywY8ZHiQH7YkbYsaRxBhRRDPDL5N6n6y0GLAV+Gbx3Lcvu+dBD/Dll95VWDLDngN9mcZOp+c6ANKMJpBEDbId8AqX3xiQx4KsYm/hfJWyTBluONO2WqDCBYYS/3VBIRIcI+1FiwFcwT+zPjv5I1USJAfcH017SJU59M/AAcF3CNvZ8f5kwPKcIYkClBIJpH8INJ6zDqxUDkkYTSOMMGO5tExUmkLVMzebCCy/kueeeY/PmzSxcuJC5c+cmbn/88cezcOFCNm/ezJIlS/joRz9asr67u5vPf/7zPPvss2zevJmHH36YU045pebjRlFHQ09749/43Kyymyi92fkX8GjCSutlwhul3+Bzn7g/Hcy72RiOD455bJaC1wFXtOg0Z0ClMAG3AvTDBKKcAds7JM5QiCJS7zABSK4b3afO6wnjqyuFCtTiDLCN3UpiAIS/j72nucngfGyIgA01iGo0FimB4D8A/xKx3HcGuJ2CXSm3lvcF0zzEABf3/uqfD1HHchvbvhgQl0TQnpv+E8ckZ4D/5M8ea7izn3qJAX8XTL+esE0abDm6qXypRzkD3OtWzoAwzOTAnI8VJQY8A6wI5tOECswIpgckbGPFgJcI68Aihgm41+kZmLJ+gMr0OfNLI5alodacAXjbtPpoAu95z3v4xje+wWWXXcaRRx7JggULuO2229hrr2jv0owZM7j11ltZsGABRx55JJdffjlXX301Z5111tA2X/nKV/joRz/Kxz/+cQ4++GCuv/56br75Zo444oiqjxuHxICUvAGY6rz3LwD3pK3kDBhNePNZTfxQO+4T9yhngFX23HI1gyhnQJHFgBOAj+S0r6zOgLjRBIYqQtvybPMGhBBFpN5hApBcN9o6YWtwaPvkvZIzIO8wgUpigCWNGPCLYFpPZ8AFwMeq/CyYsn0F+FvKfztfDPC/q+8OsG2Bat1xSX1I+1/5YkDUsdxyZxUDsjgDwJTZlsG6R9yn5Vu87aPaPHY+ixhg2VR5k0Tc37PS/xblDLDLduJ81w4WA2ybNM2T+izY39nWi2swT5GtiyqNGGDPcT/viYvrDEgKE3gX8E8pjpkXSc6AOZj687gU+7HX2npgZTDfl6EcIwk7j2nFgKTrKi5MYDyt00m9+OKLueGGG7jhhhtYtGgRn/rUp3jppZe48MILI7e/4IILePHFF/nUpz7FokWLuOGGG/j3f/93Pv3pTw9t84EPfIDLL7+c2267jeeff57rr7+eO+64g7/927+t+rhxtMrv3FSOAR4G3GAHv9HSF0x3YhpMlcIE7M1njbNtn7edq5RFOQNs5TQloexpcS/urETlDChymMBNwL8C++WwryzOgKTRBMqGFpQzQIiGYzsVQ42unMMEIDnO0487TjsmezUJBG2Z8hADxnjLx2OcawA/Cab1EgN6gWuAq6neyuveQ/3vkhQmAPFiQN7OAAhdHHEPDlySxIClwXSGs6zL2U9SzgB/NAF7fPt9bWd4OOF/vtXbPi9ngGVj5U0ScctRjRhQli8AJAZQPzHA5lNZGkxthzbNuWLruaSnzmnDBP4F+DxwRIrj5kGSM8Bea2mGNLXnuxVTwHy/tLc5t45MGyaQ1hnghgnYcjWLsWPHMm7cuKFXb2907dDT08Ps2bOZP39+yfL58+dz3HHR8syxxx5btv0dd9zBUUcdRXe3qb1HjBjBli2lUurmzZuHwgCqOW4cEgNS8I+YG6s/FIZLXzC1jcm0zoA1ZHMG7EN40eTlDDgWU+nNr7RhDEnOgONIN052o+gljJPsy2F/9r9dGbFOOQOEaC36g+lQQ7EOzoAkMcDWCfY+4maQT8LWwbbhmleYgDuaAJQ/4Y3LGXAKpk57ijAvTpoEgtWIyPap0jCqr9Pde6h/v04KE4ByMcCWoR5iQJwzIEpoySoGuL99kjNgJOW/SQ/RYkCrOAPcclRyNSaFCXSyGDAyEAOGE4qReY8EYc8LO9z2Y8F0VYbj2XMyqzMgyv1klzVixAsIv7+9nv3RqtxtkogSAyC9Lb+SGFCNM8B9+DlAWGc0M1Rg8eLFrFu3buh16aWXRm43ceJEuru7WbFiRcnyFStWMHVqdA9t6tSpkdv39PQwcaI5o+644w4uvvhi9t9/f7q6ujjppJM444wzmDZtWtXHjUNiQAWOAd4ezI8kbAj4N3p7wvpPdHalcphAfzAflzNgG/BqsO9ujLWvi7Ayq8UZMAe4HaPgHlPlPqJyBvQG+74H+E4N5csb10aWR3yq/W9XRayLyhlQcTQB28qSM0CIhtMfTPvsAi9nwO6ka2z5ZM0ZsNGbVhIDbP1h7bK1JBB0s8BXGyZgQwR+SXIemTycAe5vW23D0b2HphUD7EhAjXQGVBsm4O9raTB1hz8cGTMPlZ0BcWJAHs6ANE+XmxEm0B9MJQYYrDNg0uawY1EvZ8APMHXMxcH7LGKAPSfHEK3xjiY8HyqFCdhzvi/FcfPA1k39wTSq0+3XX1G4YsB2QkE4bf3p1vd5OQPcMAEoxogCs2bNYvz48UOvK664InH7wcHBkvddXV1lyypt7y7/5Cc/yTPPPMOiRYsYGBjgW9/6Ft/97nfZsWNHxf0kHTcKiQEV+KL3fnIwzeIMiEogGBUmkOQMgNL4UbdxWK0zYDeMEGBFhWqt/XHOAPsE3o9LbCZuo6bWUIZhhBXdRsIK1d1/UpiAnAFCFIv+YNpnFzjOgEnAc1TnoMoaJpDVGWDrj1eDaS3OALfRW0kMiHIGDAdOC+Z/QfIIM3kkEHQb9NU2HJOcAXFhAn8MprtR2ukpWpjATsxIEC4vB/sfRSiEuA149zcYhhm2zVIPZ8AIwvvheErPp2qcAV1kE+2qcQYscz7rX7dAeHK3+b3c/rf2d5vqNITqJQaswwiNrwfvq3EGQLQ7wLYR1wfHSQoTaLQY4Cdujep0Z3UGQMR9rwJ5OwP8MIFqylQPNmzYwPr164deAwMDkdutWrWK7du3lz2Nnzx5ctlTe8vy5csjt9+2bRuvv/760H7PPPNMxowZw957782BBx7Ihg0beP7556s+bhwSAxJ4A6ZRs53wxLQ3zricAVGxnqO8dWPIFiZglTJbhgmUVmLVOgNmB/uy+6228eKKAa4zwFYCRUommKcY4FZ4mwlvGlG/AZjzoMd7b5EzQIjmUzakkZMzYDrmep5JdrImEMzqDLB1mXUGjKRy9us4Z4Bt9G4lvPdY0jgD5mA6Aasxw4vZ+jAqU3vezoAk628S1TgD1hHGLu/rrC+aM8APEbDLbGd2RjCNEwOmUH7+pnEGuAkEKzkD3LbPMMKHLmC+W1RHzC2Df17egvl+aUMUq8kZYP/73ZAzAEJngCsG1CtMwBd/qnEGQHR94YYIQHKYQLOcAfb6ySNnAOQvBkSFL9jfKiq/hx8mAMVwBqRl27ZtPPjgg8ybN69k+bx587j33nsjP3PfffeVbX/yySezcOFCtm8vrbW3bt3KK6+8Qnd3N+9+97u55ZZbqj5uHBIDErDDojyAiX2Eys6AJDHAVlhjne3d0QTsMoubmA9KLw735thH8tOmOOxFZsccHkZ1J4RbTtcZ0OvMF4VaxIBdMMli9gneuw2mzYQ3Dfu0zA8T8Bs0Uc6A7R3yNEGIItIfTCcQjN3uhAnYa7SaurbaBIJZnQH9hA3lSg3juASCcckDIZ0YYEMEbsP8fG5HMGmsaWhemEAWZ4D9rlsJx+i294RuksffTkMaMSCLMyBKDIDyUIE4McDNFwDxzgB/NAE3TKCSM8D/PtO891Hnsvtf+53D4zDtorRJgqsJE3gumLqhQ5FiQJsL+36eKFcMGEN19WUceYgBlZwBcWJAkjOgUR3WdnAGrKWcooYJZOHKK6/kwx/+MOeffz4HHnggV155JdOnT+f6668H4PLLL+fGG28c2v76669n77335utf/zoHHngg559/Ph/60If4l38JB7g95phjOPPMM9lnn32YO3cut99+O8OGDeOrX/1q6uOmxa/ThYM9CdcQnqBWDKjkDLAGjT2cZa9jbrxTCe8T1TgD+iivxKYAL0R9iQRsmd14917Kb9yViMsZEDdmcTNxxYCsDc/3Y4aR2RP4KGGDaQBzv7c3jTWY/7iX0sau/59FOgOsGNDmDQghikh/MB2Gafytc8IE7DVajbhZ7wSC7lOVVRgXw0TCDl8UlZwBtYoBdkhB11g5gtL7iy3DluD41XSgm5UzYAAjBryZUAxw6/hK95d3YkahecBbniZMIEsCwSQx4HgqOwNs52gD5n/OMppApTCBXb2pJUoMeN5b1ufMd3nr7H+U9nyqJkzA5owYRygmdbQzIJi6YgAY58Qy8sH+9nk5A6LqCxveah+SxYUJdBH+xX0pjpsHSc6AahMIQmPEAFs/rCXM3fUapk/l1h/bne2gdcSAH//4x+y222584QtfYNq0aTz++OOcdtppvPiiGch12rRpTJ8eBk0vXbqU0047jauuuoqPfexjvPLKK3ziE5/gpz/96dA2I0eO5Ctf+Qr77rsvGzZs4NZbb+UDH/gAa9euTX3ctEgMSMCehGsJKwTbaKiUM8A6CaYTntS2wtrD2XaAygkEo5wBeYgB9vu5mfB7qF4MaHdngL0Z2H3Yitk2kG3juT+YjqDUaeH/Z26lPdRosldkmzcghCgiWwk7pn3AOidMwNbHvZjrOoteV+8Egm4dvBJz36mURLCSGOCPJACVcwbsCxyMadDd4ZTJL6dfBvub15ozoNowgWpGE3CdATZMwG24Jn2XPTBW9lcIOx+WRoQJQPmIAnEJBO39bglwOKVP8SxZEwi6+Y/cqcUXA94A3AD8B3BlsKzPWe9+f7ftkeYeP5LoTlUc9r69EnOv7yMMHZIYUF8xwP72fj3UCGeAHybgXt99KY6bB3k5A/q8/fR7yyvh/hY9GGHE5iVxhyh1y2WX9TvrnsKIAW7OAPvw0/6nbwa+nbJczea6667juuuui1x3/vnnly27++67mT3bT0Fbuv4Nb3hDTcdNi8IEErAVxVrCJ/1xzoCo0QRs/ObBwdQmO7GVzWpnW6jsDHDjWX2VspokgrbMvjMgK52SM8De2+0Nx95UfDHA/q9+zgD/P3MbnX3BtN/uVM4AIZpCfzDtg5IEgm6dn7VOa7QzACo3jPN0Btjy2cjF31Ha6IsbUcAVA6AYown4/09cAsEBQqu4dQb0JXzOZX9M4ytKsMk7gWBaMaCSM2BJMB1Jcs4AN2GkLUOcM2BssE0lZ8A5mKGd/8JZ1ufMu99/fMzyONIIKy7u02nbyZUYEHQKd0SLAXngPlzxnQG2bZ3mWJWcAWnDBNzru1FPr/PKGeA7LMpy5VTAvx8l3Rt9Z8ByTJ20jtBdEyUG3IC5dN5LmJBW1A+JAQm4zoDXgvlKzgD3ZvBEMLX3BNtIs/tY403dG7zbwLENqX6nXFHOgKzY77ea8H5VTWOsqDkD9ga+QGklXkuYgP1PbCPbdwY8GUwfDaZ+zgD/YnMr7SHb1ijavvEgRJHpD6Z9EJkzAOonBtSaM8AVA9I6A+JGE6gkBmx3thkV7G//4P0fKSVuRAFbJzZbDMjiDEjKGeAePykHj+1wJA23WG9ngHUSzogoi/sbuM4AiHZwRDkD3Pub7wzod+Z3obIz4Khg6rZz+pz5uNw8ac4n/9iVrm336bQVA2YF05JOaoeJAQAjBsrFgLySCLrnky9KWjGgm9Jr8P8CX/O2zeoMiAsTKIIzAMrb2WnEAD+xZ38w3bt800j8+1GUE8B/b7dZiQklezvh9eIKh7a+WghcFcxfR3TOht2AXwXb/gY4K13xRQQSAxKwlco6QjEgbc4AgMe9bfyx6H0xoM9Z515cUQk18nAG2OOtdY7RTs6ATwNfCl6WPJ0BvhjwWeAAwEb8+M4An1hngFwBQjSN/mDaB7HOgKx5UJLCBNzOSLXOAD9MAKp3BtgGcpQY4D7h3UZpMqhdCRuTS73P+VZivwx5iQHVhAmMpvR+mlYMsDkDwHzvYZSLEXHfZ8+EbdKIAf75V09ngG3zvBCzHkrFAHs/TBIDdhKeO7tQLm74Dzfs+omE36vPWV+LMyCNy8LFvUatGGD/z052BgCMdMSALE/rffYArqC0vWZ/9wHKz+mthPWVrfcOB/4K0w50/9MihQnshXloeEHK7aPEAL+z3UPl+G8/seedwfR9wGEpypHkDPDrJt8ZsA0zpLk70kyUMwDMw7wlmLC3KyLKcQpwEmZktBOBf0xRdhGNxIAEXGeAHybg3zBsYyLKGWB53Xuf5AxwLy7fGdBHvs6Atc4xqmmMueEMUc6A4eSfnOJfMRWYb1d0sTeh92JO9IlED3mSFnusCZjfyf5+9maxA2N7cp+CJR0j0hkwkrZvPAhRZPqD6QQoGVqwHs6AT2KcWe8K3ufpDKh2NAF7b6mUM8A2yq34MJVQDPDz17gisYsvBvQG+5mRUG6fWp0B/r2z0mgCtsxbMaGAA8E2e1LeKYir/10xIE4gSQoT8KkmgaANYxyF+c/dfUTlD+h3lvlPH6MSCNpzdjvR38Vt9/gdcvsb+IKUG1rh/tfNCBPYTNhhtAy1/7oIW9dtfj93O28jtoVigG3/ViMGfBa4BJOo2RI3koDFr/cOdNa550pSmMAIwvOrljCBmaT73idiwojPSbEthOKIW7/a69P9XpXcAX4uj98A/4WpM36CidE/L+HzWZwBfv4ON4eM21aOEgM2Ax8J5j8GzPX2bQWa1733IjsSAxJIChOI6zS7FZXvDPDFABtb3h9MewkvYvfiisquaW949sZbS86AfsILMEkMiOt4xzkDouKZ8qAX+BDwFpLH/LY37WnBtnt466sVA8DccOxvvtzbzlU75QwQorXoD6Z9UBImkNbqH0XcZ48NpjaFUB45A2znvNoEgrZztJpyXDHA3jNs/TeNeDEgizPgfkyoVdqGnVsv10MMiBOy7Sgy9rvuE3H8NM6ALGLAxohlUJ0zYAvhrWYUpeel+xvY8rniUNSTQX9owdHeex9XDLDnnH/ri8qHbe+7fc6yuDCBapwBacMEXGeAZej/cVvWbS4GQPgf922GCcHFbtu/1YQJvCWYuv9lVjHgUGede10mOQPc5N72/IwLE3DPrT5nfk/gMWB+TDldbLkq1e+WGcH0ecrr1CxiQNQoH5/EfOf9gQsxyToPivm8XzfHOS/cckV19t3vEFdf3Ql8J5j/N6K/56sxxxbpkRiQQJQYsCulNz4f92b9pLcuLkxgI+EFYm9M/kgCUNpItRWTjeOrxhnQF0xdZ0Dc95qEafT9W8S6qNEEoLTCyFMM2IvwxE1q8LoNmnMpFwOyuiB8McDGNb7qbef+lmmcAWOcsihngBDNpT+Y9kHdwwSs08w2Smt1BgxQfQJBm6DL3oMqiQG2nrP13z6E96E4Z0AlMaAbIyiMI338aq1hAr6QXskZYLH3OjeJYFoxwL0XNUsMgPB3H+WVI0oM2EL4naPEAD+B4BjvvY87vKA95/wn7WnFAPd3rneYQFQCQcvQ/+M2Fjrgfm7Po737zXQzYQhNVmfAJMKk2+7/mlUMOMJZF+cM8OsLP0QASsME3CEs/XPO/uXHYc6hpAdVfrnSCJ+TMb/BTsx1YX/zPJwBYOrxwzAhC9Y15LeZLUnOgDgxIMoZUClMwPJ3QZlmYUIH/HLYB60SA6pHYkACrhiwmvCmOpF0YsB6Sm9mcWKAO/8hzNMie4NyL4woZ8DSYOrf0NLgfr9KzoBDMd/71Ih1bkPUvdBdJTVPMcBtJCY1eN0GzbsJh3+y1OoMiBMDXLUz6nvb39qWr89ZvqkHOQOEaCL9wbQP6h4mYMVMW1fWmjMgSwJBe/NfRdhfmUT4lHZN2SeSxYBjgun6iM+mTSDo4ieRi6PeYQJJzgAoHV6wz9smb2dAXJhA1L3M7idJDLDnWRoxYCvh/2jPUzfxcFZngG0b7Ut4zi2N2calkjOg3mECUQkELZFiQAfcz+15MT1opC4fHtZDWcWANzvztYgBhzvr4pwBfn2RJAZAaT3sX9v2vDsimI4lOYzVPX4aMcAmKV1GeUgulJ63aZ0BvlD3Mibpos3yH9evqCaBYJIzIC5MwLIWuCiY/wzhf2u/p8SA2pEYkIDbWR4ktF9OIf4m7yv3bqhAXJgAhDe9L2ESa9jPuZ3rqASC1rFQTWfbDROo5AywF9nkiHVROQOgVAzI8yKd4cwnNXjdCrGPMPbIUu8wgR6iLzD7v9vyDeULGIaRnjvgSYIQRaVkmKUWcwa4YQJJQqlbl20lrMP2oPowgTnB1HcFQHpngEva0LdaxYBKzoA4McDe69wRBdI4A3ooFSDydgZciRm/295XksQA+39mFQOiPm/xxYA4Z4B1Th7slPV5b5tawwTyHk2g29lnKmfAIB0lBuwV9JxfGxa2d7OGCRzvzGcRA9zj7Uap4JbVGfCSs2wL4bWYdG7ZYxyRsH+fasQAe43k4QyoFMLTF7O+Hs6ASk6mW4AfB9vd4JXD3qvceqgbk1vsEqrLhdZpSAxIwBUDoDSJYBpnAIRJVHZS/rTEff8+4MvALym9wbg3x36nXLZstuGXtXHaRVhRpXEGjHLW93nr3It8J9EVZ57OgBnOfJowgYXB1M+SWkuYwCQqOwPi8CuuvmDab6/GDmg8CFFU+oNpH9R1aEGb1BTCutI2bmrJGeA+kYt7MuWWZwfhk7A9SS8G+M4Aa+2NEgPinAF5iAHud6wmTMB2zK2okTZMwHcGpBUDplHa8MpDDHD38eeY5GlHBu+rEQOi8v1spfx/iho5wC6z52xch+OpYHoQ8c6Alygnb2dAljAB99zYjGl/uZ2boY5qh4wkYBkSA4LEEq/V4Aw4wZmv1hlwuLeuFmcARI8o4F/bfcH0iIT9+2QRA2YEU1vf1JIzICpMwCUqqblLo50Blo9jrrfZmN/Ddwa4x5uOeQD4+Qr7FAaJATG4CfCsGOAOL5gmgSCEYsCmiHWuGPA0JhbmTzEV0mTgrZixOC22HN2EHVFbpqxigGthSpMzIGqoIYuv+NkLvNFiQDcm0cgHg/e2zN/2Pm8bfVmdAW4DOo0zII5YZ0CHNSCEKCL9wbQP6uIMcBP12Ruw7cDYOsF2+qwt3G0sReHWwasJ9UTfAm3xQ5pdMWCoPor4XJIzwFKNMyCqUVqNM8BPXOvSFbPOHsc2srM6A2zOgKgwgah7zJ7e+7zDBOw5ZL9HGjFgNKW/TS/huZnGGeB2DrI6A2YR7wxwxQB7TtcrTKBSGwjC77QT870GKX0YUOYM6JB7eZkzYHh1zoA+Sh/aVCMG7EayGJDkDNgrmMaJAUnOgD6MsOiGN6UVA4ZT+Z5inQFLg6nrDOgi+reKIyqBoEt/MI0TA6pJIJg0mkClnAGW1wj7QWModwa4x7dhwUsT9idCJAbE4F7ENouuO6KAPbH9ut5X7h8NpmsxNxD34ot68mJZCfyvc0wwlaC9UPZytoPsjVP7/bZhbuiVnAHu/v0YSz/ZoZ02QgxwbzQnAh/GKIEQNoYeAu5xtrMNjlrCBCYRNkriEgjGIWeAEMWlP5j2QWzOgDzCBFxRtZIzwF0XhduQ2kHYkY9riCeJAdU6AyxLIz5XyRmwnfJOazU5AyC+Af4dTKP+R5gkXxZ7P1saTOPEAL8j7jsDplEuYETdT30xIO43qTZMwJ4nflx/FHHOALsMkp0Bfsffbhe1jc/SYN0owt94qbfNasKOmA2drNdoAmlCLt1hBS2uk7PjxYDgInmtOxQDJpB+aOk3UdopqdYZcLS3Lu1oAnHOgKgRBaLCBJJEiCjc9ZXcATOCaZQzwD9nq80ZYKklTCCLM8AV4NIkPIVSEcR+z37CW7X9bn5YhUhGYkAM7hjy9iRzwwTcuDEX/2b9R8yYqZ+I2D7qyUslXFUMqncGuPkCILxAW9kZcEQw9Z+MbCYcmgTCBkctYsAs5/MrvO0Gia7wLPYmOTzYx9CTOFsbSgwQomn0B9M+qGuYgFuPxjkDthHWJWnFAKicRDBODDiAsN6MEgPcjl0ezgBX//TryWqcARDfAH9rsO17MOLwid5xlgZT/15qf1v/3m7vc6sJ78sHetukEQPydAYMd/aXxhkQl0DQ/XwaZ4D7xNx/shcnBuwEFnnb+efSJsJz+f5gav8v93/OYzSBNI7BqA5ppBhgT+xOFQOGm/ZtJYeSjw0RsL9vlOOlkhgwCXOtQ3jOxDkD8g4TOMJbVkkMcM/VSqFgvjPA/uYjKT9nk8QAd9s4MaA/mFYKE4jqN/j1Z683rcUZAKUCpptjxy6XGFAdEgNicOPpLW6YgD2xK4kBAFcAP41YX4sY4Jepm+TMpf8Hk4/AVsp+PgQ36V0UacQAexFHOQPySiDYQ+lwJ1FigD2WKwb8V1C+14HFzr6y4P6+hwTTVURXXm6Ft5nS/r3byB6F4wzosKcJQhSR/mDaByViQNyIAGmI+qxbd/kJBN37Spq8AX5Dq1ISQV8MsB0aa9HdTmkWbUsaZ0A1OQN2UF6PVpMzAOLzBuweTG2mbPsUL8oZsDemI/Eewv/Ov9e7dbzdpy9MRN1j/OG6sogBm733/u/qdgLsfDU5AyC0H9t9pwkTsM6UqDJG8ZQz74oqlk2YnD/bMQnEwJwXwygXA+ywb+O95ZWwbaJcxYAOu5cPjTIRnGyv9ZTmyUobKmCTBy4IptU4Aw7AnCObgfnBMteO79fF9v/uIaxzqg0TOMJblpczoItwFC0/gWBWZ4C7baUEgpXEALtdmjCBpJwB1YgBrjNgI6XDpILEgKxIDIjB7yxDaZhA3NOCuIoqan01YkC/936lM5/UQP048A7gF5R2QO33qyVMIM4Z4FY6eTkD9qL0pHUb1LZxNyLYxh5zM+Z3n46prPuD5bU4A+KSB1q2evNuw3EdYRthNHIGCFEk+oPpBOCvt8GCG6BvM/Q4g0zX4gyICxNwG0TuE+A0YkC1zoCdGCeTbfzuH0zj7k1ROQPWU3pfSxIDkjq+fiMwzzCBXQnvYbc7+x9L+Lu6OQNOB/4E+Atn//693q3Tn/HW2ftq3s6AQa8c9jyx+3A7AVlzBkQ5A/yniJUSCA5QXu64DgeEeQPAnHNRYsA5mP/KdhDHEy0U2d+62jAB6/BTmEB2fMHnteCisS7INEkEx2ASwwH8OphGiQG+IGaxdZ796e8jbLPbOiGqjWzPFysWbqV8GPC0YQJHBPP93nHjSCsG7I45l7cRnm+uM6BaMSAupDUpTKDL2X+UGGD3v817nzSaQDVhAq4zYBPloytIDMiGxIAYosSAqNEE0jgDXOz2bocwC/7NMq0YYNcdB/yQcjGglgSCcTkDXKJusLMwjokjI9bFMSOYWqXWKs4jg/3Zefe3sDeP5ZhGb9J3HYtRp7si1kU5L+LEAL/Ccxu6VpwAzxlga8MOaUAIUUT6g+lw4JrtMPcl+OhC6HHulnk4A9x6dBilImutYkAlZ4Bf1VgxwH7FuHw2Uc4ACOvBrZSHTbnbZnEG7EI60SWNGGCfxq8kFCumEoolGwk7LqMIhYgJVA4TgNAZYLGdibzFACg9N+x90P6u7jmS1Rngn9O+WyDKGeDnDKjVGeDmLwJzn9zB/2/vzcPlKMu8/0/2PTlAICGyBEwIe1jCIkICjFHEEUQdUFQQN4RxhlHnctCZ3+DrOjO8oiIgr1uGYRxGRKIoBIKyBcJi2AkQMCEBQnJClpOF5Jyck/Tvj7vu1NNPV1VXVXefrnP6/lxXX91dXV1VXV31LN/ne9+PnE9XdHqnd/wQnoMsYQKjCa+fvM4AdxS5YjaBFhH2K8SA4CRmmVHgJOS/eIUwKWeeqQWVeymfjhvKr+dt3mdxIQKQLkzgbYRtUBWuksSAwZTfr0ligHZsXyO8v/I6A9x8AaWYdTqC5yhnwAjCeiLJGaC51nrbGWBiQD5MDIhBb+JNzrI0zoBqYoB+nscVAOXOgM7goQ2tuAbqEMI/uhsZ9ZjtbS/t1IKQPmeAS1Sj7hPAOcCnY/YZxeTg+Qlnu2MQ277Wv4MoV3B9JTkpJOJK4H5kVgefKDHAj3FUfGeALwa4Iypa4HaYM8Awmk4nlWXYDi9nQD0SCPqj9joytY3yDlWeMAE3mVYUfofzDe/zNGKAW6apGPAq0Q3MPM4AqHShReGLAVFhAnpu3yAssycSnp+1lHeM3SR1acIEXGeAa49Omk1AOy9xeRTixAC3jeGLAVmdAdVyBrjH3028MyApTCCLMwDCDtwOKgcW9L/7SPB8j/NZHjFAQwS6SOcYTHIGbMc51y3mDPD/4zVBYZllRgENEXiA6Paonvs4MaCH8sGyKDFAy+1uwutNr5ckMUDXdd1KfvvxZOTeXYXMDubuNwq/nEoSAyYHz27HNm/OADcHSBxJYQJuPdQRPEc5A3wxoJozoNacAa4YMIqwn2JiQDpMDIghKUwgzhnQQ/VM8rp+0kwCSbjHow0BXxHzcZc/FTwf4W2vmjPA3Ua1nAFRhUzUsWlF3BazzyhU7XuBsFG0J5VZXLUQ66Kyb52ULFG3PyXis3o5AzqJdgZs0NKwRRoQhlFUOrz360bUL0wgyhkA4ei1H6vfyDABLWq6KJ+5Jk2YgFvGaSctKkTAXTep4xtVd6bJG+CXy0nOgJWUiwE6YrmO8kZmLc6ATc5nfodhIKEwsTR4zuoMiAoTqIczIEoM0GXbEZEnjTPAr2+TOh1/cY5P20TaJonq9Ol/d2bw/EfC86TnIEuYgDuNZpxg5RI1Ov0y8hvKOpEtJgb4//Ha4MRnCROoJgZUcwZAWO5tBf5MvDOgk7CMOyx4VjHAnc5SWRQ8v9NZ5t/bhwbPz1MeJrA3cDmVgohfTqVxBix3liU5A/wZUVyqTSsIyWECUdb8KGeAnvs0zoDhlM8sk0RcmIDrGJgcvI4KPTKiMTEghigxQJWuoYQFU5SdMwktyOrhDNDjySIGaGNN7Uxpcwa4hUu1nAFpwwTague4hE9R6NyhSwmtsEliQFR8WZLwoYVLW8RnUdPjxDkDfDHATyio18pYHGeAnnxzBhhGU+nw3g/ZWS4G1DtMAMJOYj3EgLQJBN2+ituZiROrXXE1yhmwPOF7EB8mEJWFHtLlDUgTJuA6A/RY9ybaGTCScjEga86ADuLr0wnB9noQFwVI3XgG0nE6i+oO8y0Rr/M6A6olEPRHEavNJpDVGdBNKKT4zoCoTp+GoOg9dB+V9XmWBIJ6D66P2E4UUR3S1Uj2+jPdFVtYDFg3AnqC36//lx8a4zMcydEB4sysVQx4CPk/45wBXcAtwevvImXrQcH7KGfAA8HzcYT3Vdy19bK338uDfVzirZdFDJgcPEc5A/ImEEwS6TqC58ERx6X10Bai7xndvi8GJM0m4NZt9UggaCEC2TExIIYoMcDtWLYFz24DoVryQHf9vGJAlDiRVgzoJLSD6g3eETzXwxmQNUygLXiulmTF5e3BsysGjKcyi2teMUDPS1vEZ1mcAUlhAp2EosyBzr426AG1SAPCMIqKP5owvKf+CQT9UftanAFxYQLVnAFuRzGNGABhfeM26m5FGsE3x3wnzhlQLUwgjTMgS5iA6wwYT9jpd50BEGbuHk1Y9/n1u1vGv0lYH28kXgzQTtEqZ3tDgXcjTrnZZHMG6LWi5zWvM2AkyTkD9LfmCRNI6nRAGCqQxRmg6z9NedjfKMobtdWcAScGz88QL1gBHIL8x18P3vvtigcJZykCWloMWOP8Cc8Ey46p8v3jkWvtDaRtF+XeTCMGqLvpvuA5yRnwb0hugn2BPwGfCz57MmK7yxHHwBDCa0bvbf+e8MUAnaFlkrdeHmeA27l12/31FgO2Et5Xbd5nrjU/qi3t5wxwZ2uA6Gm33d9eTQzQ3z2Wcoe2ez5MDMiOiQExRIkBbqZc/dwtmLI4A3ozTMC9+f3Oax5nQBvlN3/eBIJtwXNeMUAbvHsRFrj+tpPEgKjfmuQMqGcCQY0pm4o5AwyjaHR470d0N84ZoMJwo50BHycc/YrKVepmRU8Sq7VMdcu0+4Jt3x3znaxTC+r+84gBSWECbyB1r+5Lrb2uMwDKy3+1OCc5AyB0B2wkvo6ZHDyvoFwgUQFjNLWFCdRzasEoMaCaMyDrbAIA1yCx/78O3qcVA+5HQhfcDokvBFUTA04Jnh8gXrACeC9yL+l1VHXgp1rih35GnBig9vqjSZ76elbwrCPwUfdPGjHgSuC/gZ8E7/VaGhFsy3UGdAJ/H7xXV8L3CK9Dn/u9Y9Vr601vvb9QLgYcErz2QyWyiAH7Bc+vOsvyOgPShAlAfN6AtGJAFmeA2xGtFiagZc7uzjI/Z4CJAdkxMSCGKDEAwoJIP3cr5jRigG7Pn7okLR3O6zzOAN/WnkcMgPIRpzQ5A+oRJjCWsHG7jLAQPg75P9wkQEnOgKScAVnFgLQJBP0wARUDDiWMcdygJ6lFGhCGUVQ6vPcjeqI79GnxvzuEsDGjsePa0XBt4FCfnAEzgRuB/xe8zxsmANHOgGpkdQZo+VivnAFumECJ0L58ePC8juh4dwjrnKQEghDa3TuIr081zG0Z5Y15PeY0YkDa2QTyJhDcEbEszhlQ62wCIIne/opwZD2tGHCfs0+QczCGcpLEgMFIBnuQTmiSM2Cq9z6qXVGGOQOAcJR8BKHwFoXmC9AOd94wgQVIYmot/9wE4OModwYA3A78F3KP/GPwiMuwr0KFHqsemy8GuM6AAwjDamsRA9TB5CZ6ddv99U4gCPEzCuhx+mLAscDZzvbjEghG5QxwSRsmoOdT882450PrUlc8MZIxMSCGODFAL0TtwGZ1BvzUeeShlgSCncQ7A7KECUB53oA0OQOijq0teE7rDNCG1BqkQaSF8LuC5+cJG0q67XrmDND6PSpWNm4f+toPE9BRpOOc5RvNGWAYhaDDe+87A2oNE9AO5g7CkKFanAFxYQIjkIahOqp0H7WIAVHOgGrEdbTcAdQoMSBLzgD9LVHisptAEMJy23UGQHR94Y6EKd1Udhq0TO8gXnB2R6xcMSCvM6DW2QSinAHaJsibM8CvvqqNQPqkFQPuDZ7zOgOOQf7bdUjbIckZcJD3vqozwMQAQO6Rx4PlM5x1BlMee++KMpBfDPDZQdgmHEe5M0C5EOlYfq/KtvTYTqQ8+70rBuxEhD69ht3yyx3J1uNxiRMDxhL+dre9WWvOgLTOgDZveZwz4DfAbwmFs7ipBaNmE1B2EC/GKHrcWodqeejmEtBznXfQtRUxMSCGas4APXFZnQEvIbFJee0rHc7rPM4Av/Oq28vqDHDzBtSaMyCtM8ANEYCwENbC5ynCc9GIBIJav2tc2lYqG+7+PiB6akFt7Op53Az02GwChlEIOrz3I3rqN7XgcMJRjfXOvrKKAYOQkW33uLSc2UJ5o0kbpVrWVhMD0oQJZHEGVJtacKe3Pe1YZwkTUAHDb2QPIhSvdWRNO5XaaNas50kjvm79HlXH3QDMA35GdmdArWECel6jOgFZxYANEcvixAC9xmrJGeCTJAYsD57bgeeC13nFAB3hXUBluIGPLwaYM6CcODEAwlABVwz4JfIf7oOMKI9EOm4vBJ/XSwyAcst+nEV+E9VZghzzCOS36LG5Hc7XkHMRlcE+rzNA64UOyq+7vDkDogSRKLKECYwizLOigqee08HI5ZAUJqBUCxGASmeAXg9uAkG3fjXSYWJADNp4aveW+wXR1oTPGkGtCQTjwgSqOQNUDNB9uWJAnpwBQ51tDia58FLchhRUqn5PExYuacIE/IbaUMKGZVvE97R+13MY5wqA8kIuKmfAa5RXSB0Q3o3mDDCMptLhvR/uiQG1OAMGEdr31xN2/rUjk1YM+CfgWeBiZ5lbzrihAnFiQJ4EgnmcAXH1Sz3CBPTcaofeb2RPRIrWHkIh168Hk5wBilu/R9Vxf0Eyyi8gvo6phzMgaTaBKPdI1gSCHcFztTCBnc7yvLMJRKH7j2pPPYMMpnyYcATRvbb8MIGk2QTcqewg3r0yksps+KmdAS1Sl2cRA0YDH0T+q9Mp/x/0P22UGJDWIh+HXiuzCI/NzRHyF2eZT5wYoO34ODEgKkQAop0BWh40MkwgajYB9/7QvoF7DoaRnECQiM/i0PLEz+Xi9oPUGWBiQHpMDIhgMKGt0J832S+IsjoDaqXDeZ0ngeCblDcO0uYM0G1rDE5SmEAaZ4DfYEvjDohzBihPEZ6LtuA5izPAbUi1UYk2OlUgShIDfPXTfd+JVHrudFQboOWSDhlGUenw3o/orp8zAMIG3gYqR6XSigGzg+eDnWVuOeMmEdRO9cjgWKI6nG4CwTRiQD2dAb4Y4DsDxgBzgc9EbFu3ocfs1yU6sraKsLPhiwH1cAa4RNWnAwlHz+rlDEgKE1CyOgM6IpZFOQN6qGy810MMuAMR9v835vOfItn7FTfZnJ5H/c1xgxsDKU8e6G7Hv0anRHzfwgTKcf/jODFgOvIfnUxYHh5JpSgDve8MSMuzwfP+lA+C6T7cJKI+Kri9H5lmUNvA/gxfPm755RKVM0BH8+uZQLDNWx7lDNjX+Vz/W18MiHIGlCgvQ9KIAdWcASYG5MPEgAgmIWV5F5XOAL+xkDVnQK3U6gxwkye520vrDFBxJCpMQG/kNM6ANu99mrwB6gyIEwPSOgPSiAFjqExMpe//FOz7twnHWs0ZAJVzU7faaIJhFJUO770fJpDVGeCLrNrAW0+lGJAmgeBAxF4L5aOh1ZwBun7UbAJvAY8hjVNfBHfpDWeAjrANQ8ryvwE+APyAyrqiWpiAny8AKhvWaZwBbv1eTQiJyoa+T/C+CznHUQkEx1BdE06bQFBJ6o9GJRDcGLEsyhkQNR1k1GwCWUdhX0SmCr6lynruPqE8TGCdsyyKw5E2yGZkEMHfjoufPBAsTMAnyRnwCnJvDkPO+2nOukcj4gCUiwH+fzGIsA1aD2dAXjFAf+cQyke6O4LXWm71EH2cuyOJXK8DzgmWVRMDtOz2y6woZ4AeRz2cAXFhAlEJBPelErec0qS5UFlm+AJjNfTea3OOA8qnHNQ6UcsBozomBkSg6v1rVCazaLYzIEoM0JspjRgA5aMiWWcTUGfAe4E5iEMgjTPAP7Y2730aMSDJGfAqUhD6OQOiCuS4aZ/8hlRU7ClIrOJeJCecqZYzAEIrLJgzwDCKxBPIfatlWS3OgKhKVht4bpiAksYZcBBhg0ef/eRL2sF1cwaAlGtxfZWTkHI2qbFcT2dAVALB7Ujdpp37SYTTeY0CPultwxcD3NF1/T6U22zTOAP8c+PWJXmcASpmr0D+p13XFuF/6NY5tU4tqOR1BlRLIBjnDKg1gWBWosIEqokBOhq9kPA8x4UJaL6ADmeZOQPKSRIDoDxUwBUDZiHX/EZkMEfR60rvbddin5TPJIooZ0DeMAE3MajbudXy5PmI/UJ4n+1LeI9rqJiKlFnDBKJyBqRxBqQVRDqC5zQ5A6ISvXY6+3DrLr/ecN9nCRNQ9F7Uc6zl/U6iHRpGNCYGRBA1p6eSlDOgN8SAHmc/fphA3GiVLwaowriN8EZMmm7P3cby4PkwpFH2D4QFdpacAW3e+2phAoMJ/5eonAFPBc9pEgjG/VZfDHCPcQBhvGfcdIIufsZUP0wAysWADjBngGEUhCVIJ/rC4L2fMyCLGOCHCECyMyBOHHAbZW4yLu0A+Q0pFUv3pDz2fizxfZUdVG8kavmbZdqmOAt2VAJB/b0vBs9HE4oBAH+LlMeKnzMAyt0Seq5dZ4BbhndS2aCE8HcqWZwBUWKA1h+6Xe2QuB0d97rKk0CwlpwBWRMIRjkDtlG7MyArbn2u7Yi1zrIooqzpcdeoigHznWVVnQEtJuy7/3F7ghjwfmQWB0Xv/wcpb/a419UQwo7zWrKf0no6A9z72hUDLkOmJbwrYr9rCNvNUS4TLZfiZouJCxOIcgYkiQHjkb8lawLBNm95lBgQ1ZHsdPbhCh1JzoAsYQKK7wzQ87WB6jMTGCEmBkSgzoAoq2SSM6A3EghCWMhUCxPYy1uuN50WKq5qFjdaDtLw0m3452SS8zpLzoA27301Z8B+SKNvK+HxdxAWHqoq6751+3nDBPxjnBYc41vA4irH6h6HvtbjdBu9ljPAMIrLFpyR057K6QHT4n5P64gsOQNUNDyIsPHsTkmqHSC/g6odogMpbxwmiQFp+BfgBODWDN+JG3WNChPQ3/9w8PwRpE7uRuqsqcC7nW3o+d1G+H+59YmGCcQ5A1xR2a0vXqScPDkD3N+rzgCdSUi3sSfR5EkgmNcZ4IbFdQTP1cIEopwB66g9Z0BWosIE9D+Nczr6+QL87bhoB26es6yqGNiizoDtA2DjcCp6Fr8Lnt+PnJqXEcFVeaB89VgxwA8NTUMjnAG+GPAk4hR1BQ3d7wuErqUkMaAWZ4CfM2Ag5XXUwUiZ91/UHibg5m5IEkVdZ5372/zvZA0TiHMG6HIt7y1fQDZMDIggyRnQ7JwBEDYOdYQhSgz4HJIb4AIq1VBtCHU46yeFCbjb9c+JO3dqLTkDqjkDNETAH63RSl/FAP2N2hhMEgMGUz7ClBQmcELw/Djp6nffGaDnxj0ecwYYRrHZJQZ0wxBnmCGvM0A7b1nCBF5B6pbhhMnMXGeANrTinAFHeMtdMSBN48unE8ktkGXUpZozIEoMeCR4fl/wvAj4RfD6byO20UNYp7U5n1dzBriOAreh6YsB1WYTcEnjDNBtZBUDkhII5nUGuP9LR/Ccxhngn4coMaDRzoCoMIEkZ8BUxCnTCfzZWe4LVt9EOk+HBe+fctateu23qBiwZijSqPJ6Fo8RinsA91EeFnC/t71GiQG1OgPcQbO4GHh/vy8QljHqMlkM/CFYvjBYVs+cAVBeFrwDuSSPJ30CQd1WnBjgOgOi6HQ+d51afnmUNUygmjNAB0EtX0A2TAyIIK8zoLfEgA8hCXY0WUmUGDA9eD6G+DCBKGdAVOXpbvdJJJeC4k4pUmvOgIlUNlyVODHg18h5uCdm30lhAlDeWEtyBhwfPD8Wc3w+vjNAz01Z1l3C/6ADzBlgGAVD79daEgi6YoDWEVkSCJYI3UiHI426o53P48IEtEM0zVs+lugEgo0kjzPgEW/d+4H/Dl6/w1muv6WHcDTLFamjnAHbCMveOGeAO3IJtc8mEOcM8KccU/KECWR1BkS5GfW8ROUMqOYMWE8xnAFJOQM0ROBRKhP96ncGIg6YTxC2A14GPgX8D/CbagfVYmLAy8j18IQWRhE9i+87r+9FpokEuZ6f8Nb122jawSuqMyAKbWc/TXg9qjNgHXAWknNrebBsBJVJqyHdbAJ6n24lLM/cpH6Tg+cJZHcGtHnLtYxxw4yjiHIGRK1fa5iA7wzQS8+cAdkwMSCCIucMALnIXVU1SgzQhH9tVIoBGr/1jLN+kjNghLPOJqRgOSNYpmLADsIB7TTOAD8sYBxwNyI2RCUj8WcSUC5DClgtuPyGR5IzAMobC0ligDoDHo3YXhRpnAEQugM2gDkDDKNg1NsZoPWFNo42UN0ZAJK0FEQsPYywTIbqYQJ+zoJawwTykGVqQRVHXg8eyv2IgArlTrIoMcAdzYpyBkDoDnBHkPT/3kDlTEJZnAFRYXdxOQPiGmHVwgR6KK9PBpPfGeCuq+cwal72ajkD1lFZffWWM8CdWjCNGOBb093/tM37rB1p380BPkaKTkuL1eXLkc7nhw8NFkRc1Lci5dgG4I+IIAASfhF1PvW6dZ0BayLWq0Y9pxaMSyAYxdeBLwI3UBkmsA4ReXdQLv769+9owroijTOgizD81A1J0LJnLGHZmNcZ4M7qUM0ZoMcYJ1jjbSNPmIDvDFBMDMiGiQERJDkD4iwq0Hs5A3yyigF/RgruS5z1k5wBui39/k7ChtV47/uQL2fAVMJRr/2oxJ9JII5GiAHDkflwIb0zIK0Y8EPEMncXmDPAMAqG3q/1SCDYTeX9nyaBIITzWx9OGCKg/YxqYQI+SbMJNIq4+kWLvJ1UOgMgdAfsAB4ibNgPJfwPXJeDNgDVGTCCsDHrx9yqGBDlDFhFZSZqtxzP6gwYQZjE0XcGxBH336wKjvNVyuuZYWR3BnR7++mifBaKrLMJNNsZ4IcJRA1uxIkB7m/z3RoTsh5UizkDQO6nXddDRM9iB+LomYqUTQsRB+unY7bn/q/1DhPoDWfAcmQq1G2E4pQvVkF5ueKHCqiQuYnKwUZ3FrFqYsBk5/V+zrpJuMkI3XI7bc6AqASCjXQGRNWtRnpMDPDYnbBT+FrE536H370Ae8sZ4BMlBujr3Yi2Rr1OeT2VJmeA+1v9URP3Jo5zBgwkdBK0Bc9auP+Vs25U7FRcmIBPmjCBnYS/PU2YwFHBeqtJn0E7LoGg3zD6JTLVTju03GiCYRQdN4Gg6wwYRLSlMwp35Novn9KKAa4zQJMHauiAHkdcmIBPUZ0B/mwCEIoBTwTLtxAWj+ouc3MG+M4AbUxvofI8JzkDVlMefwtyfpNmzPHXhbB+0ZG5Dc52kxrjSVXAJiRMZCaVwnZWZwCU15HVxAC3/opyBkSJAc2YTcD9T906fj+kY9RDeQy7ux2oFAO+m/WgWlAMAMILN6ZnsYXy/+YZKu9Lxb2H6p1AsN6zCVTDj1/3O6rqDvDbvnHJAyE6gaArBkxx1j3AeZ1WDNhI+He2Oct1cDBLmECSM6DeswkoljMgGyYGeKgrYDXRN4tvFXRvhiKJAa4zIE3SlCzOAKhMFBTnDNCCfhiSbfU1ZJqotmC5ui/cqa/cZCNKXJiATxpnAERne44TAzREIK0rAOKnFkycksicAYZRKOLCBCC9O8AVA6KsjH494ucMgNAZMAV4T/D6Pm+dqGRuLrrvWhMI5iGufnH7TPobn3Q+/xlis/1K8L5EWKeoGOCeX98ZoGJAVGP618jo3R3OsiRngCvmZJ1NQBvjrzjrJG2jWhWwBHHnueF5Q8nuDIB4MWAsYV0c5wxIk0CwmbMJ6HJFZxF4nMr22k7Cc6WOx9eBE4FvZD0oEwNqphFiQK3OgCwJBF38zr9fNlcTA/wQAQj7IiMI2+jbqXQGDKV81i8dkKt2X7plrRsqUG9nQK1hAn7OAMWcAdkwMcAjKV8AlIsB3ZQXBEUVA9KooVmdATspr3D9zq+ihclwwuSAMwk72lHn2S8Q90QaJTsJk63EkcYZ4B5jlBig9bceoyYPTJsvwD+OJGdAGeYMMIxCoffr4BKM8O7LtEkEk8QAHcnWcjJqnnYQ59BapMI+INjWPG8dv1HqjpRD2EhspjNgCOUzuLjHcTNix77G+Xwj8EnKhQ89V9rxS8oZEJU8UPkNci7dRIV3IXXMb4gWA/I6A/zkgVCbGODiui7yiAFum6YT+d36HT1/RXYGRIUJuJ0tt46PCxFQ/KSOm5F6P7Og0arCvv7eOosB9U4gWI+cAUO9ZUnkdQbEJQ+E8v5GW/DcRZhYXMWA/Sj/OwY761YjKolgnBgQdd/7YkBvOgNMDMiGiQEeSfkCoPxC1BtBK87+Kga4tiAXN1QgjTNAO9tTSRYDfGfA4cHzK1RvhKV1BiSJAfq72oLnejgD4nIGlNGqDQjDKCju/eqLAXmcAW7ZuJmw7tjkLIvjWef1o5RPjwfRDSlXsNVkpc2YTSAuT4svSqRJEOY27iGdM8BPHhjHw4hAMJfy/2IHMlKWNGOOS1yYgBvmVi8xQI9pLNENuqzOgBJh58UXA5JyBuxEQiCalUBwGGGnw7U4DwMuBS4GTg+WLaiyLVcMyIU5A2omyhlQSwLBkYTXR7PDBPz32nfIEibg3rda3rlhAhORtvTkmGNKcw6iErLGhQm4QmcPcumbM6DvYGKAR1ZnAEg851qib9jeIEkMGEt4IyZVylnDBKC8YI5zBmhl6lbUrhgQJbr4BaJOo/Wkv2IEcQWFT5T4ofvVhmMb0ijQfAXunMTV8MWRVGEC5gwwjEKR1GAaDvwI+N8q24hzBriNlc3ecxTPOa/vjji2qIaWO5KmU+U10xkA5Y6KPPqnLwa4vyWLM6AaO519aSM1rTPAn02gkc4A3Zef9VvJKgZAKCIliQG+M6CD8nw8uu9GX2P6+912g9tRmQ1cC1xPGEf9YJVtaZiAiQEZaYAYMIxQnMnjDHBzEqjDoDcSCLo0IkygRNi+1Xu/C/m92jafQnm+AJc056DD2z7EOwNeISxrdNv6eT1zBuz09qtCit+2tpwB2TAxwKOaMyBqeqF3IDddYkevgSSJARAWgPUME4ByZ4B7E0c5A1wL30FkCxM4Jnj256KNoh5hAq4YoCECL1JpG017HK4zIFGNNWeAYRSObTG15FjgC8B5hJ2mKNKIAVmdAfOpLOuqOQOaKQa4x+aKAXn0z0Y6A+L25Tdy6+EM8AUFt9NeFDFgnLfcvX59Z4A2vt1jb3S+AAh/vztdsSsGqJj/FnK8v6U8fMYlKkwgFyYG1IxeWxOdzeXp4PUQdrY1N1WznQFZwwTixEztCLtiAJQnEZwcvPbLmzxhAkMJL23fGdBOWF7q+W3EbAK6b8WcAfUh0y17+eWX89hjj7Fp0yba29uZO3cuBx10UNk6c+bMoVQqlT0eftjP21pc8jgDNM6uWSTNJgDpCsA0zoC0YQJROQMgbKC5MzZEiS5+mEAWMaAeYQJRYkCWfAHu9kEKu/nAC0jSqljMGWAYhaPTqyW1AeYmZWpL+H5cmIDbIUkjBqgzagMSsuSXdVENKR1J206Yb6UZUwu6FvukMIE01CtnQBq0Xtdz28icAe7oXx4xoC3m81rEAH95D2H15IsB2vhulhig18MOyvM76H36O2RU85wU26qbM6DV6vI6igH+/7ee/AlPtVzQ/7XWBIJDySYGbKP8PquHMwDCukiFMD0+N4ng5OC176zNEybgDjL6zoAkMSDJGZA1TMDdPljOgHqR6ZadNWsW1157LSeeeCKzZ89m8ODBzJ8/n5Ejy9PWzJs3j4kTJ+56nHnmmXU96EaSJ2dAs6nmDJjgrRdFmpwB/vez5AyA6FkC3OkbtQBzC8RRwLTgdZowgbTOgKjfmyQGZMkX4B/HdmRU71Dg1qQvmTPAMAqH7wzQhpvrBmhL+H6WMIGomQSURcDfAeciRUSaMAHt1K0mbKg1YzYBiJ5eMI8Y0ExnQFYxYCjSARmN9JPcdoVfV7mCRZ4EgnHOgGrb8hMIQrwY4K7jTgcJoaXY3V+j8wVAeK71etjmLdf79E2qX+8WJlAjDXAG6P+XJ1+A4jtB6ukMSNsPWB/zGvIlEITKMFi93zSJ4EHI1NhQOaCVJ0xAe3oqBPpigK7vO6h62xnghngZ6RhcfZWQ9773vWXvL7roIt58802OPfZYFiwIU7J0dXXR3t7ufz2SoUOHMmxY2DwYPTpqlvneYThhxzmLM6DZVBMDhnjrRZHkDIgLE0iTMyApqeImygvFexEl0xUNpiP1ykrSVQb1cAa8HjyPRaYVgtqdAalo1dEEwygw2waUv9+M1BOuGDCOeOKcAVnDBKA8034aZ4B26typ8pqRQBDq5wxIkzNA3Q/VbLZp96XnNk+YgLoC3vC+Vy8xwHcG7CA8J1BfZ4C+HkmlM0CvYbf6aoYzoNNbrteA/5ui8MMENsWtWI1WFwMGJa6VCr229P/Lky9A8TvfvZ0zAMQN8DakTezvP0oMGEl4TVcLE1D8MIFzkb7AJmS2lr931s3iDGgLnn2XcNYwgXrkDHC3D+E58F131ozORk363bhxUh2vX19+q5166qm0t7ezZMkSfvKTn7DnnntGfR2Ar371q2zatGnXY8mSJbHrNpp9g+ctxFtMonIGNJtqYoCSVADWOptAXM6ALuILnQ6kQfE94BeEo+9ugajJA9OECBCxr1rCBEBGmDqBZ1LuX/GdAakwZ4BhFI44Z0CeMAG3fMoaJuCTJmeAziDwgrOPUYSj871Z1EQ5A+qRQDAqTAAkTl/rrrxiQIezbcgXJhCVLwDkN7u/e6X3WVr8nAEd3uf1FgNcZ4B7zW12lkd9r1H4OQP8jorrDEi7LcsZkBP9vQUTAxrpDMgiBrjPLlFiwN7OZ3GOsThngIoBWv59C1gcs24SfpiAmzwQ4sWALM6APGECUc4Ad7mFCGSnJjHgqquuYsGCBSxeHF5m8+bN42Mf+xinn346X/7ylznuuOO45557GDo0aswZvvvd7zJ27Nhdj2nTpkWu1xtoiECcKwDKL8KiOgPiprxKEyaQZTaBNDkDksQALTj+Efg0YWPVLRCz5AvQ/bnkEQM6KC9gniT7f23OAMPoH7jOgB2EZUOtYoDbYHkxeM4ihfudsagy6g/Au4EvUt6xaXO20VskOQPyJBCMyhnQQ/g7dUrateQX7uPCBKqV6e5sAlH5AhR3O7U6A+opBvgdryhHg+8McK8v/T+L4AzQoSgTA3qBgooB9XIGuPd1VjFgvffsop39Uc6yNK4m3xng5wwACRn4IVIuRIl6SXQEz1nFgCw5A2oNE3DPge7XxIDsZAoTcLnmmms48sgjOfnkk8uW33zzzbteL168mEWLFrFixQre9773MXfu3IrtbN++ne3bw0tqzJioyPLeQZMHxuULgGI7A4YgZXAeMUB/y8Dg4TbOsoYJ+KPicQVvh/deK173CsgqBri/sZv4ujgpZ8BbwbFpwZc1XwDEiyOJmDPAMApHpyMGdBOWZ64YUGuYwC+QUKTnMx5bF8mxqzuRaQiVbYi4q427ZjsDakkgGOUMADmvYwjFgLz5AqAyTEA7JNUs51HOgDgxQOuZIokB1cIEoNLZ4HacdyDVWW86A3QQIcrCDNnCBPSeyi0GtGpdrhdb7p5FSJGdAYMJy7FGOwPi8gVAfJjAFsSJdCAy2Kb3whpCB3Se2QTShgn4zoCREesTsSxPmMC2iOUmBmQn1y179dVXc9ZZZzFz5kxWrkyualevXs2KFSuYOnVqrgPsTdI4A/zOZhFwj2kY0SEC/no+7m8ZQnlBERcmkCZnQBfpxQC/QBwKHBa8TpM8UPenJE31mOQMUDFAK6Gs+QL84zBngGH0XdwwgW7CcrSeCQRLwHM5jq2T5HhMn000TwzojZwBIA3Y/QnFgLwhAu6+tI/zz8D9QOWwRjlROQP8MAGojzNAt9EWPHd4n1cTA/ImEPS364sBQ+hdZ4DeprpP/37I4gxQzBmQkTo6A/xcGLUkEKx3zgAI28XNFAPiwgQAPoiUg7c5y9rJJwbEOQO6gP9CBgzdvDS+O0ep5gzIGiawFak7Fd1vnikoW53MYsCPfvQjzjnnHE499VSWL19edf3dd9+dfffdl1Wrki7pYpDGGVBCLsCRFMcZ4N5Mw4kXA5Jufve3DCVaDIjKXr0BKSjSOgNeRTrZg4kXA9QZcDjSoFhL+awDScQphj5+w3Sw81rFAKXXnQEmBhhGYfBDw7Q828tZ3pbw/TQ5A/KSVZzehCQ/1Gz7/W02AQgb/vVwBnR4214JzEnxPTfsrpozAKSOcBuwfcUZ4OIm29vprdtI/Ou+FmeAiQE10gBngFIkZwCEHeO0YsDTwfOzEZ9FiQF5wgTc++1pZ5+KG9qbJ0xA+wKuCHGh8zouTECp12wCun1fDDFnQH4y5Qy49tpr+fjHP87555/P5s2bmTBhAhMmTGD4cDGSjxo1iiuvvJITTzyR/fffn1mzZvH73/+etWvXRoYIFI00zgConLqm2bixo0liQBZngEtcmACEhYv7fbfx4ecM2EA433WHty2teLVAzBoigLcvv6Bw8XMkuLFarhiwDliaYf9KrgSCrdqAMIwCs80LE4gqR9silv0TYv/XMiYpTCAv7rGkKWe0w6Yx0c12BtQ7gSCEDf+Dgud6OAOy1vW6/gjCQYYkZ8BGKkfW09KsBILVnAHuuo3Ev+6jRiV3ku5+8zsvucWAZkzXUQQKKgb4/309xICkZVH8CpgCfDviM+3U1xomUK0OcMWALM6AscAAQgEkbqDtUeQS+HPMPqLOVZ4wAd2///stgWB+Mt2yl156KQD3339/2fJPfvKT3HDDDezYsYMjjjiCCy64gLa2NlatWsW9997Leeedx5YtSTMoF4M0zgCQTuYeFMcZAFK4DSG/GLCTcEoiP4lgXJgAiHXrYKIr5OFUOgPeQkZXplA9TCCPGJA1TECFDxUDeoLP9NjyuAJAfud2xEmSdBy7cGU5cwYYRmFw719/dF/xcwbsC3wHua21EZsUJpCXrM6AlcCxhKPVRckZUK8EghCeVy3ba3EG6HzdWbeh/4XWK51EN+q1HtpEecbwWsSATZRPL5hHDHiLsA53l7uv9RgXIFPw3uGss8Nbt5H4bY8oZ8AG0p3TujgDWrkub0ACQaWezoC812XUvZRFKIwbWNIybTdnWRpnQFKYQBSrg+cdpHOFdQTPA5E6zg8T8HkQEcajpvuD6s6ArGECvhhgzoD8ZBIDBgwYkPh5Z2cnZ5xxRk0H1CwGEMbSVHMG6I1QFGcAyE0whvxiAMjvGUS8MyDq+6o0+jf59uB7fs6At4D5wBmE6qGiFe9ApNBRMSBtvgD/GLOECbj5AiAMS1iQYd/+cZxPZcbvWNzKs9VGEwyjwMQlEHRp895fSNgn0HCCRjsD0pQzLwBn0ZzZBOqdQHAU0oDxO71+w78WZ8Ai4J2Esz2kxa8Pl1Me26roOamHGOA21LUuh3xiAIg7YJ+I5e5sAgCnIv+pu52iOQPSdiTrIga0cl1eR2eA/1/UK2eADtLkZTvlA2b16Afob3MnYa8lgWAc2l5PK4ZsJwyLbiN5YDDqmNI4A+oZJqB1QztGVupwy/YPJiI3eA/VRwGiptVoNu70gnGzCVQ7Xu3AZ3EGRIUJQHkspC8G/BCJu9zkfWcbIqQPRAqeI4PljXQG6G9VN4IWZP+OZPa+JcO+fX6TZeVWHk0wjALj5wyoFiYwALjIee+KAW5yM78hl4es4Uj+bAXNDhOoRQwAcQf4jmxfZKnFGQCwMMd3/PowKl8AlIsB7vWQJ4Ggoq40bQfkSSAI8WKArqPHuJPKurYIzgD3P0iTLwDqFCZgYkBdeha/Az7lvE/7H0bhCoS1XpPdNE4MGI10vLeSXQzYSfV7PasYAHLuRiKuhWrOAJ80zoB6hgl8ExFu/5ByO0ZIppwB/RnNF7CS6uV30XIGQLkYEJXwL41CHzXdHiSLAb9HRl7u8pa78zG7BYKOfvhCAIhaqzf3ccF+N5EtZj+tGOD/Vs0MrgXvBiRLatpCr2ZauQFhGAVmmzOMlMYZMJMwgzxEiwH1sjFmdQb4YkBfTCDYQ1hPjCM+Z4BSizMgL3nEgJ2E9U0eZ4CizgClFmdA1HLfGRCF6tnmDMizgT6M+3trDBW4DUkCehfwY2orq9zyttZr0r+369EP2Ex4X+2JtOM1ZCBtmECaDr6KAVnOQUfwXA8xoBGzCbgsBL4YsdyojjkDAjRfQLUQASimM0BvKFcMWEUYG5pFDPCdAUlhAvMpn2LLP57t3veqjYZtRsIdZgbvnyS7pUtjHbM4AyYHz8sz7qtuqCxXojYPm2EYdSWNM8DNGfAp7zO1fvYgoxbrgfvqdGxZxQDf7t4sZ8BMpK7NOx37RiRMwHUG+DkDdFktscZ58f+LqOSBUC4GgIjlI6lNDHjLW1arGOCPOPrOgCh6M0wgzWwCaUeV/VmRcnX08l7U/QH3QhlMzedgMRJSWisbCV2n9XAGJL3PyxokVHlPwktoK9EDZ4rbnk7TH3kauReyuJ1UXG0jXZiAS6OcAVqu1MNdZwjmDAhQZ0C15IFQTDFAb45hhDfsGxGfJ+En1VOyFgDutqJyBiShzgFXDMiK7i+LGJDl/28INpOAYRQSv8PtvtfbdThS9o4BPhwsezh4dp0B65HEUB9pwLGlqY+2UF7GNSNnwHTgfuC35C/23BkFknIGrKI5UVdZnQH6e7T+K5IzwG/Qp3EGNDNMoBZngHu8SZ2wRFq5Lq+jM6CelAhHuGsVqPxZKupVvug1uhdhiEA1V1NWZ8AGZPDuw9VW9L4D+ZwB/r1Zr5wBr3jPRu2YGBCQxRnQV8IE2slm18sTJhDH75BR9ieIDhOIQz8/OnjOki9A0d+aJkygMGKA3omWL8AwCkVSmMAbhLdsG3Ae0mB6HpgXLNfyUztP9ewgZXUGgCQRVJrhDDgleD6MMGQga7GnHTU3TEB/iysGNCNEAGpzBkDvOgOScgZAvBhQFGdAmtkE8oQJ5J5WsJXFgBLh7y6Y71jLhXo6A+rZB3CTCOpMAkn5AiA5WV8cWQcxO4Ln3QjrsrRigH/f1Ws2gZuRGUz+NeX6RnVMDAjoL84AVwxwLUZZnAFZwgTiuBwJUVhPNmeAVsBan+YRA7I4A1T40P9/eY791YVWbkAYRoHxxQC3HHyTcFS3jTBx4BxgnbedRsTn5xED3LwBzXAGvD14HkxYt9TiDEgKE6g1eWAtuP933AiW/n+1iAF+w3wr5W2TtDmQIJ0zQDsoSR3sIjgDak0gaGJATuqYRLCeqBhQz5wBjRADXGdAI8SArLhhAuoMSDsw+ALl4nPU+coTJlACHqV3xMZWwcSAgCzOAK1YOhpzKLmImk1gG+Expiko6ukMiDo2SB8moPvLOqWTu788OQPMGWAYhosrBvjTA64jLGNPBE4K1rmRykR2jRADss4mAM0TA5KOr55iQBGcARD+3vWEx+tzIzJ97dzgfR4xYL73/i0aGyZwHXAuMitQHM1MIFgYZ0Cr1uV6wRUoTABCkbCezoB6Dgi6YQLqDMgSJtCowclawgQAbnBeV3MGFMlt3WqYGBCQxRlwFfBl4GeNO5zMRDkDXDGgFmdArWJAljABtwJ+hnyN1axhAiMI43otZ4BhGC5JzoD1hGXsF4PnO5AQLX/GgCI6A5oxm0AUecWAMVSKAZuc7TXTGaD/R1Jc6x+R/DgqeucRA54EHnHe+86AeosBbwG/JrkuL4IzoNYEguYMyEnBwwSK7gzYk2I5AzqC5zxhAgD/7bzeN+LzPGECRv0xMQDJSNwWvE7jDFiNCAL+yE8ziRMDshSAUc6AQYRlet5CNE8CQcgXIuDuL6nAcsME1BWykSa6PcwZYBiFpNO5J30xYB1hx3R68PyL4LmoYkCzcwZEkfU41FbfRmXydjdZWDOdAfp/xOULiELrv6zVwHXO63olEFwI3O1tOy19NWeAhQnUgYKGCWh5XM8EgvUUA/IkECx6mACIIPt68PqBiM8bdT6NbBTsdm0O2hlcR9+dqqIezoCoqQVHOK/r4QzoDTEga5hA05MHgjUgDKOgJCUQdMMEQEZ3bg9e90aYQNbZBECOdxXS4GxGzoAo8iYQ3M1Z5p7fN4E9gNcybreepHEG+ORxBoAk1Pov5Ly8QX5nQKe3/N0Zj0PpTWeA34EwZ0CT0d9dsDCBvpRAUEN9qzkDss4mkAc3TCCPMwAkWewJiLjoY2ECxcDEALLlCygqUWJAJ9lyBkRNLeiKAXkVVfd7WcIEanUGJIkBWsBNpgD5AsCcAYZRULa5zoAB0OmIA74YcCPRieygOM4AkFCBvTN+p1Z8UXiU8z5rv0nrid2dZe75/WfgdKJHonqLPGKA/q6s56MLmIDMzrCFsC7fiTglkuhBjnUI9etQ9KYzoIT8Bm3M+rM9bSXfVGi5xQDfqtJqFNQZoCPvtQ749UYCQS0biyAGdATPuwEDgtdZBwY3ES0EQDbh0mgcBbtdm0MhRoZrpFoCwbxhAnlmEvDJ4wzoBp7LuT+1ViXFi94fPB8NzAheL8+5v7rQ6g0IwygovhjQlSAGzHFeu8uhWGLAVUhRM6/ainXEbfTdCXzIeZ83TCBODLg1eDQTrfd6wxkAYWcCwmsh7TW3Bpl/3Bew8vIgMBV4uk7bq8Z2KsMZ9XpL6woACxOoCwUVA/4XCeXKE/bi0igxQMWKiYTu3GphAiWknT+CxicQbKNcYKsX5gwoBpYzAFgEfBO4pdkHUgPVcgZkcQZEhQnkDRHw951WDFhM/sLty8BZhHbdKFYjDZWBwEeCZYUIEzBngGEUim1Oo757QGXOAC1jH0PKLWUn5aECjZ5NIEtD6g7gPfSujd491tsp7yvlFQPiwgSKwA+B24D7MnxH80/UOsqndWfac3Ie8DdIvVgPLgPG03tuS7et4OcMSJsvwN+OiQE5KWiYwGpk6tc/17idRsW4q5in7W/X2ZuEtql7I0wgz2wC1WiUuGJko2DaXXP4M7UXEM0mTgy4HTgf+E2KbUQ5A+otBlQLE3gIaRDdVMP+1gK/T7HeXYhSPCZ4X4gwgVZtQBhGQSkbfY8QA/4HOBn4PxHf3UDYYS1KzoBm4R7fE0hH8YDgfT2cAUUrOq8NHln4NeJUy/o9n6zOgIdq3F8UvXk9uvvSe0I781mSSJoYUAcK6gyoF43qvGo4i3a40163byHCW6PDBIYSJlqvpT8QRRcS4lQ0QbeV6Ke3a+sRJwY8ARySchtaEZ6A2F076f0wgaeRxnO1OMd6MB/4ivN+eS/sMxZzBhhGIXEbPj0DKhMILiU+0dp64ED9bgOOLW+YQDPQ89aDTKW3lFAMyJtAUMWANLHxfYHVwIV12E5WZ0BfJ8oZ8Hvgu4jAkhb33t4Uu1YVWr0u14uuYM6AetHIkew1hDmsquULUHSUvlFiwBbCnBw6ZlVPZwCEYkDR67D+jIUJ9BPixIAs6JRTFwJLgI8TJjKpRQl0G6xpkrf0VqPuQcoLNXMGGIbh45Z93QMrnQFJuDHYrS4G6Hl8GWn8LXU+y+sMUFdXq3R609JqYoB77es9sQX4GvBkhu2YM6AO6O/up0ONjRYDlLRigLapG+nE8WfGaYQYAMWvw/ozJgb0E+JmE8jCt4ELEPvmfkhm7P8JPqtHmMB2inWzdxEmEtxKttjCutPqowmGUVD8MIEOpBzbSBjjHUejcwb0pTCB+5FwNQ2ncMWAvM4ApVU6vWnJGibQ14lyBtS6HRMDcmJhArlx26BZwgSgsdN4dnjv6x0m8Cckf81f6rxdIz0mBvQT4mYTyEIJEQCmAf+ENHQneNvPgxZStU7p0gjmB89Nn1bSnAGGUUhKQFfQwO8eKB3Rs4C/prqLyZwBIZuBDwO/Ct6rGJCnyPPFACs2y2k1Z0BUzoA82GwCdaCfhwk0KoEg5HMGNDpMAMpF7U7q7979KBIyVsQ+QqtgYkA/oR5hAu62/gN4O5IRuQtYUMOxFVkM+G9kxOrqZh+IOQMMo7BsC0a5uoMa804kzKgajRYD+vK0TLWIAX5HrVU6vWkxZ0Dt28ktBrS6sG9hArkpqjPAFQPqHSKgtOrtUhT66e3aetRTDFDWAf8A/CO1NSqeRZLz3VHj8TSCtcCpzT4IsAaEYRSYbYOCeZYzjnb1pjOg6GECPs8DT5HPlbUDaQRrTptW6fSmpVWdATuprYNmYQJ1wMIEclNUZ0CH87reIQJGMeint2vr0QgxQKm1QbGJMGu0EYM5AwyjsGzTMIGMYkBv5gzoa86AbuDoGr6/CRMD4tBroVX6o9qJr7XNY2ECdaCfhwkUTQy4CwlZe6DOx+LSG84Ao7lYmEA/QRuFI6i/GGD0AuYMMIzCsksMyCifW86AxuHmDbBis5xWcwbotV9LvgCQTs9axK2SO6yx1cUACxPITZ4wgf8B9qCxYkCH89rEgP5JP71dWw+tBNsilhl9AHMGGEZh6QzEup4CiwF9LUygVlwxoFU6vWlpNTGgXs6AbuBQ5LzlTpLW6mJAPw8T6I0Egl2U1x3NxnUG2CBj/6Sf3q6th96gu0UsM/oA5gwwjMKybYA8dw/J9r1Ghwl0IHbmTkwMMEJaNYFgPQZAap5iuNWF/RYKE6h3mbsYyaPyRJ23WysWJtD/MTGgn7A2eNY/tIfWaQj0C1q9AWEYBWZbINYVLUygCzgJaZTWe7qnomNiQDyt6gwohBuy1Z0BFiaQmy5qy6PSKDqc1zbI2D/pp7dr67EO6UfqALPdsH0McwYYRmFZNlyeV4xKXs+n0WIAwHMN2m7RMTEgnlZ1BhSi3dPqYkALOQNaJU+LOQP6P5ZAsJ+wg9AdAAWpFI30mDPAMArLF6fAjM/C/InZvtdJWBa3Ssest7AEgvGYM6CJtLqwb86AfoeJAf0fEwP6Ee60JCYG9DFavQFhGAVm2w54/G3AsOzf1YZUq3TMegtzBsSj0+LlzojfxzBnQIGwBIL9jg7ndSHuMaPumBjQj2h3XhdCITfSY84Awygu2gIcmv2rK4PnjfU6FgMwMSCJecC/Af+n2QfSS9RrasG6YGKAYGEC/QZzBvR/+ql215q4YoCpd30McwYYRnGpQQy4GDgReKyOh2OYGJDEVuCrzT6IXqRQzgBtVbdqXW5hAv0OV8g2MaB/0k9v19bEwgT6MOYMMIzior2NjFMLAjwZPIz6YjkDDKVQOQOCZKPFOJgm0M/DBFpRDNiBlLdjsb5Ff8XCBPoR5gzow+idaGKAYRSPGpwBRmMwZ4ChaHunEDkSTAwQ+mmYQCvmDIAwVMCcAf2TfqrdtSbmDOjDtHqcoWEUGRMDCoeJAYbyv8BRwM+bfByAiQEWJtAv2QDsj4kB/ZV+eru2JpZAsA9jzgDDKC7a6jMxoDCYGGAoLwMfavZBKK0uBliYQL/EnAH9m356u7YmFibQhzFngGEUF3MGFA4TA4zCMZiwVd3qYkA/DRNoVTHgR0AXML/ZB2I0BBMD+hEWJtCHMWeAYRQXEwMKx2bntWmoRiFQV8BOyoPLWwkLE+iXzA0eRv/EEgj2I0wM6MOYM8Awios27AdjtWZBMGeAUThUDOgCSs08kCaiN+MA+mVZ2aoJBI3+TT+8VVuXTsIGkokBfQxzBhhGcXFbgDmmFzTqTxfh32JigFEIWj1fAJTfjP3QHdCqzgCjf2NiQD9D8waYGNDHMGeAYRSXHYT3poUKFAYVv00MMAqBiQHlbRgTAwyjT2BiQD9DxYBWrov6JCYGGEaxsRkFCoeKAVZsGoXAxAAJj9Absh8mEXQFgFZNC2H0P0wM6GesCp43J65lFA5rRBhGsbEkgoXDnAFGobB6XOjH0wuaM8Doj5gY0M/4D+BnwC3NPhAjGyODZ5vE1TCKiYkBhcPEAKNQmBgg9OMZBSyBoBHHJZdcwrJly9i2bRuLFi3i5JNPTlx/5syZLFq0iG3btrF06VIuvvjiinUuu+wyXnzxRbZu3cqrr77KVVddxbBhw3Z9fsUVV1Aqlcoeq1atqthONfrhrdraLAoeRh9iCGFSMhMDDKOYaCvQEggWBhMDjEJhYoCgN2Q/DxMwMcBQzj33XH7wgx9w6aWX8tBDD3HxxRczb948Dj30UF577bWK9SdPnswdd9zBT3/6Uz7+8Y/zzne+k+uuu44333yTW2+9FYDzzz+ff/u3f+NTn/oUCxcu5KCDDuI///M/AfjSl760a1vPPfcc73rXu3a937Eje+CciQGG0WzUFdCDBaEZRlExZ0DhsJwBRqFQMaDVMzj3Y2eAiQGtw+jRoxkzZsyu911dXWzfHt1I/9KXvsTPf/5zfv7znwPwxS9+kfe85z1ccsklfO1rX6tY//Of/zyvvvoqX/ziFwF48cUXmTFjBv/4j/+4Swx4xzvewUMPPcRNN90EwIoVK7jppps4/vjjy7bV09NDe3s7tWBhAobRbCxEwDCKj4kBhcOcAUahMGeAYDkDjH7AkiVL2LRp067HV7/61cj1hgwZwrHHHsv8+fPLls+fP5+TTjop8jvveMc7Kta/6667mDFjBoMHy43z4IMPcuyxx3LccccBcMABB3DmmWdy++23l31v6tSprFy5kmXLlnHTTTdxwAEHZP6t/fBWNYw+hokBhlF8bDaBwrEseH6jqUdhGAEmBghbgD2B3YDlzT2UemM5A1qHadOmlcXfd3V1Ra43fvx4Bg8eXDE6397ezsSJEyO/M3HixMj1hwwZwvjx41m9ejW/+tWv2HPPPXnwwQcZMGAAQ4YM4brrruPf//3fd33n0Ucf5YILLuCll15iwoQJ/Mu//AsLFy7ksMMOY/369al/q4kBhtFsTAwwjOJjzoDCcTXwFPBAk4/DMAATA5SVwAHAPsCTTT6WOmPOgNZhy5YtbN6cfm62UqlU9n7AgAEVy6qt7y6fNWsW//zP/8yll17Ko48+ypQpU/jhD3/IqlWr+Na3vgXAnXfeuev7zz33HA8//DBLly7lwgsv5Pvf/37qYzcxwDCajYkBhlF8TAwoHF3A3c0+CMNQTAwQXg+e92nqUTQENyTJxAADYO3atfT09FS4APbaa6/YWP7Vq1dHrt/d3c26desA+OY3v8mNN964Kw/Bc889x6hRo/jJT37Ct7/97UihYevWrTz77LNMnTo102/IlDPg8ssv57HHHmPTpk20t7czd+5cDjrooNj1r7/+ekqlEpdddlmmgzKMlsLEAMMoPjabgGEYSZgYIKgYsBcwLGnFvskK4C1gXbMPxCgE3d3dPP7448yePbts+ezZs1m4cGHkdx5++OGK9d/97nezaNEienpEcho5ciQ7d+4sW2fHjh0MGDBgl4vAZ+jQoRxyyCGZpxfMJAbMmjWLa6+9lhNPPJHZs2czePBg5s+fz8iRIyvWPfvssznhhBNYuXJlpgMyjJbDxADDKD7mDDAMIwkTA4QtwAZgADCpycfSAN4JHI0IAoYBcNVVV/GZz3yGiy66iIMPPpirrrqK/fbbj+uvvx6A73znO9xwww271r/++uvZf//9+d73vsfBBx/MRRddxKc//Wn+7//9v7vW+f3vf88ll1zCeeedx+TJk3nXu97FN7/5TW677bZdIsGVV17JzJkzmTx5Mscffzy33HILY8eOLdtXWkp5H+PHjy+VSqXSKaecUrZ80qRJpddee6106KGHll555ZXSZZddlnqbkyZNKpVKpdKkSZNyH5c97NGnHn9Dia9T4vgCHIs97GGP6McJyH36IWfZcEoMLsCx2cMe9mjuYzBSPnydEkMLcDzNfnwoOBczC3As9rBHhkfefugll1xSeuWVV0qdnZ2lRYsWlfWN58yZU7r33nvL1p85c2bp8ccfL3V2dpaWLVtWuvjii8s+HzRoUOlf//VfSy+//HJp69atpRUrVpSuueaa0rhx43atc9NNN5VWrlxZ6urqKr3++uulW265pXTIIYdk/s015QwYN24cQFnGwgEDBnDjjTdy5ZVX8vzzz1fdxtChQxk2LPQRjR49upZDMoy+hzkDDKP4aIDoZOA0ZMRrCmKJ/XmN254MHALcj5UDRj4OQYYqX232gbQo6grYSXnK+VbldeAIovMGjEdcA2+m3NahwGxgLXALkiwkLwcD7wNuB16sYTt9hfOQNuZ/ATuafCz9nB//+Mf8+Mc/jvzsoosuqlj2wAMPcOyxx8Zub8eOHXzjG9/gG9/4Ruw6H/3oR7MfaASZwgR8rrrqKhYsWMDixYt3Lfunf/onenp6uPrqq1Nt46tf/WrZPI5Lliyp5ZAMo+9hYoBhFJ83EEFgDDALmIo0aPcFomcPSsc04BPACUgjtZmMJezUNJqRwCcRz20RGQDMAC5CxJoiMwlp9H8CGNXkY2lVLESgnLgkgscClwCfJWz7xDEI+ChwLjJN4VTgwhTfi2M0cDZShh+Vcxt9iYmISLg/sF+Tj8UoNLnFgGuuuYYjjzyyTJU45phjuOyyy/jkJz+Zejvf/e53GTt27K7HtGnT8h6SkcTewO7NPggjEhMDDKP4rAZ+APwOeBZYACwNPjss5zYPQhq6g5ztHJj/EGtiIvB3wGX0Tuf3mGA/s5ERxCLRhnRW/hppSM+MWGcA8A5kpLFe7A6ctIsEtwAAI/lJREFUHuw/CzOC5yGIqGT0PtrZsnpcWI2k3h9J2PY8A3g/Ut4NRUb8kzgCEUt7gIcR58sk4OM5j+lMYETwuh/OdFCBe34PaNpRGH2AXGLA1VdfzVlnncVpp51WliDwlFNOYa+99uLVV1+lu7ub7u5uJk+ezPe+9z1eeeWVyG1t376dzZs373ps2bIl3y8x4hkLfBr4XPDaKBYmBhhG3+AtZN7s3wB/Ap4Ilh+eY1sjgA8gDeNngceC5e8lFAeSOBnpuE/JsW+fgciI2ZDguD4BnI90TD+PNL5r8hFGcKTz+iyKJVa/B+l0qN17PyonYj4qWO8jwAep3VExArgAER4+h4gQaRhG+fV3HMXM4D4U+BCSea0vMAQZjU7jtBgBvCt4/XjDjqhvsQNxU4Fc04cCJwbvXwueq5Wb2pldANwF/AIRBiYh/40yiOqzvBwcbG8HEsoxmuyiW16yJp0dQLaJ3wciwuRHEdeD4ooBrsg8A/hM8DgfORe9wQDknLf10v6M1GTOGfCjH/2Ic845h1NPPZXly5eXfXbjjTfyxz/+sWzZXXfdxY033sicOXNqOlCjBg5E/unBiCr7y+YejuEwjLDhb2KAYfQtXkI6jGphfTnDd09HhMB2YC7SYDwM2BM4HhkJi2MIcApSfpwPzAceyXjsLu9E3GNbgeVII/Kg4KFMBeoVxTcRmXasB+kw7IeMaM+LWX8s8FfAYuScN5KxyGgkwM+AjwHjgmNcFiwfAJzkfOdI5Pf8lOxxuYOQzskHCBvJIxEB5hdA1AxRgxC79RSkszqUMP56z+Cz6BmtmseRyEjvocBKYE1zDyeRkcAXCIX6XwEvRKyn7arZhPfyo71xgH2E+5H75yjCjv/9iGDyJUTwGgtsivjuMODtwWtNP7YOcRzsEzw2IOf/s0iHdg6SVyCKo4LnR5BR8knA24CODL9nN0TMGoiEQUTlHBiEdLbXIWFl70XuyXnAopT7ORu5X5YgInH0WKowCTgn2AfIvfUAUh6NR8qjQcF6w4P376ZcoHg/cBNS/2wiFGvqzYcIr4PXgn12BvvdBvylQfs1qpJJDLj22ms5//zzOfvss9m8eTMTJkwAYOPGjXR2drJ+/fqyZIIg8y+uXr2al15qdA3eC4xEbIMvAU8191AyMdl5PRUpZJ5pzqGUsTdSMDwCbG7ysTQLbWxsRxrGhmH0HbqRhupRSKd8EXA31ROI7Udo7b4D6Qx2Bt/9AHAq8BzS6RyHNOBWB+uBdFaHETb0zkBGhO5OedyDkdHMzYhYPCtYfidSN0xAOpoTg/WmIB3MamLARKTBvMQ51ijUFbAE6RhcAExH3BZR5+6E4PPpyDm+izChYxz7ILbge8km0hyDNPaXIx3WZUgH4EBCMWAK0vjuAm5GnAETkZHPh7zt7Yk0xDcHj03B90YgjeMpSNk/OHi+Afk/piCug59QPofZaCSPwR7efv6MnLsPIKOEj5D8H/Q2GkozCOl8/ALJY11EjqQ8Lv00pOPnHu+BSEfXdfHcTrHOebNZipyT9yPXdwfwIHLvrkDEgMOIFj4PQs7tm5QnGnydUAx4FhEyJwSfnY8IeFEDKxoW8CIipk4Kli2OWDeODyI5YpS/IL9vg7PsXcj95/PXyL17X5V9DEHaxQOReP9DkN//GPA0co+PQ6ZvHEzoBtgZfEfDVdQV8DIiCoxHzvdgRAjYANyDlBfTkHN3EFKnzCHM+VAv9iIUAnYi5/FTwe+ZFCz7CVLPpWEAxS0/+iCZxIBLL70UgPvvv79s+Sc/+clccxr2Oc5EbrCJlIsBRyI35rKI7xQBjRVaiiitZyAN2GZ2PndHGoAjkNGCXyKqem/te0gv7i8JCxEwjL7NncHzUYhFezfgf4jvFIxD8gQMQBp3K5zPnkZEgn2ASwnjW0E6mdrZnx48P4g0pmYjjeI9kRHh5QnHOwgJG5uICA6HBsteIBSJ2wnLxz2QXAJTKB/FG4EIGCWkEfl+QovqXcQ7GwYQ5gh4Bhn1Wos0Vo8g2mb9duf1DKQheTPSqR6ONIL3D45jQ7D/M5FG5geAa5CRJ5+ByHnbhHQsQMQAkM41wfGpGKCoK+BxpF6dj4zOzQq2o+doKNJx9xOedSHnTUMLtCV2OzJidgti4R2PJAacQ9jwPRn5T7Yg//VbwfaWBL/nvcj/sBflDethVM/CPjjYT72zjo8mHJTYjvx/x5I8UnoMcr29RGg3H4Wc+8XOskag99efkOtjL+TaUjFMz7MrBDyCzeQQxePI9X8y8AdCEe855J49gsqyYgjijoLQFaBoJ/VtSFl7SvB+G9K2uxC57t9ChMBupMwdjVzXq4LvHR9sIy37I9dtD1JOT0fKxM8C1yH34z6EoRBdyP3/eHBspyAi7w4k7IHg88lIGaL3nIYkbUKEi+lIuf4+RGh4K/id6xGxcgziQrgNKWv2Ra5PFd+eD5aNR+4d9/w/i7iR/orQBTYI+Bvg/1HfdunJwfNiRIT4RHBMit5TaUzkpyH35XOI02RD8upGdTKJAQMGDMi8gwMO6CdZK6YRqlrjkAt3J3KTfhApIH6AFAiNYBDSKNiKJLCKU8SGIIWDGjR2IxxZuhmxvo1BCq3lwTr7IDfhM2S3uA0O9pF2ihiQRslHkcbkTqTC/yjwQxqv9I0ELg6OYRXwv8DGBu/T5W1IZbEAKchNDDCMvk0n8Fuk/PwI0kB8L9Kx8xkSrDMaKX/+4H1eQpwCnyUsHzchDbYZSMNnKGHn+GmkrH8LibtXa38XUhe9hZQtbwXbeTLYzt7B97VT/mzwG6JYh9QVk5FO8f1Ig/VCpKN8c7Dv0YSjU4cQLwYcgNRBW5GRtRLSKTwDEVN8MWA0IlyUkE7yGchI4N/FbB+k464NzVGILfZ3EetNRxrCII3V15H6aAuhBVhF/r2RTuGQ4DfsIAzNeBrpvO6PNFI13GEGUsZvDbapszVoTP8G5Px1Ou8J3t+E5A7YD4l3fiHYlooVt1I5ALEDEROmBN9TMeAYpDOxBPg10fXsIKRuHIcITw+Rb8BgAHKuBiKW5RJynQ0Iju1ZRKh5F3KOo9pM45FrCqQD1Y6c078OPpsGXOv8jhORjs5cki3VIPdVG9HhFyD/8d7IuXwc+a9ODh4qBhyDtP22IkJTJ+YISGIBYQdYWYyUk5MIO7cg/8/HkHZpN3JvuagYsDfSJh6M3AfzEJFzAqFToIQIqOoKaEeuaU11th/w/yHX4NPBMXUjZaV/7avo8CRSti9ERN0JyHX5a8TeryLv7ci9rsLgluD3no4IYtuQa3t3pMN+c7Ceio5LkbrgT0g5dTxy7WvZsTthnpV5iBDVGezzdOT67EHEtDXBNtw8JOqIeAgprychneuJwX7OoXpIsc4IMwapY9Yg9/RiygXFNsL+04NInfJz5B7fgrgeLgqO7xPB+XieaAH3JEIn21HIYOyjiPBjU3rmJnPOgJZkBHKzK4OQSr2D0DI0GKmQylMm1I+phMrdCqRA8hmGjCbsiYxqzEducpACtAspNKcjBc7yYLvnIg2cvZGKNG0s3z6IELI7cjPeSXkjY3SwnzWUj1B8IDjGjcCNiFWoDWlgNdpdcQhhYbo3Uqjc1uB9KgciHQGN1foNJgYYRn9hGdJBOw/p1K6lUlw9ESl33kKEyCir+xvAfyKd2FeQBt7fIg20I5GyeiDS+NMG9FPB944L1hkWPHwr+XGEZc5CpNP4EtLgTBJiH0fEgGMIO+6DCEfGRiP14Y1IJ30fpN6MasxpiIDbYHwK6ZRPROpUN2ZVG8ergu+8ioxc7Rccs7oT1iJ14wmEQsBLSB13NNJAX+4di3asVdjXuNsnnGPbglhtpyKitY6uuw4AEJHkAqSBek/wfXUQzCd0Ew5B2g8jkXoxLtxhHSI2zEI6oi8gHYKhyH8dV1eqGLAv0sg+FnFtQJjELUqomUr4+09D/otfIf+X5lDoDo75NcIOFYg49dfBbxtCWMeqGKXbXYy0TaYjwvgZiMDjo22dLcHvnYB0OpTxhALJBMQZMwhpy/yE+JHCkYjA0ob8f7dTORWgXp8vBcf+CHLO9kXO/1hCG/h9WN2dl63INTwF6Sg+gJzbTyDXyzakM7re+14Hcl2NQq7RLYjQtxH57w9CyqOTkf/tSUIHgAoJ65DyZG/kuhmHJDnUWUN2IuXJquCxLTjOnYS5ONYhbbiLkWvx4uC4tyBt4e2Ud04fDT6fgYgCLociI/mLCcs7vb+7kPv4z0gZPAIp594R/L7FhLH2rwa/X0fhH0Su79VInfKx4LytJWyT70TO8xREDNgDuUemIsJYUn/ghGAdguMaH/yWWYgIMyI45glInfUXQhFuM+Viwx8RkfDtweO9wfHcQXgej0KEXZD/Yc/gON8R7Pf14Lfdh4UQZMTEgGoMRjpwY5CLbBAyEr4bUii505PMQNTPKCveaKTAWRnxWRrcrKuzEYXarYSGIR1zrXSPQxpA+g8vD55dMeB15LcNDI55GPBhpBGzN1Lxb3eeO5Hf9zLSiPprwgzTJyDnZCdS2OjogNroOpACchDSId+BNDTWIjf88cFxVRMDBiAFjF8BjyadK0PjqF5BxIe3J6wbx96IonsP6UMNJiMxWfp/qGHGxADD6D+8iDSC3o1kmt9AmPBuCKGF9E6SHUkrvPfaAT+esAzxR8zWIJ2bu5C6ZpT3OIyw4fYc0kGdn+5n8QLSAB8HfDE4hu7gN2md8xDSQF4T7OftwX5chhAK1G7ems5g3aODhysGaBmt0zhuRuLNhxPa7f1jPR8ZFZuLjJIdh9RX1xOO+O2FdPB2IDbfQ5DOSBeVcf+3Ig1kzWC+jcr8DMsQh9yeSF2mGcs7vN/ajZyndVTnUURQeBvSUFYnx4MJ31Gr+n7B+u93lu+HjMgvp3JkXK3xS5ERukOQ0cEjkXrX55eEuRhmUJ7dXQUavfZ2IOfnqWD575HzeThyHfs5HVQMeAAZIfwocg5U8DmWcKT+bKRdsQNpG3wEGXX0RwkHIyJSW/D+iOB3/pbyxJAqBuj9tQURh45H/gPledInhDOieRbphB6B3LcfR8qYjcB/E+84fR0RqLQdqWXpekK3zp7BOucStlPdOPifErZV90HatG8jnPZwr+Ax3fnOYsqFpjWICHh6sO52pMyJEkFBRvC7kVF4CJ0KJyHX1iBC15bfFi5R7nr5I9IGdR0pKgbotl03xhtIuXkalTnP3iK83tcgnfaDkTojTgwYRhjKcTtyb05G7s09kPvQJ6ncegz5zQcj5cJEpPPfgzjophG6hdyQuSmI60ld0CDlWzWHkFGGiQHKMKRxMRhpsOjzCUiF0YnYeGYTigGvECqOPcH3j6Pygn87UgkNR26aPzufDUU65m9HCrZnqIyFG0qozG9GhInZiBo6CqnspyIFSQ9yk5wcrLcdaYy68Y8ghdGHkELyGURJu4SwsQihyu9yFtKgenfw3WeDbb6P8szTSjviHGhDhAatoB9wfufTSKFyCHJ+4qw+U4LfvRdiyXoeKaxORQr+Owin5wJRI/dFKvKdSENBO+HzkMbIOETNjMtA6zME+S93R/7PtJNkvBO5pl5E/uvRwe8wMcAw+hcLkcbQsUiZ93OkHDwaKa83kC1pFYQj51o+b6Syo630EN3ZfCw4npFIWZmFHqRxfhZhY/V+pCH9V0iHSd1qLwfHOTXiGDXx4QYqM1Y/iZyjwwgbzVApBij+iK7yBnAVUt5uRxrNByPl/CmInRRCV8BLyLlKaqhuQ2z7nw6Ofz7lSf2Ux5C68ERCIXwh+S3kWxFXxomEDe91RGe2V14P9jcOceGBdJDuRFwrhyDXwf8jrGtHEtbfdyFtnvcRdoReRM7/CMK8SVMJO/HaDroVERn02tsv2PYyyv+v1cExnRTs51rC/3s4YRK0l5Br6z+R62A50u44Mtjnx5C2zDbgvxARaELwu91wiAOD/eyBiD1/QEYvxyNujkcRd8wZiCC0lXKBYn5wHNOD47yHxs9q0Qq8iJQteyKu1mGIAHAj0TMMKE8hHfi7iM98f2ewjtumdQfjdhImrlZbujIGKecmBs86sFWeLk14MNjPOEQISEqAtyM4ZpdByPU5ERnQA6kvosoXH79cWeEs/x2VuT/WEe3E8VlM2Cm/N2ad45DyYC0iiqlYsRBpk09HxJm/IL9lA8m5bAi29WDwmIrc3zOQa+EU5N5/knIh9i+EYu7RSBv/IEwMyIiJAcr7iZ/ztAdpCKwhVAXbCNVDkMrh3Yhd5RnkJvwr5MJ0FfMzkMJuOdIh/CyhmgVS6T9IebjBNKQTug5RsT+NXPTLkIv+4GC9N4PvLUFumPHBMbuxT5sIRy+GITfMb5HCY26wrWWEN9JQ5/F+pBP8caTCXo9U/qXgvByHNMTWBss6kEJtCNL4mIZcce2UN7xWEiaQOoTKEa+JyLl1EzidjhQMH3aWHUcoBoxA7GajkQb4/cFvG4g0VtYgBadaktT18e5gf/cRXZicThinpQllqk3DMogwVuse5BxMCX6PiQGG0f+4HSn3D0Q6KL8gtIzn6Rx2IvXKsUjZeivVk8H5bEMa2XlZhdhwj0Lqv4eRem4DUqdoPfMSIn4ejtQxzwbLuglHXaNms9Gwh92RekBnNRiNdFqzTHe1k7Cj24WIC+ciIvlzyPlUMSAqYWEUa5BO6R7ECzFPIyPvWkdsIDqkLwv3IPW25hvQhnccauWfhNQ9Swg7ILcFy/dAOsdzg+WHBeu+gfzONYTOgrWIHVo762uRc6kd9jHBse1ERAo37GF5wnHehwgLbUjnwR3pGxgcQ4fzm150vvsEMlCjQtGdyPX5KySc4FDkf1iEtMO0bbc5+M3Lgu3NRkSWE5A21dDgd9xJeUeqBxnAeCDh9xjZ6UJEFw3ffA1Jvho3sq68QLIgBnLvXYf8x0chAmoaNw6EM3+kEXx2Iseclx1IeX5WsK23yD895WuIqLWB2hJsLkHuuT2Qe2wZ5WXOvojDAKQt737WhZQ3vuiRlZeR83AC0u7W4/p9xLrdSH3RjfS5ptVh/y2GiQFKT/Do9l53IhWAKm4dwfNuiDI9IFj2CKKETUA6y9sJQwhKSOU1FKlcz0XUuSMRIWAzosSNCj4/GSkM1aqoFdlzyM3+ABLbdDZh9t//pNxaup34wmAZIgZsQNRzbZj66qjPn5BRcR0ZeoKwEHiFeCWuGynsPoOctyjF8hnkhp9OKAaMdZYNQP6TPyMF+3hCFfUJ5FxqzOebiOgyOvh8JlJxaIiA/salSEF3YLD/jxI2cC5E/pM7kMJ5DGG8Jci5nYT8VzfF/G5lH+S/30I4VdUUpNDSc29igGH0H3YiTrLPIGXV3yOdLXcEPSsPInXOk1SGEfQWJSqP3+8Yv0YYhnVw8NiONOSmBOs8SzRPIWX+MUiZrOsvp7YM988H+5+GhAusQ8rkV8k2t7XGEMexHamP3oF0yP+H6lMgVmM7YZxyWpYj9dMaQsEepF3xG6TDPB2pA58hnIPdFeJ/hzTIl1H+G/Tam4CIE+oKeJNsv3U7Ur+ej5yv5cjAgHYykjpiDyK/byvSJtD/8HVEiDsbEaTeGSzfiQwU3EsoonUH+9dQg7FIG+MWouePNxrDnxEx4CWkPVrr/eLyFjLYtZDs4mlvsgaZErEe+Ika87AdufcPRQbVtiPlmQ6knYbUZ89TOXhXT/6EDHjuhpQ7bn8liqXIPbw72Ry/hokBu/gt8dmUXdQZoGIASAWm6uBnCN0C25CK+FWkIBqMXKRvQ6xpIJX0TYQd9zeQWNPZwfdXENr3tAF1L1IRa+jAU2RrHC5AbpjHydYJXYxU2vsgDbOnMny3CxlVGkZ0bP8zSAFzIKI67oY4ETRM4VmkYOhACqZZyOjBy4hSOAo5H4ch/8d05Ny+gZzv8whjBV0xAKTRqgmptiGNgyODbe2PiDUqgBD87gXIzAzTiE6ycmRwjG8QWtE0BkxFkwOcc2FigGH0LzqRuOrPIOVTCRkNzZOhHaTuub4uR9ZYdgI3EM4rfQRSnmu8uzrBongaKTcnB+vPCJYviVk/C3cgZe7+hE4tP+6/HvwJqY+XUt+OTRYeROqyp6jsBL2KOOVOQ4QRjZXeSbmw00O0g+MtREzZA6mr3XZQVl4K9nEkIsavD7bbQfxsFCB16s9jPnsSaRucgtT5ryECQZx9eynwY8R1s4zGTlloVLIM+HequwFqIW1SbCPkfsS5OgkRTvcjHCwDuZ9+S2MT9W1Hwn+mIfd1tbpzOyIqTgm+Y2JAakwMyIqKAbsTqulqX9yINP4uQm6e31A+6tCDXNinIda0gcgoglv5PIzcgKcgFfVqZFR8CeGFrTbRTyJqdlxMTxxbyN8ImoeMmj9D9mkUu4lvHHUgN/sxSMddR/VXIHYf9xw9Gqy3FTnHJUSomIY0II8K1nsEUYT/ljCr9l8IrWJrgt+gsx5sRxwW7cF3zyG0qZaQUYclwWc9hDkLzkDiaXci/8cHCedUdrN5qxiwOtjHBEI7qYkBhtH/2IBMPTYGqR+KPDpVb9YgFvd7EAH5cKQxeU/CdzYizrfTkLjvQUjnM6pTmpWNwb7PCN4/T7bQg7T00PyR5a0kjxA+gAgjk4NHN1K3p4lTBqmX90D+z1rEAAg7FNOR0bxtSH2a9liieBxpT+xBGLaYxDaSc0YYjaWRQoCRj3akPTwAuY8mUZ4/4Tf0zlR+GwiTQqZBHWjTCBM0Lq//YfU3TAzIiooBI4PHZspHyFcjjb/hRGdC7ULi0Z5ERieiLKN/CrZ9LGHWUb9i70KyoQ4k/0hTHlYC/9Ggff4RsYupELAQES38inwr8INguVqGlgTHpJ3vDqTh143Y/mYgoQLuyEcJGRXQJEm3Es4OsBo5v0cE+3iZyg77vUiSkwMRm+FKJGxgLFJILkLiEEcE66sjQMM63OzQ6h4wDKN/sQ1r7L5OeSbvJBYgoQXqxvoz9RthfwypY/aicdMA9wV0QOE8JB/BfOKn44viVUSQP46wFZlXDNiJCAIbkf/9D9RnRG8n8dnoDcNIRwm5H9dSH1G20byE5EPZDwnZfhXJ2WMkYmJAVrqQTqEmfouap1aTjyTRTvK0dH8gzNy7jOiG1E7yZymuhUaJD1uR8/keRAhIsgn68aNdSMdeQyfcbNR/IT4u9AkkDOMBKkdzekiO712LxFX+DSIoqKiwBgn92IAIRechjRJ3KjGN3XwGsRCnTWxjGIbRn9FM2J8lzBNTz23fQO+L6EVkEyJ450FDIIYH77dSmxW7ROgiMQzDyMtGxPG1L1IurUTK+2b0lfoQhRUDJkyY0OxDiKeEjF6/hFSok5JXz83DSCfytQbuo2isJ0zIl/U3r0TcFH9BxJg03+8mzLCd5xxvQISEqcH7zcj/NoLQEfCbhO2/FTxa5f81DMNIw22EU+SNq7Ku0fvcjDjxRiBCwMTmHo5hGAYgbfLxSJ+gh8xlU6H7nw2icGKA/glPPPFEk4/E6NP8uNkHYBiGYRiGYRhGX2PChAm88UZrZBQdQGNzQebi6KOPpr09yUNff0aPHs2SJUuYNm0aW7ZkzYxnGHYNGfXBriOjVuwaMuqBXUdGrdg1ZNSD3r6OJkyYwJNP5p0HuO9RSDGgGYwZM4ZNmzYxduxYNm+2bG5GduwaMuqBXUdGrdg1ZNQDu46MWrFryKgHdh01loHNPgDDMAzDMAzDMAzDMHoXEwMMwzAMwzAMwzAMo8UwMSCgq6uLr3/963R1dTX7UIw+il1DRj2w68ioFbuGjHpg15FRK3YNGfXArqPGYjkDDMMwDMMwDMMwDKPFMGeAYRiGYRiGYRiGYbQYJgYYhmEYhmEYhmEYRothYoBhGIZhGIZhGIZhtBgmBhiGYRiGYRiGYRhGi2FigGEYhmEYhmEYhmG0GCYGAJdccgnLli1j27ZtLFq0iJNPPrnZh2T0Ia644gpKpVLZY9WqVc0+LKPAnHLKKdx2222sXLmSUqnE2WefXbHOFVdcwcqVK9m6dSv33nsvhx56aBOO1Cgy1a6jOXPmVJRNDz/8cJOO1igil19+OY899hibNm2ivb2duXPnctBBB1WsZ+WRkUSa68jKIyOJz3/+8zz99NNs3LiRjRs3snDhQs4444yydawcagwtLwace+65/OAHP+Db3/42Rx99NAsWLGDevHnsu+++zT40ow/x3HPPMXHixF2PI444otmHZBSYUaNG8fTTT/OFL3wh8vOvfOUrfOlLX+ILX/gCxx13HKtXr+buu+9m9OjRvXykRpGpdh0BzJs3r6xsOvPMM3vxCI2iM2vWLK699lpOPPFEZs+ezeDBg5k/fz4jR47ctY6VR0Y10lxHYOWREc/rr7/O5ZdfzowZM5gxYwb33HMPv/vd73Z1+K0caiylVn488sgjpeuuu65s2fPPP1/6zne+0/Rjs0ffeFxxxRWlJ598sunHYY+++SiVSqWzzz67bNkbb7xR+spXvrLr/dChQ0sbNmwofe5zn2v68dqjmI+o62jOnDmluXPnNv3Y7NF3HuPHjy+VSqXSKaecsmuZlUf2yPqIuo6sPLJH1se6detKn/rUp0pg5VAjHy3tDBgyZAjHHnss8+fPL1s+f/58TjrppCYdldEXmTp1KitXrmTZsmXcdNNNHHDAAc0+JKOPcsABB7D33nuXlUvbt2/n/vvvt3LJyMypp55Ke3s7S5Ys4Sc/+Ql77rlnsw/JKDDjxo0DYP369YCVR0Y+/OtIsfLISMPAgQM577zzGDVqFA8//LCVQw2mpcWA8ePHM3jwYNrb28uWt7e3M3HixCYdldHXePTRR7ngggt4z3vew2c/+1kmTpzIwoUL2X333Zt9aEYfRMseK5eMWpk3bx4f+9jHOP300/nyl7/Mcccdxz333MPQoUObfWhGQbnqqqtYsGABixcvBqw8MvLhX0dg5ZFRncMPP5zNmzfT1dXF9ddfzznnnMMLL7xg5VCDGdzsAygCpVKp7P2AAQMqlhlGHHfeeeeu18899xwPP/wwS5cu5cILL+T73/9+E4/M6MtYuWTUys0337zr9eLFi1m0aBErVqzgfe97H3Pnzm3ikRlF5JprruHII4+MTKJs5ZGRlrjryMojoxpLlizhqKOOoq2tjQ996EPccMMNzJo1a9fnVg41hpZ2Bqxdu5aenp4KVWmvvfaqUJ8MIy1bt27l2WefZerUqc0+FKMPsnr1agArl4y6s3r1alasWGFlk1HB1VdfzVlnncVpp53GypUrdy238sjIQtx1FIWVR4ZPd3c3S5cu5fHHH+drX/saTz/9NJdddpmVQw2mpcWA7u5uHn/8cWbPnl22fPbs2SxcuLBJR2X0dYYOHcohhxxi0wsauXjllVdYtWpVWbk0ZMgQZs2aZeWSURO77747++67r5VNRhk/+tGP+OAHP8jpp5/O8uXLyz6z8shIS9J1FIWVR0Y1BgwYwLBhw6wc6gWansWwmY9zzz231NXVVbroootKBx98cOmqq64qbd68ubTffvs1/djs0TceV155ZWnmzJmlyZMnl44//vjSbbfdVtq4caNdQ/aIfYwaNao0ffr00vTp00ulUqn0D//wD6Xp06eX9t133xJQ+spXvlLasGFD6QMf+EDpsMMOK/3yl78srVy5sjR69OimH7s9ivNIuo5GjRpVuvLKK0snnnhiaf/99y/NmjWr9NBDD5Vee+01u47ssetx7bXXljZs2FCaOXNmacKECbsew4cP37WOlUf2qPaodh1ZeWSPao9vf/vbpZNPPrm0//77lw4//PDSt771rVJPT0/pXe96VwmsHGrwo+kH0PTHJZdcUnrllVdKnZ2dpUWLFpVNhWIPe1R73HTTTaWVK1eWurq6Sq+//nrplltuKR1yyCFNPy57FPcxa9asUhRz5szZtc4VV1xReuONN0rbtm0r3XfffaXDDjus6cdtj2I9kq6j4cOHl+68885Se3t7qaurq7R8+fLSnDlzSvvss0/Tj9sexXnEceGFF5atZ+WRPZIe1a4jK4/sUe3xs5/9bFdfrL29vXT33XfvEgL0YeVQYx4DgheGYRiGYRiGYRiGYbQILZ0zwDAMwzAMwzAMwzBaERMDDMMwDMMwDMMwDKPFMDHAMAzDMAzDMAzDMFoMEwMMwzAMwzAMwzAMo8UwMcAwDMMwDMMwDMMwWgwTAwzDMAzDMAzDMAyjxTAxwDAMwzAMwzAMwzBaDBMDDMMwDMMwDMMwDKPFMDHAMAzDMAzDMAzDMFoMEwMMwzAMwzAMwzAMo8UwMcAwDMMwDMMwDMMwWoz/H6Cd8pUzAfuQAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "fig, axis = plt.subplots(1,1,figsize = (12,6))\n", - "ser = serial.Serial(Ambit_Port, 115200, rtscts=False) # This is Ambit\n", - "\n", - "l1 = axis.plot([0], 'g')\n", - "l2 = axis.plot([0], 'g--')\n", - "ax = axis.twinx()\n", - "l3 = ax.plot([0], 'r')\n", - "\n", - "START_TIME = time.time()\n", - "T1, S, R, F, B = [], [], [], [], []\n", - "T2, Temp1, Temp2 = [], [], []\n", - "try:\n", - " _wait_for_line(ser, target = \"NEW Name\", input = \"hello\", print_out=True)\n", - " if not _wait_for_line(ser, target = \"Start\", input = \"ww\", print_out=True):\n", - " ser.write(b'E\\n')\n", - " raise NameError(\"Function not started\")\n", - " \n", - " if not _wait_for_line(ser, target = \"Run\", input = \"\", print_out=True):\n", - " ser.write(b'E\\n')\n", - " raise NameError(\"Run not started\") \n", - " plot_timer = 0\n", - " while True:\n", - " try:\n", - " _ret = Trigger(ser)\n", - " if _ret != {}: \n", - " T1.append(_ret['T']/1000)\n", - " S.append(_ret['S']/_ret['R'])\n", - " # R.append(_ret['R'])\n", - " # F.append(_ret['F'])\n", - " # B.append(_ret['B'])\n", - " l3[0].set_xdata(T1)\n", - " l3[0].set_ydata(S)\n", - " \n", - "\n", - " ser.write(b\"T\")\n", - " ser.flush()\n", - " _ret = _parse_data_line(_readline(ser, .5))\n", - " if _ret != {}: \n", - " T2.append(_ret['T']/1000)\n", - " Temp1.append(_ret['o'])\n", - " Temp2.append(_ret['a'])\n", - " l1[0].set_xdata(T2)\n", - " l2[0].set_xdata(T2)\n", - " l1[0].set_ydata(Temp1)\n", - " l2[0].set_ydata(Temp2)\n", - "\n", - " if time.perf_counter() - plot_timer > 1: \n", - " autoscale(S, ax) \n", - " autoscale(Temp1, axis)\n", - "\n", - " axis.set_xlim([-1, max(T1[-1], T2[-1]) + 1])\n", - " display(fig)\n", - " clear_output(True)\n", - " plot_timer = time.perf_counter()\n", - "\n", - " except KeyboardInterrupt:\n", - " break\n", - " \n", - " \n", - "\n", - "finally:\n", - " ser.write(b'E\\n')\n", - " ser.close()" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[23]" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "T2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "NEW Name Here Ready\n", - "Start\n", - "Run\n", - "Stop\n" - ] - }, - { - "data": { - "image/png": "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q6uL//mf/4k777wz9uzZM7K+devWWLhwYfzpn/7pmHOeffbZmD59eqxatWpk7eabb44f/ehHMXv27Dhx4sSYc+rr6+Oyyy4b+bpUKkVPT080NDREf3//hW4XAAAAqlIqlaKvr++idmhFT8CHh4fj0KFD0draOmq9tbU1uru7xz3nM5/5TPz2t78dtXb27NmI+OjJ+XiGhoaiv79/1AAAAMBUVvFb0Ds7O+O+++6Ltra2mD9/fnR2dkZzc/PIW8o7Ojpi165dI8f/4Ac/iNtvvz2+9rWvxbx586KlpSW2bdsWP/7xj+O99967eFcCAAAAl7DaSk/YvXt3XH755fHQQw/F7Nmz4/Dhw7FixYo4fvx4RETMnj07mpubR47ftWtXlEqlWL9+fTz++OPx61//Ov71X/81/uZv/ubiXQUAAABc4ir6HfDJMhHvvQcAAIDzmfTfAQcAAACqI8ABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACBBVQHe3t4ex44di4GBgSiXy7FkyZKPPb6+vj6+853vxC9+8YsYHByMo0ePRltbW1Ub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BwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIUFWAt7e3x7Fjx2JgYCDK5XIsWbLkvMeuXLkyurq64v3334/f/OY30d3dHcuXL696wwAAADAVVRzgq1atiq1bt8amTZti0aJFsX///ti7d2/MnTt33OOXLVsW//Iv/xIrVqyIxYsXx759++IHP/hBLFy48JPuHQAAAKaMmogoKjnh4MGD8dprr8W6detG1o4cORJ79uyJBx988IK+x+HDh+OFF16Ib3/72xd0fKlUir6+vmhoaIj+/v5KtgsAAAAVm4gOregJeF1dXSxevDi6urpGrXd1dUVLS8sFfY+ampoolUrxwQcfnPeY+vr6KJVKowYAAACmsooCvLGxMWpra6O3t3fUem9vbzQ1NV3Q93jggQdixowZsXv37vMes2HDhujr6xuZnp6eSrYJAAAAl5yq/ghbUYx+13pNTc2YtfHcddddsXHjxvjKV74Sv/zlL8973ObNm6OhoWFk5syZU802AQAA4JJRW8nBJ0+ejDNnzox52j1r1qwxT8V/16pVq+Kpp56KO++8M1555ZWPPXZoaCiGhoYq2RoAAABc0ip6Aj48PByHDh2K1tbWUeutra3R3d193vPuuuuueOaZZ+Luu++Ol156qbqdAgAAwBRW0RPwiIjOzs547rnnolwux4EDB+KrX/1qNDc3x44dOyIioqOjI+bMmRP33HNPRHwU388++2z85V/+ZRw8eDCuuOKKiIgYGBiIvr6+i3gpAAAAcOmqOMB3794dl19+eTz00EMxe/bsOHz4cKxYsSKOHz8eERGzZ8+O5ubmkePXrl0bdXV1sX379ti+ffvI+jPPPBNtbW0X4RIAAADg0lfx54BPBp8DDgAAQKZJ/xxwAAAAoDoCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABAIcAAAAEghwAAAASCDAAQAAIIEABwAAgAQCHAAAABIIcAAAAEggwAEAACCBAAcAAIAEAhwAAAASCHAAAABIIMABAAAggQAHAACABFUFeHt7exw7diwGBgaiXC7HkiVLPvb4ZcuWRblcjoGBgXjrrbdi7dq1VW0WAAAApqqKA3zVqlWxdevW2LRpUyxatCj2798fe/fujblz5457/DXXXBMvvfRS7N+/PxYtWhQdHR2xbdu2uP322z/x5gEAAGCqqImIopITDh48GK+99lqsW7duZO3IkSOxZ8+eePDBB8ccv2XLlrj11lvjhhtuGFl78sknY8GCBdHS0nJBr1kqlaKvry8aGhqiv7+/ku0CAABAxSaiQ2srObiuri4WL14cW7ZsGbXe1dV13pi+6aaboqura9Tayy+/HGvWrIna2to4c+bMmHPq6+vjsssuG/m6VCqN+k8AAACYSBPRnxUFeGNjY9TW1kZvb++o9d7e3mhqahr3nKampnGPr6uri8bGxjhx4sSYczZs2BAbN24cs97T01PJdgEAAOATmTlz5uQ8AT+nKEa/a72mpmbM2u87frz1czZv3hydnZ0jX5dKpejp6Yk5c+Z4Czp/sNznfBq4z/k0cJ/zaeA+59Pg3H3+wQcfXLTvWVGAnzx5Ms6cOTPmafesWbPGPOU+58SJE+MePzw8HKdOnRr3nKGhoRgaGhqz3t/f73/g/MFzn/Np4D7n08B9zqeB+xwqU9FfQR8eHo5Dhw5Fa2vrqPXW1tbo7u4e95wDBw6MOX758uVRLpfH/f1vAAAA+ENU8ceQdXZ2xn333RdtbW0xf/786OzsjObm5tixY0dERHR0dMSuXbtGjt+xY0dcffXV8fjjj8f8+fOjra0t1qxZE4899tjFuwoAAACYAopKp729vXj77beLwcHBolwuF0uXLh35Zzt37iz27ds36vhly5YVhw4dKgYHB4tjx44Va9eurej16uvri4cffrior6+veK/GTJVxn5tPw7jPzadh3Ofm0zDuc/NpmIm4zyv+HHAAAACgchW/BR0AAAConAAHAACABAIcAAAAEghwAAAASHDJBHh7e3scO3YsBgYGolwux5IlSz72+GXLlkW5XI6BgYF46623Yu3atUk7hepVcp+vXLkyurq64v3334/f/OY30d3dHcuXL0/cLVSn0p/n57S0tMTw8HC8/vrrE7xD+OQqvc/r6+vjO9/5TvziF7+IwcHBOHr0aLS1tSXtFqpT6X1+9913x09/+tM4ffp0vPvuu/H000/HzJkzk3YLlVm6dGl8//vfj56eniiKIm677bbfe87FatBJ//Puq1atKj788MNizZo1xfz584snnnii6O/vL+bOnTvu8ddcc03x3//938UTTzxRzJ8/v1izZk3x4YcfFrfffvukX4sx55tK7/Mnnnii+Ou//uviC1/4QnHdddcVmzZtKj788MNi4cKFk34txpxvKr3Pz01DQ0Nx9OjR4p//+Z+L119/fdKvw5iPm2ru8z17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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "## This block run the protocol\n", - "# Change the protocol want to run:\n", - "run_protocol = Protocol_Eg1\n", - "# Signal offset when no exist\n", - "OFFSET = 4000\n", - "# Set the Serial ports for devices:\n", - "# If auto port detection doesn't work, put in Serial ports manually.\n", - "\n", - "ser = serial.Serial(Ambit_Port, 115200, rtscts=False) # This is Ambit\n", - "try:\n", - " ser_pulser = serial.Serial(Pulser_Port, 115200, dsrdtr= True, rtscts=False) # This is external LED\n", - "except:\n", - " ser.close()\n", - " raise ConnectionRefusedError\n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "#\n", - "fig, axis = plt.subplots(1,1,figsize = (12,6))\n", - "START_TIME = time.time()\n", - "try:\n", - " _wait_for_line(ser, target = \"NEW Name\", input = \"hello\", print_out=True)\n", - " if not _wait_for_line(ser, target = \"Start\", input = \"ww\", print_out=True):\n", - " ser.write(b'E\\n')\n", - " raise NameError(\"Function not started\")\n", - " \n", - " if not _wait_for_line(ser, target = \"Run\", input = \"\", print_out=True):\n", - " ser.write(b'E\\n')\n", - " raise NameError(\"Run not started\") \n", - "\n", - "\n", - " _act_ambit, _act_blue, _act_red, act_blue, act_red = -1, -1, -1, 0, 0\n", - " _data = []\n", - " for _l in run_protocol:\n", - "\n", - " if _l[0] == 'M' and len(_l) == 4: # Run measurement\n", - " \n", - " if (_act_ambit != _l[3]): # Set ambit actinic\n", - " _act_ambit = Ambit_actinic(ser, _l[3])\n", - " if (_l[1] < 1): continue # no data\n", - " for _ in range(_l[1]):\n", - " _ts = time.perf_counter()\n", - " _ret = Trigger(ser)\n", - " if _ret != {}: \n", - " _ret.update({\"ACT\":_act_ambit,\"Blue\":act_blue,\"Red\":act_red}.copy())\n", - " _data.append(_ret)\n", - " _elapsed = time.perf_counter() - _ts\n", - " if _elapsed < _l[2]: time.sleep(_l[2] - _elapsed)\n", - "\n", - " if _l[0] == 'A' and len(_l) == 3: # Change Actinic\n", - " if _l[1] == \"B\":\n", - " if _l[2] > 0:\n", - " if _act_blue != _l[2]: _act_blue = DAC_set(ser_pulser, 2, _l[2])\n", - " DAC_ONOFF(ser_pulser, 2, True)\n", - " act_blue = _act_blue\n", - " else: \n", - " DAC_ONOFF(ser_pulser, 2, False)\n", - " act_blue = 0\n", - " \n", - " if _l[1] == \"R\":\n", - " if _l[2] > 0:\n", - " if _act_red != _l[2]: _act_red= DAC_set(ser_pulser, 4, _l[2])\n", - " DAC_ONOFF(ser_pulser, 4, True)\n", - " act_red = _act_red\n", - " else: \n", - " DAC_ONOFF(ser_pulser, 4, False)\n", - " act_red = 0\n", - " \n", - "\n", - " if _l[0] == 'W' and len(_l) == 2:\n", - " time.sleep(_l[1])\n", - "\n", - " if _l[0] == 'P':\n", - " _plot_data = pd.DataFrame(_data)\n", - " axis.clear()\n", - " axis.plot(_plot_data['T']/1000, (_plot_data['S'] - OFFSET) / _plot_data['R'])\n", - " display(fig)\n", - " clear_output(True)\n", - " \n", - "\n", - "\n", - " \n", - "\n", - " DAC_ONOFF(ser_pulser, 0, False)\n", - " Ambit_actinic(ser, 0)\n", - "\n", - " if not _wait_for_line(ser, target = \"Stop\", input = \"E\", print_out=True):\n", - " ser.write(b'E\\n')\n", - " raise NameError(\"Run not Stopped\")\n", - " \n", - "finally:\n", - " ser.close()\n", - " ser_pulser.close()\n", - "\n", - "\n", - "data = pd.DataFrame(_data)\n", - "data.attrs['OFFSET'] = OFFSET\n", - "data.attrs['START_TIME'] = START_TIME\n", - "data.attrs['protocol'] = run_protocol" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'OFFSET': 4000,\n", - " 'START_TIME': 1738691730.8549724,\n", - " 'protocol': [['M', 100, 0.1, 0],\n", - " ['M', 10, 0.1, 0],\n", - " ['M', 250, 0.005, 254],\n", - " ['M', 20, 0.01, 0],\n", - " ['M', 20, 0.1, 0],\n", - " ['M', 100, 0.1, 0]]}" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data.attrs" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
TSRFBACTBlueRed
0579251553929561549000
11038160183951185888000
22038189194461043868000
33048192185951066851000
44048205182491081842000
...........................
492242978479182371523836000
493243988489182411444836000
494244988466182411365832000
495245988486182431373840000
496246988468182421331833000
\n", - "

497 rows × 8 columns

\n", - "
" - ], - "text/plain": [ - " T S R F B ACT Blue Red\n", - "0 5 7925 15539 2956 1549 0 0 0\n", - "1 103 8160 18395 1185 888 0 0 0\n", - "2 203 8189 19446 1043 868 0 0 0\n", - "3 304 8192 18595 1066 851 0 0 0\n", - "4 404 8205 18249 1081 842 0 0 0\n", - ".. ... ... ... ... ... ... ... ...\n", - "492 24297 8479 18237 1523 836 0 0 0\n", - "493 24398 8489 18241 1444 836 0 0 0\n", - "494 24498 8466 18241 1365 832 0 0 0\n", - "495 24598 8486 18243 1373 840 0 0 0\n", - "496 24698 8468 18242 1331 833 0 0 0\n", - "\n", - "[497 rows x 8 columns]" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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EZGdYoghIiIiu8QQQ0RERHaJIYaIiIjsEkMMERER2SWGGCIiIrJLDDFERERklxhiiIiIyC4xxBAREZFdYoghIiIiu8QQQ0RERHaJIYaIiIjsEkMMERER2aXrbhVrlUpl6yoQERFRO1nze/u6CTGmi1BQUGDjmhAREZGlVCoVqqurLXqPDIDQNdWRXt++fS2+ANeiUqlQUFCAgICATj82meO1lhavt3R4raXDay2tzrreKpUKhYWFFr/vummJAdChC9Be1dXV/B9CIrzW0uL1lg6vtXR4raVl7fXu6Hs5sJeIiIjsEkMMERER2SWGmGvQarVYuXIltFqtraty3eO1lhavt3R4raXDay0tW1/v62pgLxERETkOtsQQERGRXWKIISIiIrvEEENERER2iSGGiIiI7BJDzDXExcUhJycHdXV1yMjIwLhx42xdpW5txYoVEATBbCsqKmpWpqCgALW1tfj5558xbNgws9eVSiU2bNiAy5cv48qVK/j2228REBBgVsbLywtbtmxBRUUFKioqsGXLFvTs2bPLP58tjR8/Htu2bUNBQQEEQcD06dOblZHq2gYFBWHbtm24cuUKLl++jLfffhsKhaLzP7SNXOtab968udn3PC0tzawMr3X7/PWvf8X+/ftRVVWF4uJifP311xg0aFCzcvxud472XG97+34L3FreYmNjBa1WKyxcuFAYMmSI8I9//EOorq4WgoKCbF637rqtWLFCOHr0qNCnTx9x8/X1FV9funSpUFlZKcycOVMYPny48NlnnwkFBQWCh4eHWOb9998X8vLyhDvuuEMICwsTdu3aJWRmZgpyuVws8/333wtHjhwRIiMjhcjISOHIkSPCtm3bbP75u3K78847hddff12YOXOmIAiCMH36dLPXpbq2crlcOHLkiLBr1y4hLCxMuOOOO4T8/Hxhw4YNNr9GUl3rzZs3C99//73Z97xXr15mZXit27clJycL8+bNE4YNGybccsstwvbt24Xz588Lbm5u/G7b6Hrb2ffb9he1u27p6enC+++/b7bv2LFjwurVq21et+66rVixQsjMzGz19cLCQmHp0qXiz0qlUigvLxcWLVokABA8PT0FrVYrxMbGimX8/f0Fg8EgREdHCwCEIUOGCIIgCCNHjhTLjBo1ShAEQRg0aJDNr4EUW0u/WKW6tnfeeadgMBgEf39/sczs2bOFuro6QaVS2fzaSHGtN2/eLHz99detvofXuuObr6+vIAiCMH78eHEfv9vSXm97+n6zO6kVCoUCERERSElJMdufkpKCMWPG2KhW9mHgwIEoKChATk4OPvvsM/Tv3x8A0L9/f/j7+5tdU51Oh19//VW8phEREVAqlWZlioqKkJWVJZYZPXo0KioqsH//frHMvn37UFFR4bB/N1Je29GjRyMrK8usm3DHjh1wdXVFREREl37O7mTSpEkoLi7GyZMnsWnTJvTu3Vt8jde640zdDWVlZQD43e5qf7zeJvby/WaIaYWvry+cnZ1RXFxstr+4uBhqtdpGter+9u3bh4cffhhTp07FY489BrVajb1798Lb21u8bm1dU7VaDa1Wi4qKijbLXLp0qdm5L1265LB/N1JeW7Va3ew8FRUV0Gq1DnP9k5OTMXfuXEyePBnPP/88RowYgZ9++glKpRIAr7U14uPjsXv3bmRnZwPgd7ur/fF6A/b1/b6uVrHuCoIgmP0sk8ma7aMmP/zwg/jnrKwspKWl4ezZs5g3bx7S09MBdOya/rFMS+X5dyPdtXX065+UlCT+OTs7GxkZGbhw4QLuvvtufP31162+j9e6be+++y5uueWWFh+g4He787V2ve3p+82WmFaUlJTAYDA0S4N+fn7NkiO1rra2FkePHsXAgQNx8eJFAGjzml68eBEuLi7w8vJqs0yfPn2anat3794O+3cj5bW9ePFis/N4eXlBqVQ69PW/cOECBg4cKP7Ma22ZDRs2YNq0abj99ttRUFAg7ud3u2u0dr1b0t2/3zYfWNRdt/T0dOG9994z25ednc2BvRZsSqVSyMvLE1599VUBaBig9+KLL4qvKxSKFgfo3XfffWIZtVrd4oCxESNGiGVGjhzJgb0SXVvTYDy1Wi2WiY2NvW4HP7Z0rf+4eXt7C3V1dcJDDz3Ea92B7Z133hHy8/OFkJCQFl/nd1va6/3HrZt/v21/QbvrZnrEesGCBcKQIUOE+Ph4obq6WggODrZ53brrtnbtWmHChAnCDTfcIIwcOVLYtm2bUFlZKV6zpUuXCuXl5cKMGTOE4cOHC59++mmLj0rm5uYKkydPFsLCwoSdO3e2+Oje4cOHhVGjRgmjRo0Sfv/99+v+EWt3d3chNDRUCA0NFQRBEJ599lkhNDRUfORfqmtreizyxx9/FMLCwoTJkycLubm519VjqG1da3d3d2Ht2rVCZGSk0K9fP2HixIlCamqqkJeXx2vdge29994TysvLhQkTJpg90uvq6iqW4Xdbuutth99v21/U7rzFxcUJ586dEzQajZCRkWH2GBq35ptp/gatVivk5+cL//nPf4ShQ4ealVmxYoVQWFgo1NXVCb/88oswfPhws9ddXFyEDRs2CCUlJUJNTY2wbds2ITAw0KxMr169hMTERKGyslKorKwUEhMThZ49e9r883flNnHiRKElmzdvlvzaBgUFCdu3bxdqamqEkpISYcOGDYJSqbT5NZLiWru6ugo//PCDUFxcLGi1WuH8+fPC5s2bm11HXuv2ba2ZN2+eWTl+t6W53vb2/ZY1/oGIiIjIrnBgLxEREdklhhgiIiKySwwxREREZJcYYoiIiMguMcQQERGRXWKIISIiIrvEEENERER2iSGGiIiI7BJDDBEREdklhhgiIiKySwwxREREZJcYYoiIiMgu/X/CU1uJi50o1wAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(data['T'], data['S']/data['R'])" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/.vscode/examples/Untitled-2.ipynb b/.vscode/examples/Untitled-2.ipynb deleted file mode 100644 index 42833ec..0000000 --- a/.vscode/examples/Untitled-2.ipynb +++ /dev/null @@ -1,277 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import serial\n", - "import numpy as np\n", - "import time\n", - "import matplotlib.pyplot as plt\n", - "from IPython.display import clear_output, display\n", - "import pandas as pd\n", - "from collections import namedtuple\n", - "plt.style.use('dark_background')" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "def _readline(ser: serial.Serial, timeout: int):\n", - " _t0 = time.time()\n", - " s = \"\"\n", - " while (time.time() - _t0 < timeout):\n", - " if ser.in_waiting > 0:\n", - " c = ser.read()\n", - " if (c > bytes([0])) and (c < bytes([128])):\n", - " if (c == b'\\n') or (c == b'\\r'):\n", - " if s == \"\":\n", - " pass\n", - " elif len(s) > 2:\n", - " return s\n", - " else:\n", - " s += c.decode()\n", - " \n", - " return s\n", - "\n", - "def _wait_for_line(ser: serial.Serial, target: str, input: str = \"\", timeout: float = 1.0, print_out = False):\n", - " if input != \"\":\n", - " ser.write((input + \"\\n\").encode())\n", - " t0 = time.time()\n", - " timeout_t = t0 + timeout\n", - " condition = False\n", - " while (condition == False):\n", - " _l = _readline(ser, 0.25)\n", - " if target in _l:\n", - " condition = True\n", - " if print_out:\n", - " if _l != \"\": print(_l)\n", - " if time.time() > timeout_t:\n", - " break\n", - "\n", - " return condition\n", - "\n", - " \n", - "def _parse_data_line(l:str)->dict:\n", - " ret = {}\n", - " if len(l) > 5:\n", - " if (l[:2] == \"T:\") and (\",\" in l) and (\":\" in l):\n", - " items = [n.split(':') for n in l.split(',') if \":\" in n]\n", - " for i in items:\n", - " k = i[0]\n", - " v = i[1]\n", - " if len(k) > 0:\n", - " if v.isdecimal():\n", - " ret.update({k: int(v)})\n", - " if (v.count(\".\") == 1) and v.replace(\".\", \"0\").isdecimal():\n", - " ret.update({k: float(v)})\n", - "\n", - " return ret \n", - "\n", - "\n", - "def Trigger(ser: serial.Serial) -> dict:\n", - " ser.write(b\"G\")\n", - " ser.flush()\n", - " _ret = _parse_data_line(_readline(ser, .5))\n", - " return _ret\n", - "\n", - "def Ambit_actinic(ser: serial.Serial, v:int)->int:\n", - " if (v > 255): return -1\n", - " ser.write(bytes([65, v]))\n", - " ser.flush()\n", - " return v\n", - " \n", - "\n", - "def DAC_set(ser: serial.Serial, ch:int, v:int):\n", - " ret = False\n", - " if (ch == 2) or (ch == 4):\n", - " if (v > -1) and (v < 4095):\n", - " ret = _wait_for_line(ser, target = \"Done\", input = f'DAC,{ch},{v},\\n', print_out=False)\n", - " if ret == False: \n", - " print(\"DAC_set error\", ch, v)\n", - " return -1\n", - " return v\n", - "\n", - "def DAC_ONOFF(ser: serial.Serial, ch:int, onoff:bool):\n", - " if onoff:\n", - " if (ch == 2) or (ch == 4):\n", - " _wait_for_line(ser, target = \"Done\", input = f'LED_ON,{ch},\\n', print_out=False)\n", - " else:\n", - " if (ch == 2) or (ch == 4):\n", - " _wait_for_line(ser, target = \"Done\", input = f'LED_OFF,{ch},\\n', print_out=False)\n", - " else:\n", - " _wait_for_line(ser, target = \"Done\", input = f'LED_OFF,0,\\n', print_out=False)\n", - "\n", - "# plot autoscale\n", - "def autoscale(data: list, axis:plt.Axes):\n", - " if len(data) == 0: return\n", - " if len(data) == 1: \n", - " axis.set_ylim([data[0]-1, data[0]+1])\n", - " return \n", - " l, h = axis.get_ylim()\n", - " x, d = np.min(data), np.max(data)\n", - " r = d - x\n", - "\n", - " _l, _h, a = l, h, False\n", - " if (x < l) or (l < x - r):\n", - " _l = x - 0.05 * r\n", - " a = True\n", - " if (d > h) or (h > l + r):\n", - " _h = d + 0.05 * r\n", - " a = True\n", - " if a and _l != _h:\n", - " axis.set_ylim([_l, _h])" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [], - "source": [ - "protocol = [['M', 100, 0.001, 0]]" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "NEW Name Here Ready\n", - "Start\n", - "Run\n", - "Stop\n" - ] - } - ], - "source": [ - "ser = serial.Serial(\"COM9\", 115200, rtscts=False)\n", - "START_TIME = time.time()\n", - "try:\n", - " _wait_for_line(ser, target = \"NEW Name\", input = \"hello\", print_out=True)\n", - " if not _wait_for_line(ser, target = \"Start\", input = \"ff,190\", print_out=True):\n", - " ser.write(b'E\\n')\n", - " raise NameError(\"Function not started\")\n", - " \n", - " if not _wait_for_line(ser, target = \"Run\", input = \"\", print_out=True):\n", - " ser.write(b'E\\n')\n", - " raise NameError(\"Run not started\") \n", - "\n", - "\n", - " _act_ambit, _act_blue, _act_red, act_blue, act_red = -1, -1, -1, 0, 0\n", - " _data = []\n", - " for _l in protocol:\n", - "\n", - " if _l[0] == 'M' and len(_l) == 4: # Run measurement\n", - " \n", - " if (_act_ambit != _l[3]): # Set ambit actinic\n", - " _act_ambit = Ambit_actinic(ser, _l[3])\n", - " if (_l[1] < 1): continue # no data\n", - " for _ in range(_l[1]):\n", - " _ts = time.perf_counter()\n", - " _ret = Trigger(ser)\n", - " if _ret != {}: \n", - " _ret.update({\"ACT\":_act_ambit,\"Blue\":act_blue,\"Red\":act_red}.copy())\n", - " _data.append(_ret)\n", - " _elapsed = time.perf_counter() - _ts\n", - " if _elapsed < _l[2]: time.sleep(_l[2] - _elapsed)\n", - " \n", - "\n", - " if _l[0] == 'W' and len(_l) == 2:\n", - " time.sleep(_l[1])\n", - "\n", - " if not _wait_for_line(ser, target = \"Stop\", input = \"E\", print_out=True):\n", - " ser.write(b'E\\n')\n", - " raise NameError(\"Run not Stopped\")\n", - "\n", - "finally:\n", - " ser.close()\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "data = pd.DataFrame(_data)" - ] - }, - { - "cell_type": "code", - "execution_count": 124, - "metadata": {}, - "outputs": [], - "source": [ - "data.attrs['OFFSET'] = 1" - ] - }, - { - "cell_type": "code", - "execution_count": 118, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 118, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(data['T'], data['B'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/.vscode/examples/ambit_fsm.excalidraw b/.vscode/examples/ambit_fsm.excalidraw deleted file mode 100644 index b3888ce..0000000 --- a/.vscode/examples/ambit_fsm.excalidraw +++ /dev/null @@ -1,5170 +0,0 @@ -{ - "type": "excalidraw", - "version": 2, - "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", - "elements": [ - { - "id": "1AceduuKyjAEicwMtKieL", - "type": "rectangle", - "x": 540.7999877929688, - "y": 378.3999938964844, - "width": 204.800048828125, - "height": 91.78756713867188, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 1585265939, - "version": 93, - "versionNonce": 1972389053, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "swGNJJTp38JRogcSElTHZ" - }, - { - "id": "gG53V94Hs4M8Od4h0p5Rv", - "type": "arrow" - }, - { - "id": "68P5vEf1kj79sIFqAd-rT", - "type": "arrow" - }, - { - "id": "SQeHgmklU1eLjMjKYB3QA", - "type": "arrow" - } - ], - "updated": 1714402299521, - "link": null, - "locked": false - }, - { - "id": "swGNJJTp38JRogcSElTHZ", - "type": "text", - "x": 608.5680313110352, - "y": 401.7937774658203, - "width": 69.26396179199219, - "height": 45, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1150952115, - "version": 65, - "versionNonce": 1684263411, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "Idle", - "fontSize": 36, - "fontFamily": 1, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 31, - "containerId": "1AceduuKyjAEicwMtKieL", - "originalText": "Idle", - "lineHeight": 1.25 - }, - { - "id": "JZ0K6tXACCW2EGqwMGHf3", - "type": "rectangle", - "x": 1076.8001098632812, - "y": 378.3999938964844, - "width": 204.800048828125, - "height": 91.78756713867188, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 485474813, - "version": 142, - "versionNonce": 1395914749, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "nUPfwkOIRuggiid05Iipr" - }, - { - "id": "7gp1qzUOY0czzGF-6vJCI", - "type": "arrow" - }, - { - "id": "p0lrF4zO3SgeKn0sD5HfT", - "type": "arrow" - } - ], - "updated": 1714402312013, - "link": null, - "locked": false - }, - { - "id": "nUPfwkOIRuggiid05Iipr", - "type": "text", - "x": 1136.1261672973633, - "y": 401.7937774658203, - "width": 86.14793395996094, - "height": 45, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 218135133, - "version": 111, - "versionNonce": 2124094355, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "sleep", - "fontSize": 36, - "fontFamily": 1, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 31, - "containerId": "JZ0K6tXACCW2EGqwMGHf3", - "originalText": "sleep", - "lineHeight": 1.25 - }, - { - "id": "9swZXvIOWgkhnpW8_AAOL", - "type": "diamond", - "x": 830.4000244140625, - "y": 342.9875793457031, - "width": 179.2000732421875, - "height": 167.38754272460938, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1501065341, - "version": 138, - "versionNonce": 1413620371, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "gO1S5UgQIOOBUtdpjJwD7" - }, - { - "id": "SQeHgmklU1eLjMjKYB3QA", - "type": "arrow" - }, - { - "id": "7gp1qzUOY0czzGF-6vJCI", - "type": "arrow" - } - ], - "updated": 1714402308643, - "link": null, - "locked": false - }, - { - "id": "gO1S5UgQIOOBUtdpjJwD7", - "type": "text", - "x": 891.9700622558594, - "y": 401.83446502685547, - "width": 56.4599609375, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1097816285, - "version": 78, - "versionNonce": 309866803, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "idle\n100ms", - "fontSize": 20, - "fontFamily": 1, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 43, - "containerId": "9swZXvIOWgkhnpW8_AAOL", - "originalText": "idle\n100ms", - "lineHeight": 1.25 - }, - { - "type": "diamond", - "version": 299, - "versionNonce": 214574077, - "isDeleted": false, - "id": "91QgijPwAjHPsQPXDhXJd", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1577.3999633789062, - "y": 343.6561737060547, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 179.2000732421875, - "height": 167.38754272460938, - "seed": 1237483901, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "kWt_smhnWOhoCebqGmlmz" - }, - { - "id": "sY2dxYIT00ouAkmYO_J5N", - "type": "arrow" - }, - { - "id": "auFN6X7BnJ9hTAiNS4d53", - "type": "arrow" - }, - { - "id": "QvahvFaqmITq3A9lK6PSc", - "type": "arrow" - } - ], - "updated": 1714402341096, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 250, - "versionNonce": 23171795, - "isDeleted": false, - "id": "kWt_smhnWOhoCebqGmlmz", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 1643.6800003051758, - "y": 402.50305938720703, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 47.03996276855469, - "height": 50, - "seed": 542067165, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714402202186, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "CMD\n(160)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "91QgijPwAjHPsQPXDhXJd", - "originalText": "CMD\n(160)", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "id": "DhXnx6-g9HjL7buARwkue", - "type": "diamond", - "x": 1336.7999877929688, - "y": 342.9875793457031, - "width": 179.2000732421875, - "height": 167.38754272460938, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 46510131, - "version": 170, - "versionNonce": 1799972605, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "g3I7LaKIacgaO1VTygMM2" - }, - { - "id": "p0lrF4zO3SgeKn0sD5HfT", - "type": "arrow" - }, - { - "id": "sY2dxYIT00ouAkmYO_J5N", - "type": "arrow" - }, - { - "id": "5_6i8jy9BLi4Raoq7wu8Y", - "type": "arrow" - } - ], - "updated": 1714402322456, - "link": null, - "locked": false - }, - { - "id": "g3I7LaKIacgaO1VTygMM2", - "type": "text", - "x": 1399.3000259399414, - "y": 401.83446502685547, - "width": 54.59996032714844, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1242289875, - "version": 122, - "versionNonce": 1541618195, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "uart \ninput", - "fontSize": 20, - "fontFamily": 1, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 43, - "containerId": "DhXnx6-g9HjL7buARwkue", - "originalText": "uart input", - "lineHeight": 1.25 - }, - { - "id": "4WQmrGNtNOnpuJTz3oKzu", - "type": "rectangle", - "x": 1834.4000854492188, - "y": 378.3999938964844, - "width": 204.800048828125, - "height": 91.78756713867188, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 829237821, - "version": 191, - "versionNonce": 595488637, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "bBNsM6KfWxTxNUYwaDBZj" - }, - { - "id": "QvahvFaqmITq3A9lK6PSc", - "type": "arrow" - }, - { - "id": "pU5xOgFud-RCppGcjI_Fe", - "type": "arrow" - } - ], - "updated": 1714402344324, - "link": null, - "locked": false - }, - { - "id": "bBNsM6KfWxTxNUYwaDBZj", - "type": "text", - "x": 1857.2941360473633, - "y": 401.7937774658203, - "width": 159.01194763183594, - "height": 45, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 908515997, - "version": 172, - "versionNonce": 2117631923, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "read cmd", - "fontSize": 36, - "fontFamily": 1, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 31, - "containerId": "4WQmrGNtNOnpuJTz3oKzu", - "originalText": "read cmd", - "lineHeight": 1.25 - }, - { - "id": "tkgLkECrwUk37XbPVZcBR", - "type": "rectangle", - "x": 2213.600067138672, - "y": 16.58746337890625, - "width": 204.800048828125, - "height": 91.78756713867188, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 1914675645, - "version": 220, - "versionNonce": 2136393981, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "iOBFR3zHQhAJXHC0Ma41J" - }, - { - "id": "YAVECsQH4pzpzN2EWIZ6P", - "type": "arrow" - } - ], - "updated": 1714402202186, - "link": null, - "locked": false - }, - { - "id": "iOBFR3zHQhAJXHC0Ma41J", - "type": "text", - "x": 2251.5469665527344, - "y": 50.48124694824219, - "width": 128.90625, - "height": 24, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 949903901, - "version": 219, - "versionNonce": 1561596243, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "1:set gains", - "fontSize": 20, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 19, - "containerId": "tkgLkECrwUk37XbPVZcBR", - "originalText": "1:set gains", - "lineHeight": 1.2 - }, - { - "id": "N5fVsn-fstc9TLBsEkuE2", - "type": "rectangle", - "x": 2213.600067138672, - "y": 138.77496337890625, - "width": 204.800048828125, - "height": 91.78756713867188, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 23270931, - "version": 268, - "versionNonce": 988717405, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "r8GQuIt-8LdhpQRwzZbvL" - }, - { - "id": "rWiNBdY_GSQ3jUZJh9j9e", - "type": "arrow" - } - ], - "updated": 1714402202186, - "link": null, - "locked": false - }, - { - "id": "r8GQuIt-8LdhpQRwzZbvL", - "type": "text", - "x": 2239.8282165527344, - "y": 172.6687469482422, - "width": 152.34375, - "height": 24, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 526179251, - "version": 288, - "versionNonce": 701263603, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "2.set current", - "fontSize": 20, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 19, - "containerId": "N5fVsn-fstc9TLBsEkuE2", - "originalText": "2.set current", - "lineHeight": 1.2 - }, - { - "id": "npCnZj0g3qwogXzMuDCWW", - "type": "rectangle", - "x": 2214.4000549316406, - "y": 256.7500915527344, - "width": 204.800048828125, - "height": 91.78756713867188, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 309671357, - "version": 308, - "versionNonce": 2058120637, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "lreK3yTBCi8zT9og-VQ7h" - }, - { - "id": "IDqXx46-nFH73EVmhokvk", - "type": "arrow" - } - ], - "updated": 1714402202186, - "link": null, - "locked": false - }, - { - "id": "lreK3yTBCi8zT9og-VQ7h", - "type": "text", - "x": 2246.487579345703, - "y": 278.6438751220703, - "width": 140.625, - "height": 48, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1629582877, - "version": 354, - "versionNonce": 652744851, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "10.run array\nconfig", - "fontSize": 20, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 43, - "containerId": "npCnZj0g3qwogXzMuDCWW", - "originalText": "10.run array\nconfig", - "lineHeight": 1.2 - }, - { - "id": "X9ZeCduHYuJ72eB5ZETh8", - "type": "rectangle", - "x": 2214.4000549316406, - "y": 374.5125427246094, - "width": 204.800048828125, - "height": 91.78756713867188, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 2093722835, - "version": 348, - "versionNonce": 16544829, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "3-UYk_M99rmw3uhZQ_Kdf" - }, - { - "id": "JNhkwkzBMK3KtrBKhdLjN", - "type": "arrow" - }, - { - "id": "pU5xOgFud-RCppGcjI_Fe", - "type": "arrow" - } - ], - "updated": 1714402344324, - "link": null, - "locked": false - }, - { - "id": "3-UYk_M99rmw3uhZQ_Kdf", - "type": "text", - "x": 2258.206329345703, - "y": 408.4063262939453, - "width": 117.1875, - "height": 24, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 923048563, - "version": 390, - "versionNonce": 35491379, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "20.run MPF", - "fontSize": 20, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 19, - "containerId": "X9ZeCduHYuJ72eB5ZETh8", - "originalText": "20.run MPF", - "lineHeight": 1.2 - }, - { - "id": "drMFFK8UmzfslilfaWI7i", - "type": "rectangle", - "x": 2214.4000549316406, - "y": 491.9000549316406, - "width": 204.800048828125, - "height": 91.78756713867188, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 924430931, - "version": 381, - "versionNonce": 1659888253, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "RtKSJBt7mKeApqNBdQlH3" - }, - { - "id": "QOwONcVPbJwb_R67i2dvy", - "type": "arrow" - } - ], - "updated": 1714402202186, - "link": null, - "locked": false - }, - { - "id": "RtKSJBt7mKeApqNBdQlH3", - "type": "text", - "x": 2246.487579345703, - "y": 525.7938385009766, - "width": 140.625, - "height": 24, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 642748509, - "version": 433, - "versionNonce": 1850953683, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "21.run array", - "fontSize": 20, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 19, - "containerId": "drMFFK8UmzfslilfaWI7i", - "originalText": "21.run array", - "lineHeight": 1.2 - }, - { - "id": "0i4g4WbpQrjegeyvuCkEz", - "type": "rectangle", - "x": 2214.4000549316406, - "y": 595.6875915527344, - "width": 204.800048828125, - "height": 91.78756713867188, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 1047136339, - "version": 423, - "versionNonce": 1328057053, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "k6N1tS0Jh_HeUJc5zOTbe" - }, - { - "id": "xiW2VrAyc1GUP03CYemni", - "type": "arrow" - } - ], - "updated": 1714402202186, - "link": null, - "locked": false - }, - { - "id": "k6N1tS0Jh_HeUJc5zOTbe", - "type": "text", - "x": 2246.487579345703, - "y": 629.5813751220703, - "width": 140.625, - "height": 24, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 2068547059, - "version": 499, - "versionNonce": 2107628915, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "31. get spec", - "fontSize": 20, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 19, - "containerId": "0i4g4WbpQrjegeyvuCkEz", - "originalText": "31. get spec", - "lineHeight": 1.2 - }, - { - "id": "-GJ8y9iJ6Jofh4-J-28zz", - "type": "rectangle", - "x": 2214.4000549316406, - "y": 703.6875915527344, - "width": 204.800048828125, - "height": 91.78756713867188, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 166368157, - "version": 441, - "versionNonce": 317531965, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "7XdYRR3YYeAVurTr14aKG" - }, - { - "id": "BKH0AcpQRoBtXWmQMIv38", - "type": "arrow" - } - ], - "updated": 1714402202186, - "link": null, - "locked": false - }, - { - "id": "7XdYRR3YYeAVurTr14aKG", - "type": "text", - "x": 2246.487579345703, - "y": 737.5813751220703, - "width": 140.625, - "height": 24, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1643124733, - "version": 530, - "versionNonce": 994686739, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "32. get temp", - "fontSize": 20, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 19, - "containerId": "-GJ8y9iJ6Jofh4-J-28zz", - "originalText": "32. get temp", - "lineHeight": 1.2 - }, - { - "id": "ajWzG2mD9MR1R84IFg5Fh", - "type": "rectangle", - "x": 2558.1331176757812, - "y": 195.78738403320312, - "width": 178.40008544921875, - "height": 92.375, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 1398620019, - "version": 209, - "versionNonce": 1084176285, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "YMqKF0ru8vtrSPERrGvsK" - }, - { - "id": "YAVECsQH4pzpzN2EWIZ6P", - "type": "arrow" - }, - { - "id": "rWiNBdY_GSQ3jUZJh9j9e", - "type": "arrow" - }, - { - "id": "IDqXx46-nFH73EVmhokvk", - "type": "arrow" - }, - { - "id": "gG53V94Hs4M8Od4h0p5Rv", - "type": "arrow" - } - ], - "updated": 1714402202186, - "link": null, - "locked": false - }, - { - "id": "YMqKF0ru8vtrSPERrGvsK", - "type": "text", - "x": 2596.953193664551, - "y": 216.97488403320312, - "width": 100.75993347167969, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 814366227, - "version": 153, - "versionNonce": 96951475, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "text": "CMD Done\n(161)", - "fontSize": 20, - "fontFamily": 1, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 43, - "containerId": "ajWzG2mD9MR1R84IFg5Fh", - "originalText": "CMD Done\n(161)", - "lineHeight": 1.25 - }, - { - "id": "YAVECsQH4pzpzN2EWIZ6P", - "type": "arrow", - "x": 2429.599853515625, - "y": 61.012374877929524, - "width": 122.58930870547692, - "height": 125.7878023020815, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1724666141, - "version": 100, - "versionNonce": 577780733, - "isDeleted": false, - "boundElements": [], - "updated": 1714402202186, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 122.58930870547692, - 125.7878023020815 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "tkgLkECrwUk37XbPVZcBR", - "focus": -0.7834522181347663, - "gap": 11.199737548828125 - }, - "endBinding": { - "elementId": "ajWzG2mD9MR1R84IFg5Fh", - "focus": -0.3082579603972113, - "gap": 10.774993896484375 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "rWiNBdY_GSQ3jUZJh9j9e", - "type": "arrow", - "x": 2435.1998291015625, - "y": 188.21237182617193, - "width": 110.933349609375, - "height": 34.370006738930186, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 408227645, - "version": 78, - "versionNonce": 1080144989, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 110.933349609375, - 34.370006738930186 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "N5fVsn-fstc9TLBsEkuE2", - "focus": -0.432172408415282, - "gap": 16.799713134765625 - }, - "endBinding": { - "elementId": "ajWzG2mD9MR1R84IFg5Fh", - "focus": -0.16203210909218146, - "gap": 11.99993896484375 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "IDqXx46-nFH73EVmhokvk", - "type": "arrow", - "x": 2431.1998291015625, - "y": 301.81237792968756, - "width": 114.13336181640625, - "height": 54.82981623128521, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1270376627, - "version": 90, - "versionNonce": 923274227, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 114.13336181640625, - -54.82981623128521 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "npCnZj0g3qwogXzMuDCWW", - "focus": 0.5714348015148849, - "gap": 11.999725341796875 - }, - "endBinding": { - "elementId": "ajWzG2mD9MR1R84IFg5Fh", - "focus": 0.4940878079241482, - "gap": 12.7999267578125 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "gG53V94Hs4M8Od4h0p5Rv", - "type": "arrow", - "x": 2648.7890753245397, - "y": 182.16353788829963, - "width": 1949.333419799804, - "height": 417.3333740234374, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 984256243, - "version": 264, - "versionNonce": 614995347, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 4.277514030929069, - -228.35418701171875 - ], - [ - -1938.3892391022082, - -233.33333333333326 - ], - [ - -1945.055905768875, - 184.00004069010413 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "ajWzG2mD9MR1R84IFg5Fh", - "focus": 0.0037254461007783244, - "gap": 13.623846144903496 - }, - "endBinding": { - "elementId": "1AceduuKyjAEicwMtKieL", - "focus": 0.5779379271248143, - "gap": 12.236415318080617 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "rectangle", - "version": 264, - "versionNonce": 1773928733, - "isDeleted": false, - "id": "Nf_D1fgEk2tNU8K5b0R9U", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2531.5329691569013, - "y": 443.24685087658077, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 178.40008544921875, - "height": 92.375, - "seed": 1582259869, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "id": "JNhkwkzBMK3KtrBKhdLjN", - "type": "arrow" - }, - { - "id": "QOwONcVPbJwb_R67i2dvy", - "type": "arrow" - }, - { - "type": "text", - "id": "BO2Djliz0u5vf5dgpSgQL" - }, - { - "id": "Diu-lUS065dRmY2yluVCr", - "type": "arrow" - } - ], - "updated": 1714402202186, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 202, - "versionNonce": 550890291, - "isDeleted": false, - "id": "BO2Djliz0u5vf5dgpSgQL", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2573.8580118815107, - "y": 465.43435087658077, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 93.75, - "height": 48, - "seed": 2010195379, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 3, - "text": "CMD Done\n(161)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Nf_D1fgEk2tNU8K5b0R9U", - "originalText": "CMD Done\n(161)", - "lineHeight": 1.2, - "baseline": 43 - }, - { - "id": "JNhkwkzBMK3KtrBKhdLjN", - "type": "arrow", - "x": 2431.7331746419277, - "y": 449.97327399434914, - "width": 86.66666666666697, - "height": 21.42667915839229, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 800783933, - "version": 73, - "versionNonce": 1882707325, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202186, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 86.66666666666697, - 21.42667915839229 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "focus": 0.014320796960052715, - "gap": 12.533070882162065, - "elementId": "X9ZeCduHYuJ72eB5ZETh8" - }, - "endBinding": { - "focus": -0.10646939352812587, - "gap": 13.133127848306685, - "elementId": "Nf_D1fgEk2tNU8K5b0R9U" - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "QOwONcVPbJwb_R67i2dvy", - "type": "arrow", - "x": 2430.3999633789062, - "y": 559.5358509818523, - "width": 81.33321126302144, - "height": 39.189908347962955, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 753319197, - "version": 68, - "versionNonce": 804785363, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 81.33321126302144, - -39.189908347962955 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "focus": 0.8031449635118987, - "gap": 11.199859619140625, - "elementId": "drMFFK8UmzfslilfaWI7i" - }, - "endBinding": { - "focus": 0.24234380222657526, - "gap": 19.799794514973655, - "elementId": "Nf_D1fgEk2tNU8K5b0R9U" - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "rectangle", - "version": 295, - "versionNonce": 1962548701, - "isDeleted": false, - "id": "6yDCAunbFVaU8UTDj400L", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2840.866261800131, - "y": 444.22595650809126, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 178.40008544921875, - "height": 92.375, - "seed": 2012827197, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "id": "JNhkwkzBMK3KtrBKhdLjN", - "type": "arrow" - }, - { - "id": "QOwONcVPbJwb_R67i2dvy", - "type": "arrow" - }, - { - "id": "Diu-lUS065dRmY2yluVCr", - "type": "arrow" - }, - { - "type": "text", - "id": "Qn7oeiXCDM0ejNltLwKq6" - }, - { - "id": "eh9FNlMPUSqysroVgLFbc", - "type": "arrow" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 243, - "versionNonce": 94698099, - "isDeleted": false, - "id": "Qn7oeiXCDM0ejNltLwKq6", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2881.646336873373, - "y": 477.91345650809126, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 96.83993530273438, - "height": 25, - "seed": 529432221, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "ADPD run", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "6yDCAunbFVaU8UTDj400L", - "originalText": "ADPD run", - "lineHeight": 1.25, - "baseline": 18 - }, - { - "id": "Diu-lUS065dRmY2yluVCr", - "type": "arrow", - "x": 2719.733154296875, - "y": 490.1635175432475, - "width": 117.33327229817769, - "height": 0.7137752991171169, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 839986237, - "version": 46, - "versionNonce": 1326733885, - "isDeleted": false, - "boundElements": [], - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 117.33327229817769, - -0.7137752991171169 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "focus": 0.028491589862986146, - "gap": 9.800099690754905, - "elementId": "Nf_D1fgEk2tNU8K5b0R9U" - }, - "endBinding": { - "focus": 0.03272967111096435, - "gap": 3.799835205078125, - "elementId": "6yDCAunbFVaU8UTDj400L" - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "rectangle", - "version": 411, - "versionNonce": 2003307165, - "isDeleted": false, - "id": "xKBQWxStslhW-hmFaps0M", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2627.866587320964, - "y": 659.225956508091, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 178.40008544921875, - "height": 92.375, - "seed": 814975955, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "hw1DwXjkzXpuGxFn0xMm7" - }, - { - "id": "xiW2VrAyc1GUP03CYemni", - "type": "arrow" - }, - { - "id": "BKH0AcpQRoBtXWmQMIv38", - "type": "arrow" - }, - { - "id": "OPC0mc4nR5Cnsyyo1SHwQ", - "type": "arrow" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 348, - "versionNonce": 451492275, - "isDeleted": false, - "id": "hw1DwXjkzXpuGxFn0xMm7", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 2666.6866633097334, - "y": 680.413456508091, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 100.75993347167969, - "height": 50, - "seed": 75012467, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714402202187, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "CMD Done\n(161)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "xKBQWxStslhW-hmFaps0M", - "originalText": "CMD Done\n(161)", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "id": "xiW2VrAyc1GUP03CYemni", - "type": "arrow", - "x": 2431.7333780924487, - "y": 631.7868345029348, - "width": 181.33321126302053, - "height": 48.58754762655826, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 154310067, - "version": 74, - "versionNonce": 1319546621, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "vFM13GwzZDJORtJ4nVx9g" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 181.33321126302053, - 48.58754762655826 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "0i4g4WbpQrjegeyvuCkEz", - "focus": -0.5547579509721468, - "gap": 12.53327433268305 - }, - "endBinding": { - "elementId": "xKBQWxStslhW-hmFaps0M", - "focus": -0.0403397191063741, - "gap": 14.79999796549464 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "vFM13GwzZDJORtJ4nVx9g", - "type": "text", - "x": 2458.2958780924487, - "y": 640.1635124569842, - "width": 46.875, - "height": 24, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 140821427, - "version": 9, - "versionNonce": 1469942611, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "text": "meas", - "fontSize": 20, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 19, - "containerId": "xiW2VrAyc1GUP03CYemni", - "originalText": "meas", - "lineHeight": 1.2 - }, - { - "id": "BKH0AcpQRoBtXWmQMIv38", - "type": "arrow", - "x": 2430.399963378907, - "y": 738.1634971981952, - "width": 184.00004069010402, - "height": 30.666605631510492, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1301709267, - "version": 46, - "versionNonce": 592562013, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "SOSg6UcQz_zAoc5RXbHCv" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 184.00004069010402, - -30.666605631510492 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "-GJ8y9iJ6Jofh4-J-28zz", - "focus": 0.11936697090635726, - "gap": 11.199859619141534 - }, - "endBinding": { - "elementId": "xKBQWxStslhW-hmFaps0M", - "focus": 0.24613615402321876, - "gap": 13.46658325195267 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "SOSg6UcQz_zAoc5RXbHCv", - "type": "text", - "x": 2498.962483723959, - "y": 710.8301943824399, - "width": 46.875, - "height": 24, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1753995795, - "version": 9, - "versionNonce": 1226221811, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "text": "meas", - "fontSize": 20, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 19, - "containerId": "BKH0AcpQRoBtXWmQMIv38", - "originalText": "meas", - "lineHeight": 1.2 - }, - { - "id": "OPC0mc4nR5Cnsyyo1SHwQ", - "type": "arrow", - "x": 2751.7333780924487, - "y": 641.8094322567889, - "width": 85.33345540364553, - "height": 687.9999796549478, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 491935507, - "version": 228, - "versionNonce": 2008732605, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 6.666666666666515, - -687.9999796549478 - ], - [ - -78.66678873697902, - -687.9999796549478 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "xKBQWxStslhW-hmFaps0M", - "focus": 0.37982535473551865, - "gap": 17.41652425130212 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "rectangle", - "version": 508, - "versionNonce": 349901459, - "isDeleted": false, - "id": "_bTuWLWVkJ15NJTNaFe4L", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3152.866282145183, - "y": 451.1635175432474, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 238.40028889973928, - "height": 92.375, - "seed": 1255899773, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "id": "JNhkwkzBMK3KtrBKhdLjN", - "type": "arrow" - }, - { - "id": "QOwONcVPbJwb_R67i2dvy", - "type": "arrow" - }, - { - "id": "Diu-lUS065dRmY2yluVCr", - "type": "arrow" - }, - { - "type": "text", - "id": "rNAWILXrKvXx6oj9pisdd" - }, - { - "id": "eh9FNlMPUSqysroVgLFbc", - "type": "arrow" - }, - { - "id": "6nOlra-spVR8nmex7M75z", - "type": "arrow" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 481, - "versionNonce": 134435869, - "isDeleted": false, - "id": "rNAWILXrKvXx6oj9pisdd", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3233.356458028158, - "y": 472.3510175432474, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 77.41993713378906, - "height": 50, - "seed": 2023762653, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "wake up\ncalls 0", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "_bTuWLWVkJ15NJTNaFe4L", - "originalText": "wake up\ncalls 0", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "id": "WaD6ueKE4XNUlEMy2tooF", - "type": "ellipse", - "x": 3153.06679280599, - "y": 567.1426333472837, - "width": 106.31256103515636, - "height": 106.31256103515636, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 558097565, - "version": 47, - "versionNonce": 1805731891, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "foRYdI8Q8Mvg2O3W-IHBc" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "id": "foRYdI8Q8Mvg2O3W-IHBc", - "type": "text", - "x": 3182.1330922444668, - "y": 595.2117474482279, - "width": 48.179962158203125, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1140279549, - "version": 37, - "versionNonce": 270321789, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "text": "buf\nclear", - "fontSize": 20, - "fontFamily": 1, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 43, - "containerId": "WaD6ueKE4XNUlEMy2tooF", - "originalText": "buf\nclear", - "lineHeight": 1.25 - }, - { - "id": "aX0CI4fhYtd5N6BntWaC5", - "type": "ellipse", - "x": 3279.733459472657, - "y": 567.1426333472837, - "width": 106.31256103515636, - "height": 106.31256103515636, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "#a5d8ff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1977976787, - "version": 68, - "versionNonce": 592431571, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "5XsOdC4iGhG75C7EPM5g_" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "id": "5XsOdC4iGhG75C7EPM5g_", - "type": "text", - "x": 3309.7025903582694, - "y": 595.2117474482279, - "width": 46.19996643066406, - "height": 50, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1177228659, - "version": 68, - "versionNonce": 1372106973, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "text": "wake\n211", - "fontSize": 20, - "fontFamily": 1, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 43, - "containerId": "aX0CI4fhYtd5N6BntWaC5", - "originalText": "wake\n211", - "lineHeight": 1.25 - }, - { - "type": "diamond", - "version": 363, - "versionNonce": 285022067, - "isDeleted": false, - "id": "HzL0GsyPyI1MtIzoZB3FN", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3476.4668375651045, - "y": 410.7613823300311, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 179.2000732421875, - "height": 167.38754272460938, - "seed": 325170173, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "id": "6nOlra-spVR8nmex7M75z", - "type": "arrow" - }, - { - "id": "m7Efs73wnsp9FlzEweaij", - "type": "arrow" - }, - { - "id": "mw0EQBA_FTBKqMGDbdbhG", - "type": "arrow" - }, - { - "type": "text", - "id": "m7kQqvsPf6ost906icOfz" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 341, - "versionNonce": 1519947069, - "isDeleted": false, - "id": "m7kQqvsPf6ost906icOfz", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3536.6068751017256, - "y": 469.60826801118344, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 59.31996154785156, - "height": 50, - "seed": 73673821, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Awake\n(210)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "HzL0GsyPyI1MtIzoZB3FN", - "originalText": "Awake\n(210)", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "type": "diamond", - "version": 464, - "versionNonce": 586133779, - "isDeleted": false, - "id": "TVTFFITN1cOHhjT4e_4Au", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3476.4668375651045, - "y": 200.7196241106302, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 179.2000732421875, - "height": 170, - "seed": 357724701, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "PbL1dTGTSU5GSDsZywmDz" - }, - { - "id": "mw0EQBA_FTBKqMGDbdbhG", - "type": "arrow" - }, - { - "id": "KfTYmWxD3C4PPayb6eSZ9", - "type": "arrow" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 458, - "versionNonce": 166583709, - "isDeleted": false, - "id": "PbL1dTGTSU5GSDsZywmDz", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3540.456881205241, - "y": 248.2196241106302, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 51.61994934082031, - "height": 75, - "seed": 567813757, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Ambit\nwake\n170", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "TVTFFITN1cOHhjT4e_4Au", - "originalText": "Ambit\nwake\n170", - "lineHeight": 1.25, - "baseline": 68 - }, - { - "type": "diamond", - "version": 509, - "versionNonce": 983439027, - "isDeleted": false, - "id": "YAw3xjrLKFSEYmcYEJp5k", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3481.8004964192723, - "y": 3.30296253022658, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 179.2000732421875, - "height": 167.38754272460938, - "seed": 1941604883, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "nGndLxfX4jQqMZVJfPtXM" - }, - { - "id": "siLekLf7ndrtOq37og-1E", - "type": "arrow" - }, - { - "id": "3SONNUcem5JBsIe0DEqmL", - "type": "arrow" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 501, - "versionNonce": 1459233277, - "isDeleted": false, - "id": "nGndLxfX4jQqMZVJfPtXM", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3542.7105382283544, - "y": 62.149848211378924, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 57.77995300292969, - "height": 50, - "seed": 1051054003, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714402202187, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Reset\n(222)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "YAw3xjrLKFSEYmcYEJp5k", - "originalText": "Reset\n(222)", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "type": "rectangle", - "version": 587, - "versionNonce": 1185859667, - "isDeleted": false, - "id": "Hg85iUuDbtM_3FFIdLgHk", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3743.532786051433, - "y": 451.1635175432474, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 238.40028889973928, - "height": 92.375, - "seed": 479013075, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "id": "JNhkwkzBMK3KtrBKhdLjN", - "type": "arrow" - }, - { - "id": "QOwONcVPbJwb_R67i2dvy", - "type": "arrow" - }, - { - "id": "Diu-lUS065dRmY2yluVCr", - "type": "arrow" - }, - { - "id": "m7Efs73wnsp9FlzEweaij", - "type": "arrow" - }, - { - "type": "text", - "id": "hR5cai5mR4utKVi3xTwPJ" - }, - { - "id": "BTbmeY-DZvSeRXSUnxmaR", - "type": "arrow" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 583, - "versionNonce": 1455348317, - "isDeleted": false, - "id": "hR5cai5mR4utKVi3xTwPJ", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3785.842999776205, - "y": 472.3510175432474, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 153.7798614501953, - "height": 50, - "seed": 1496749683, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "send length info\n1", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Hg85iUuDbtM_3FFIdLgHk", - "originalText": "send length info\n1", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "id": "eh9FNlMPUSqysroVgLFbc", - "type": "arrow", - "x": 3038.400044759115, - "y": 492.8301435198098, - "width": 105.33345540364553, - "height": 0.9792073567708712, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1780988755, - "version": 34, - "versionNonce": 486032883, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 105.33345540364553, - 0.9792073567708712 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "6yDCAunbFVaU8UTDj400L", - "focus": 0.02998056892226112, - "gap": 19.133697509765625 - }, - "endBinding": { - "elementId": "_bTuWLWVkJ15NJTNaFe4L", - "focus": 0.049658941415434484, - "gap": 9.13278198242233 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "6nOlra-spVR8nmex7M75z", - "type": "arrow", - "x": 3409.066752115886, - "y": 506.09279325514274, - "width": 61.28959423910101, - "height": 2.991081646826842, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1318666899, - "version": 32, - "versionNonce": 34440893, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 61.28959423910101, - -2.991081646826842 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "focus": 0.29889665695649315, - "gap": 17.80018107096339, - "elementId": "_bTuWLWVkJ15NJTNaFe4L" - }, - "endBinding": { - "focus": -0.047502349499503424, - "gap": 10.489837639782309, - "elementId": "HzL0GsyPyI1MtIzoZB3FN" - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "m7Efs73wnsp9FlzEweaij", - "type": "arrow", - "x": 3663.4989582864455, - "y": 496.7167199079794, - "width": 69.5678345195447, - "height": 1.0344169628718873, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1947258973, - "version": 42, - "versionNonce": 344002451, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 69.5678345195447, - 1.0344169628718873 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "focus": 0.01134490525654605, - "gap": 8.15203345573704, - "elementId": "HzL0GsyPyI1MtIzoZB3FN" - }, - "endBinding": { - "focus": -0.048543650247888336, - "gap": 10.46599324544286, - "elementId": "Hg85iUuDbtM_3FFIdLgHk" - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "mw0EQBA_FTBKqMGDbdbhG", - "type": "arrow", - "x": 3563.9282232900337, - "y": 410.96054578897116, - "width": 2.7010221596206065, - "height": 35.66568438759498, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1735141715, - "version": 50, - "versionNonce": 998745885, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -2.7010221596206065, - -35.66568438759498 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "focus": 0.04282565766436311, - "gap": 1.3143205288072934, - "elementId": "HzL0GsyPyI1MtIzoZB3FN" - }, - "endBinding": { - "focus": 0.12972490421438299, - "gap": 6.650113051103283, - "elementId": "TVTFFITN1cOHhjT4e_4Au" - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "KfTYmWxD3C4PPayb6eSZ9", - "type": "arrow", - "x": 3466.485381393517, - "y": 286.380152488667, - "width": 137.41842582710888, - "height": 1.882824562427004, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1600135571, - "version": 260, - "versionNonce": 1602200883, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -137.41842582710888, - -1.882824562427004 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "TVTFFITN1cOHhjT4e_4Au", - "gap": 10.003287711723438, - "focus": -0.02353075099318264 - }, - "endBinding": { - "elementId": "Oz9-wpfUp_7qVhfZq_XSv", - "gap": 20.000000000000455, - "focus": 0.1172219957782847 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "siLekLf7ndrtOq37og-1E", - "type": "arrow", - "x": 3565.066711425782, - "y": 204.8301638648619, - "width": 3.8927154211878587, - "height": 31.133787634667442, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 2102729971, - "version": 17, - "versionNonce": 362724221, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 3.8927154211878587, - -31.133787634667442 - ] - ], - "lastCommittedPoint": null, - "startBinding": null, - "endBinding": { - "elementId": "YAw3xjrLKFSEYmcYEJp5k", - "focus": -0.09374004509087391, - "gap": 3.8722422145236237 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "Oz9-wpfUp_7qVhfZq_XSv", - "type": "rectangle", - "x": 3215.733622233074, - "y": 231.76762317475766, - "width": 93.33333333333303, - "height": 92.6249694824219, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 369954205, - "version": 129, - "versionNonce": 626689747, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "dw9OZGLlCKPeTyXYRqtAy" - }, - { - "id": "KfTYmWxD3C4PPayb6eSZ9", - "type": "arrow" - }, - { - "id": "_scyjoLEs9u8kB235TtQ5", - "type": "arrow" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "id": "dw9OZGLlCKPeTyXYRqtAy", - "type": "text", - "x": 3241.3065388997406, - "y": 256.48010791596863, - "width": 42.1875, - "height": 43.199999999999996, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#a5d8ff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1558345843, - "version": 97, - "versionNonce": 1486111709, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "text": "-2", - "fontSize": 36, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 34, - "containerId": "Oz9-wpfUp_7qVhfZq_XSv", - "originalText": "-2", - "lineHeight": 1.2 - }, - { - "id": "_scyjoLEs9u8kB235TtQ5", - "type": "arrow", - "x": 3199.7334594726567, - "y": 280.8301740373879, - "width": 437.33337402343705, - "height": 323.0208587646484, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 2094383027, - "version": 52, - "versionNonce": 1554527347, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -437.33337402343705, - -323.0208587646484 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "Oz9-wpfUp_7qVhfZq_XSv", - "focus": -0.6070314044928766, - "gap": 16.000162760417425 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "3SONNUcem5JBsIe0DEqmL", - "type": "arrow", - "x": 3479.125988566133, - "y": 87.25190893004165, - "width": 395.3924884033722, - "height": 395.64580281575513, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 567959485, - "version": 176, - "versionNonce": 1215938621, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -395.3924884033722, - -5.088421904372439 - ], - [ - -392.72565897628874, - 390.55738091138267 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "YAw3xjrLKFSEYmcYEJp5k", - "focus": -0.01723764136293494, - "gap": 2.6866534120111183 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "diamond", - "version": 569, - "versionNonce": 1697590803, - "isDeleted": false, - "id": "S3_ciCfD6SYPS-EYJaV4_", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3701.13348388672, - "y": 554.3864128476093, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 119.20017496744829, - "height": 120.01246134440089, - "seed": 1801127059, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "9KjD-G5e8Y37-OJ2ltPsB" - }, - { - "id": "7R5AB9CyKO18BnxhCHdhk", - "type": "arrow" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 599, - "versionNonce": 724611229, - "isDeleted": false, - "id": "9KjD-G5e8Y37-OJ2ltPsB", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 3737.2055460611996, - "y": 594.3895281837096, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 47.455963134765625, - "height": 40, - "seed": 290650675, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714402202187, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "Awake\n(211)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "S3_ciCfD6SYPS-EYJaV4_", - "originalText": "Awake\n(211)", - "lineHeight": 1.25, - "baseline": 34 - }, - { - "id": "oftnwFxFjOUyzkU8S7icv", - "type": "ellipse", - "x": 3839.733357747397, - "y": 567.1426333472837, - "width": 106.31256103515636, - "height": 106.31256103515636, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "#a5d8ff", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 754283955, - "version": 107, - "versionNonce": 1642460083, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "jNTIw0mf1j0FAAEOXLIKB" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "id": "jNTIw0mf1j0FAAEOXLIKB", - "type": "text", - "x": 3864.4324996193372, - "y": 595.2117474482279, - "width": 56.73994445800781, - "height": 50, - "angle": 0, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 393036019, - "version": 140, - "versionNonce": 241865981, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "text": "length\n212", - "fontSize": 20, - "fontFamily": 1, - "textAlign": "center", - "verticalAlign": "middle", - "baseline": 43, - "containerId": "oftnwFxFjOUyzkU8S7icv", - "originalText": "length\n212", - "lineHeight": 1.25 - }, - { - "type": "diamond", - "version": 393, - "versionNonce": 1391658323, - "isDeleted": false, - "id": "pInF-NX9Wlny_wLJB_Olk", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4067.1334431966147, - "y": 410.7613823300311, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 179.2000732421875, - "height": 170, - "seed": 1866376349, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "id": "6nOlra-spVR8nmex7M75z", - "type": "arrow" - }, - { - "id": "m7Efs73wnsp9FlzEweaij", - "type": "arrow" - }, - { - "id": "mw0EQBA_FTBKqMGDbdbhG", - "type": "arrow" - }, - { - "type": "text", - "id": "5mEDN53hcN7KdA3b94n0o" - }, - { - "id": "ehMKCfJF_oyC83sa07rsd", - "type": "arrow" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 380, - "versionNonce": 1302916445, - "isDeleted": false, - "id": "5mEDN53hcN7KdA3b94n0o", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4130.113484700521, - "y": 458.2613823300311, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 53.63995361328125, - "height": 75, - "seed": 77203709, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Data\nready\n200", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "pInF-NX9Wlny_wLJB_Olk", - "originalText": "Data\nready\n200", - "lineHeight": 1.25, - "baseline": 68 - }, - { - "type": "diamond", - "version": 428, - "versionNonce": 1873601267, - "isDeleted": false, - "id": "hXmuLQ7QlCPx-ZZRQZztB", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4067.1334431966147, - "y": 201.0738823300312, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 179.2000732421875, - "height": 170, - "seed": 766647251, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "id": "6nOlra-spVR8nmex7M75z", - "type": "arrow" - }, - { - "id": "m7Efs73wnsp9FlzEweaij", - "type": "arrow" - }, - { - "id": "mw0EQBA_FTBKqMGDbdbhG", - "type": "arrow" - }, - { - "type": "text", - "id": "QJFtxfzgrxBna2fmVnfjR" - }, - { - "id": "1zSOF8pPIkVBMsOaLs13r", - "type": "arrow" - }, - { - "id": "kINfovyMzyKOApwv8qrMF", - "type": "arrow" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 416, - "versionNonce": 1480407485, - "isDeleted": false, - "id": "QJFtxfzgrxBna2fmVnfjR", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4127.273480733236, - "y": 261.0738823300312, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 59.31996154785156, - "height": 50, - "seed": 2135239027, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Awake\n(210)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "hXmuLQ7QlCPx-ZZRQZztB", - "originalText": "Awake\n(210)", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "type": "diamond", - "version": 549, - "versionNonce": 1083837587, - "isDeleted": false, - "id": "arV-pBSIgBpdJNr-qVS9S", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4069.7998657226567, - "y": 0.01141284760939243, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 179.2000732421875, - "height": 154.31251525878906, - "seed": 1155970429, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "7ZTJ9EuV747mt0glQQ213" - }, - { - "id": "6nOlra-spVR8nmex7M75z", - "type": "arrow" - }, - { - "id": "m7Efs73wnsp9FlzEweaij", - "type": "arrow" - }, - { - "id": "mw0EQBA_FTBKqMGDbdbhG", - "type": "arrow" - }, - { - "id": "W7sho896R5OQE8GjmjKz6", - "type": "arrow" - }, - { - "id": "OhdW0e5A8-zTkTy3_X5Ty", - "type": "arrow" - } - ], - "updated": 1714402202187, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 540, - "versionNonce": 927529501, - "isDeleted": false, - "id": "7ZTJ9EuV747mt0glQQ213", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4130.709907531738, - "y": 52.08954166230666, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 57.77995300292969, - "height": 50, - "seed": 281869789, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Reset\n(222)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "arV-pBSIgBpdJNr-qVS9S", - "originalText": "Reset\n(222)", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "id": "1zSOF8pPIkVBMsOaLs13r", - "type": "arrow", - "x": 4061.066975911459, - "y": 287.14268420991397, - "width": 365.33345540364553, - "height": 209.74340021103552, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 980812979, - "version": 259, - "versionNonce": 2037449267, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -365.33345540364553, - -1.6875203450521212 - ], - [ - -360.0001017252607, - 208.0558798659834 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "hXmuLQ7QlCPx-ZZRQZztB", - "focus": -0.01777290911947176, - "gap": 4.950575195143216 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "BTbmeY-DZvSeRXSUnxmaR", - "type": "arrow", - "x": 3995.7336222330737, - "y": 496.83018420991397, - "width": 58.666585286458485, - "height": 1.33331298828125, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1756035645, - "version": 84, - "versionNonce": 1404939901, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 58.666585286458485, - -1.33331298828125 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "Hg85iUuDbtM_3FFIdLgHk", - "focus": 0.05116654225737033, - "gap": 13.800547281901345 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "kINfovyMzyKOApwv8qrMF", - "type": "arrow", - "x": 4161.066874186199, - "y": 403.4968508765806, - "width": 0, - "height": 31.999969482421875, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 190266579, - "version": 50, - "versionNonce": 320357331, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 0, - -31.999969482421875 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "hXmuLQ7QlCPx-ZZRQZztB", - "focus": 0.04836375666692547, - "gap": 26.504771972258368 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "W7sho896R5OQE8GjmjKz6", - "type": "arrow", - "x": 4155.733622233074, - "y": 202.1635378882994, - "width": 1.3332112630205302, - "height": 41.687520345052064, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1392308659, - "version": 24, - "versionNonce": 733475645, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 1.3332112630205302, - -41.687520345052064 - ] - ], - "lastCommittedPoint": null, - "startBinding": null, - "endBinding": { - "elementId": "arV-pBSIgBpdJNr-qVS9S", - "focus": -0.003696658593147046, - "gap": 6.184233190448403 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "OhdW0e5A8-zTkTy3_X5Ty", - "type": "arrow", - "x": 4059.7336629231777, - "y": 83.49685087658071, - "width": 652.0001220703125, - "height": 125.33332824707028, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1440070461, - "version": 242, - "versionNonce": 39955219, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202187, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -281.3334147135415, - 6.666666666666629 - ], - [ - -274.666748046875, - -115.99998474121094 - ], - [ - -652.0001220703125, - -118.66666158040366 - ], - [ - -650.6668090820312, - 0 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "arV-pBSIgBpdJNr-qVS9S", - "focus": -0.05142061028717455, - "gap": 11.364500694371351 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "rectangle", - "version": 709, - "versionNonce": 1207437309, - "isDeleted": false, - "id": "FtpcpLBg7XTyN8Q-iuuhN", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4354.1992899576835, - "y": 454.45519438244, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 389.06699625650896, - "height": 92.375, - "seed": 920608957, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "mRGCQ2R3y_I5P3iHWd2ae" - }, - { - "id": "JNhkwkzBMK3KtrBKhdLjN", - "type": "arrow" - }, - { - "id": "QOwONcVPbJwb_R67i2dvy", - "type": "arrow" - }, - { - "id": "Diu-lUS065dRmY2yluVCr", - "type": "arrow" - }, - { - "id": "m7Efs73wnsp9FlzEweaij", - "type": "arrow" - }, - { - "id": "1zSOF8pPIkVBMsOaLs13r", - "type": "arrow" - }, - { - "id": "9CJJ1LycDQEREP5vkV0cB", - "type": "arrow" - }, - { - "id": "ehMKCfJF_oyC83sa07rsd", - "type": "arrow" - } - ], - "updated": 1714402202188, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 722, - "versionNonce": 1500136019, - "isDeleted": false, - "id": "mRGCQ2R3y_I5P3iHWd2ae", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4478.142837524415, - "y": 488.14269438244, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 141.17990112304688, - "height": 25, - "seed": 1829650717, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714402202188, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "send data (2)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "FtpcpLBg7XTyN8Q-iuuhN", - "originalText": "send data (2)", - "lineHeight": 1.25, - "baseline": 18 - }, - { - "type": "diamond", - "version": 593, - "versionNonce": 2064646237, - "isDeleted": false, - "id": "m2D24vjPA_gORtFep5SPy", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4338.1334533691415, - "y": 570.1155795142761, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 119.20017496744829, - "height": 120.01246134440089, - "seed": 1292620723, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "OOC9aSSv1jP2NyT4crZBP" - }, - { - "id": "sPKerDNeKg2PLeAGbriiV", - "type": "arrow" - } - ], - "updated": 1714402202188, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 637, - "versionNonce": 2117369843, - "isDeleted": false, - "id": "OOC9aSSv1jP2NyT4crZBP", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4374.493509928387, - "y": 610.1186948503763, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 46.879974365234375, - "height": 40, - "seed": 509404499, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714402202188, - "link": null, - "locked": false, - "fontSize": 16, - "fontFamily": 1, - "text": "Ready\n200", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "m2D24vjPA_gORtFep5SPy", - "originalText": "Ready\n200", - "lineHeight": 1.25, - "baseline": 34 - }, - { - "id": "7R5AB9CyKO18BnxhCHdhk", - "type": "arrow", - "x": 3766.400227864584, - "y": 684.8301638648618, - "width": 676.0000610351562, - "height": 232.000020345052, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1358347187, - "version": 139, - "versionNonce": 222948541, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202188, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 5.33335367838572, - 62.66672770182299 - ], - [ - -670.6667073567705, - 60 - ], - [ - -669.333292643229, - -169.33329264322902 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "S3_ciCfD6SYPS-EYJaV4_", - "focus": 0.005504071076706266, - "gap": 11.37146098279895 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "sPKerDNeKg2PLeAGbriiV", - "type": "arrow", - "x": 4335.733622233074, - "y": 640.4760175432473, - "width": 553.3334350585942, - "height": 106.31245930989576, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 829945651, - "version": 150, - "versionNonce": 908462483, - "isDeleted": false, - "boundElements": [], - "updated": 1714402202188, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -553.3334350585942, - 106.31245930989576 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "m2D24vjPA_gORtFep5SPy", - "focus": 0.02596219984013676, - "gap": 8.999315978165313 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "ellipse", - "version": 129, - "versionNonce": 913661747, - "isDeleted": false, - "id": "Qg70WrqvJvAZqUQdsaJW2", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4469.244008382162, - "y": 578.6321963355649, - "strokeColor": "#2f9e44", - "backgroundColor": "#a5d8ff", - "width": 106.31256103515636, - "height": 106.31256103515636, - "seed": 867311827, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "5irT9X_2vVwnIi9aIebbv" - } - ], - "updated": 1714402202188, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 166, - "versionNonce": 494510461, - "isDeleted": false, - "id": "5irT9X_2vVwnIi9aIebbv", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4497.63313743672, - "y": 619.2013104365092, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 49.35997009277344, - "height": 25, - "seed": 821857907, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": null, - "updated": 1714402202188, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "data", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Qg70WrqvJvAZqUQdsaJW2", - "originalText": "data", - "lineHeight": 1.25, - "baseline": 18 - }, - { - "type": "ellipse", - "version": 186, - "versionNonce": 1233715411, - "isDeleted": false, - "id": "Hj8Y-N9g-vkVLjheEqryL", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4585.243967692058, - "y": 573.9238426571794, - "strokeColor": "#2f9e44", - "backgroundColor": "#a5d8ff", - "width": 135.64605712890648, - "height": 120, - "seed": 70032979, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [ - { - "type": "text", - "id": "9jgGwnDb83wjrii2byeKD" - } - ], - "updated": 1714402202188, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 262, - "versionNonce": 807561693, - "isDeleted": false, - "id": "9jgGwnDb83wjrii2byeKD", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4610.348916478114, - "y": 608.9974357859866, - "strokeColor": "#2f9e44", - "backgroundColor": "transparent", - "width": 85.51991271972656, - "height": 50, - "seed": 1481333747, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714402202188, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "checksum\n212", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Hj8Y-N9g-vkVLjheEqryL", - "originalText": "checksum\n212", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "type": "diamond", - "version": 433, - "versionNonce": 687304307, - "isDeleted": false, - "id": "V-1etljVxtQwcu221FZSg", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4817.800150553385, - "y": 419.74056934174985, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 179.2000732421875, - "height": 170, - "seed": 334955155, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "id": "6nOlra-spVR8nmex7M75z", - "type": "arrow" - }, - { - "id": "m7Efs73wnsp9FlzEweaij", - "type": "arrow" - }, - { - "id": "mw0EQBA_FTBKqMGDbdbhG", - "type": "arrow" - }, - { - "type": "text", - "id": "45G3D4MCqIc1cPv4vD3f0" - }, - { - "id": "9CJJ1LycDQEREP5vkV0cB", - "type": "arrow" - }, - { - "id": "4lod7oOAXX-jO_Hv7jZKL", - "type": "arrow" - } - ], - "updated": 1714402202188, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 436, - "versionNonce": 391325245, - "isDeleted": false, - "id": "45G3D4MCqIc1cPv4vD3f0", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4883.460192362467, - "y": 479.74056934174985, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 48.27995300292969, - "height": 50, - "seed": 29308979, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": null, - "updated": 1714402202188, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Pass\n180", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "V-1etljVxtQwcu221FZSg", - "originalText": "Pass\n180", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "type": "diamond", - "version": 428, - "versionNonce": 1388939027, - "isDeleted": false, - "id": "ZkVhJwzREaCp7dn6TmykB", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4818.466756184896, - "y": 201.45517403738802, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 179.2000732421875, - "height": 170, - "seed": 1996567411, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "4_qirs90h6wIvspTeT81U" - }, - { - "id": "AMA5cBcDp3AGK_DAtKN6X", - "type": "arrow" - }, - { - "id": "JK9kK6IN5MgtGzFGm928P", - "type": "arrow" - } - ], - "updated": 1714402207146, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 412, - "versionNonce": 1710053021, - "isDeleted": false, - "id": "4_qirs90h6wIvspTeT81U", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4881.446797688803, - "y": 248.95517403738802, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 53.63995361328125, - "height": 75, - "seed": 1779453203, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714402202188, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Data\nready\n200", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "ZkVhJwzREaCp7dn6TmykB", - "originalText": "Data\nready\n200", - "lineHeight": 1.25, - "baseline": 68 - }, - { - "type": "diamond", - "version": 601, - "versionNonce": 1785825693, - "isDeleted": false, - "id": "Tl-ydMOspxNXtENv4jiYa", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4819.800170898439, - "y": -8.367734999884135, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 179.2000732421875, - "height": 154.31251525878906, - "seed": 1733794205, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "boundElements": [ - { - "type": "text", - "id": "CXRF6zGubqqXg0gdFl9lE" - }, - { - "id": "Ff0iSsU3vymIPvV0oTBVy", - "type": "arrow" - }, - { - "id": "YaEtw2Rso7AeO2nXSnljt", - "type": "arrow" - } - ], - "updated": 1714402223042, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 590, - "versionNonce": 1453488893, - "isDeleted": false, - "id": "CXRF6zGubqqXg0gdFl9lE", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 4880.710212707521, - "y": 43.71039381481313, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 57.77995300292969, - "height": 50, - "seed": 1473011197, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714402202188, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Reset\n(222)", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Tl-ydMOspxNXtENv4jiYa", - "originalText": "Reset\n(222)", - "lineHeight": 1.25, - "baseline": 43 - }, - { - "id": "9CJJ1LycDQEREP5vkV0cB", - "type": "arrow", - "x": 4755.733622233074, - "y": 507.4968407040546, - "width": 55.99995930989462, - "height": 4.354044596354129, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 411305267, - "version": 23, - "versionNonce": 924090195, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202188, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 55.99995930989462, - -4.354044596354129 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "FtpcpLBg7XTyN8Q-iuuhN", - "focus": 0.37428882041044537, - "gap": 12.467336018882179 - }, - "endBinding": { - "elementId": "V-1etljVxtQwcu221FZSg", - "focus": 0.10630509896380333, - "gap": 5.3344057624532795 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "ehMKCfJF_oyC83sa07rsd", - "type": "arrow", - "x": 4251.73358154297, - "y": 496.83018420991397, - "width": 85.333251953125, - "height": 0.979156494140625, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 575160349, - "version": 30, - "versionNonce": 1374950237, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202188, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 85.333251953125, - 0.979156494140625 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "pInF-NX9Wlny_wLJB_Olk", - "focus": -0.00025031638453241643, - "gap": 4.491931604175804 - }, - "endBinding": { - "elementId": "FtpcpLBg7XTyN8Q-iuuhN", - "focus": 0.008355951359004408, - "gap": 17.13245646158839 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "type": "rectangle", - "version": 604, - "versionNonce": 2105281821, - "isDeleted": false, - "id": "ot3I2R6hxCeL2d-X5XmXe", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 5092.199981689455, - "y": 463.60100737072116, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 238.40028889973928, - "height": 92.375, - "seed": 323555933, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "boundElements": [ - { - "type": "text", - "id": "GPevYv6fJ04dMT57Er6Il" - }, - { - "id": "4lod7oOAXX-jO_Hv7jZKL", - "type": "arrow" - }, - { - "id": "68P5vEf1kj79sIFqAd-rT", - "type": "arrow" - } - ], - "updated": 1714402292436, - "link": null, - "locked": false - }, - { - "type": "text", - "version": 621, - "versionNonce": 875798461, - "isDeleted": false, - "id": "GPevYv6fJ04dMT57Er6Il", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "angle": 0, - "x": 5168.940172831219, - "y": 497.28850737072116, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "width": 84.91990661621094, - "height": 25, - "seed": 546356925, - "groupIds": [], - "frameId": null, - "roundness": null, - "boundElements": [], - "updated": 1714402202188, - "link": null, - "locked": false, - "fontSize": 20, - "fontFamily": 1, - "text": "Complete", - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "ot3I2R6hxCeL2d-X5XmXe", - "originalText": "Complete", - "lineHeight": 1.25, - "baseline": 18 - }, - { - "id": "4lod7oOAXX-jO_Hv7jZKL", - "type": "arrow", - "x": 5009.066955566406, - "y": 503.4968508765806, - "width": 66.66666666666788, - "height": 0.979156494140625, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 486237437, - "version": 26, - "versionNonce": 304416403, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202188, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 66.66666666666788, - 0.979156494140625 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "V-1etljVxtQwcu221FZSg", - "focus": -0.032199218974210915, - "gap": 9.207087089805093 - }, - "endBinding": { - "elementId": "ot3I2R6hxCeL2d-X5XmXe", - "focus": 0.06925409328960842, - "gap": 16.46635945638036 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "AMA5cBcDp3AGK_DAtKN6X", - "type": "arrow", - "x": 4907.733540852865, - "y": 404.8302147274921, - "width": 0, - "height": 31.020863850911496, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 105078749, - "version": 20, - "versionNonce": 1401069597, - "isDeleted": false, - "boundElements": null, - "updated": 1714402202188, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 0, - -31.020863850911496 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "ZkVhJwzREaCp7dn6TmykB", - "focus": -0.003719328313829567, - "gap": 24.44243388633941 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "JK9kK6IN5MgtGzFGm928P", - "type": "arrow", - "x": 4810.400166829428, - "y": 295.4968712216327, - "width": 515.9999593098955, - "height": 194.6666463216145, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1726282973, - "version": 82, - "versionNonce": 2094640499, - "isDeleted": false, - "boundElements": null, - "updated": 1714402207146, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -515.9999593098955, - -3.9999898274738825 - ], - [ - -510.66650390625, - 190.66665649414062 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "ZkVhJwzREaCp7dn6TmykB", - "focus": -0.11528001113239493, - "gap": 12.111348600589459 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "Ff0iSsU3vymIPvV0oTBVy", - "type": "arrow", - "x": 4906.400329589844, - "y": 204.83021472749215, - "width": 1.3332112630214397, - "height": 48.000030517578125, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 512433747, - "version": 71, - "versionNonce": 106557875, - "isDeleted": false, - "boundElements": null, - "updated": 1714402215746, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 1.3332112630214397, - -48.000030517578125 - ] - ], - "lastCommittedPoint": null, - "startBinding": null, - "endBinding": { - "elementId": "Tl-ydMOspxNXtENv4jiYa", - "focus": -0.008690951667139494, - "gap": 9.336132978037355 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "YaEtw2Rso7AeO2nXSnljt", - "type": "arrow", - "x": 4806.400329589844, - "y": 68.83017912365102, - "width": 1020.0001017252598, - "height": 98.66666158040366, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 70730579, - "version": 103, - "versionNonce": 220313917, - "isDeleted": false, - "boundElements": null, - "updated": 1714402223042, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -1020.0001017252598, - -98.66666158040366 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "Tl-ydMOspxNXtENv4jiYa", - "focus": -0.12967246181940068, - "gap": 8.775315972701492 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "68P5vEf1kj79sIFqAd-rT", - "type": "arrow", - "x": 5215.733662923178, - "y": 446.5072675432473, - "width": 4582.000122070312, - "height": 632.0000076293943, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1560716179, - "version": 243, - "versionNonce": 927361043, - "isDeleted": false, - "boundElements": null, - "updated": 1714402292447, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 3.99993896484375, - -632.0000076293943 - ], - [ - -4574.000244140624, - -631.9999694824216 - ], - [ - -4578.000183105468, - -81.99996948242188 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "ot3I2R6hxCeL2d-X5XmXe", - "focus": 0.0329144494018595, - "gap": 17.093739827473883 - }, - "endBinding": { - "elementId": "1AceduuKyjAEicwMtKieL", - "focus": -0.057442993023585305, - "gap": 13.89269583565897 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "SQeHgmklU1eLjMjKYB3QA", - "type": "arrow", - "x": 759.7333730061866, - "y": 430.50735909598166, - "width": 50, - "height": 1.999969482421875, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1610858323, - "version": 60, - "versionNonce": 735286899, - "isDeleted": false, - "boundElements": null, - "updated": 1714402299526, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 50, - 1.999969482421875 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "1AceduuKyjAEicwMtKieL", - "focus": 0.031052749665004417, - "gap": 14.133336385092889 - }, - "endBinding": { - "elementId": "9swZXvIOWgkhnpW8_AAOL", - "focus": -0.12230991534358891, - "gap": 18.364796085822178 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "7gp1qzUOY0czzGF-6vJCI", - "type": "arrow", - "x": 1021.6617896670404, - "y": 430.3033352806451, - "width": 38.857160295758945, - "height": 1.4463588169642208, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 524070557, - "version": 37, - "versionNonce": 1269192755, - "isDeleted": false, - "boundElements": null, - "updated": 1714402308643, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 38.857160295758945, - -1.4463588169642208 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "9swZXvIOWgkhnpW8_AAOL", - "focus": 0.08849025862569071, - "gap": 10.880320615228342 - }, - "endBinding": { - "elementId": "JZ0K6tXACCW2EGqwMGHf3", - "focus": -0.0029291448634439937, - "gap": 16.281159900481953 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "p0lrF4zO3SgeKn0sD5HfT", - "type": "arrow", - "x": 1291.3761451357902, - "y": 429.16049557640406, - "width": 34.28571428571422, - "height": 0.30351911272316556, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1593714547, - "version": 20, - "versionNonce": 424538205, - "isDeleted": false, - "boundElements": null, - "updated": 1714402312014, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 34.28571428571422, - -0.30351911272316556 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "JZ0K6tXACCW2EGqwMGHf3", - "focus": 0.12520801716350022, - "gap": 9.775986444383989 - }, - "endBinding": { - "elementId": "DhXnx6-g9HjL7buARwkue", - "focus": -0.015339576010003958, - "gap": 9.1929128429489 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "sY2dxYIT00ouAkmYO_J5N", - "type": "arrow", - "x": 1538.233270554317, - "y": 426.87481616792195, - "width": 27.4285888671875, - "height": 2.5892857142857792, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1774796061, - "version": 10, - "versionNonce": 1112526291, - "isDeleted": false, - "boundElements": null, - "updated": 1714402315057, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 27.4285888671875, - -2.5892857142857792 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "DhXnx6-g9HjL7buARwkue", - "focus": 0.1284520162189083, - "gap": 15.318004559229735 - }, - "endBinding": { - "elementId": "91QgijPwAjHPsQPXDhXJd", - "focus": 0.1509172589325972, - "gap": 10.251973714646624 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "5_6i8jy9BLi4Raoq7wu8Y", - "type": "arrow", - "x": 1428.5190022786473, - "y": 532.017638433547, - "width": 737.1429007393972, - "height": 139.4286237444196, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1019131155, - "version": 154, - "versionNonce": 1610728093, - "isDeleted": false, - "boundElements": null, - "updated": 1714402322456, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -4.57144601004461, - 100.57146344866067 - ], - [ - -731.4286150251114, - 97.14285714285711 - ], - [ - -737.1429007393972, - -38.857160295758945 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "DhXnx6-g9HjL7buARwkue", - "focus": -0.07708710629889162, - "gap": 17.262405492385923 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "auFN6X7BnJ9hTAiNS4d53", - "type": "arrow", - "x": 1669.6618419828885, - "y": 534.3034050351094, - "width": 241.14283970424117, - "height": 108.57142857142856, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1695517629, - "version": 69, - "versionNonce": 1127335795, - "isDeleted": false, - "boundElements": null, - "updated": 1714402335445, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -7.99996512276789, - 108.57142857142856 - ], - [ - -241.14283970424117, - 99.42853655133922 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "91QgijPwAjHPsQPXDhXJd", - "focus": -0.11766277429885352, - "gap": 18.81477118916989 - }, - "endBinding": null, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "QvahvFaqmITq3A9lK6PSc", - "type": "arrow", - "x": 1771.3760579427098, - "y": 429.16049557640406, - "width": 42.28585379464289, - "height": 0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1524725843, - "version": 50, - "versionNonce": 773161779, - "isDeleted": false, - "boundElements": null, - "updated": 1714402341100, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 42.28585379464289, - 0 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "91QgijPwAjHPsQPXDhXJd", - "focus": 0.021633037663064965, - "gap": 11.409386411551353 - }, - "endBinding": { - "elementId": "4WQmrGNtNOnpuJTz3oKzu", - "focus": -0.10604307886777632, - "gap": 20.73817371186601 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - }, - { - "id": "pU5xOgFud-RCppGcjI_Fe", - "type": "arrow", - "x": 2044.5190197172635, - "y": 429.16049557640406, - "width": 153.1427873883929, - "height": 2.2856794084821104, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 562341075, - "version": 50, - "versionNonce": 1088880563, - "isDeleted": false, - "boundElements": null, - "updated": 1714402344330, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 153.1427873883929, - -2.2856794084821104 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "4WQmrGNtNOnpuJTz3oKzu", - "focus": 0.1365278334171839, - "gap": 5.318885439919768 - }, - "endBinding": { - "elementId": "X9ZeCduHYuJ72eB5ZETh8", - "focus": -0.09890601847601437, - "gap": 16.738247825984217 - }, - "startArrowhead": null, - "endArrowhead": "arrow" - } - ], - "appState": { - "gridSize": null, - "viewBackgroundColor": "#ffffff" - }, - "files": {} -} \ No newline at end of file diff --git a/.vscode/examples/demo.ipynb b/.vscode/examples/demo.ipynb deleted file mode 100644 index f8cdb42..0000000 --- a/.vscode/examples/demo.ipynb +++ /dev/null @@ -1,296 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import serial\n", - "import time\n", - "import matplotlib.pyplot as plt\n", - "from IPython import display\n", - "plt.style.use(\"dark_background\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "#helper functions\n", - "# plot autoscale\n", - "def autoscale(data: list, axis:plt.Axes):\n", - " if len(data) == 0: return\n", - " if len(data) == 1: \n", - " axis.set_ylim([data[0]-1, data[0]+1])\n", - " return \n", - " l, h = axis.get_ylim()\n", - " x, d = np.min(data), np.max(data)\n", - " r = d - x\n", - "\n", - " _l, _h, a = l, h, False\n", - " if (x < l) or (l < x - r):\n", - " _l = x - 0.05 * r\n", - " a = True\n", - " if (d > h) or (h > l + r):\n", - " _h = d + 0.05 * r\n", - " a = True\n", - " if a and _l != _h:\n", - " axis.set_ylim([_l, _h])\n", - "\n", - "# convert protocol to ambit array\n", - "def gen_cmd_arr_line(num:int, freq:int, actinic:int)->list:\n", - " return [2, 0, num//256, num%256, freq//256, freq%256, actinic, 1]\n", - "\n", - "\n", - "# flatten ambit array, calc time, actinic array\n", - "def calc_arr_param(cmd: list, persist:bool = False):\n", - " cmd_arr = np.reshape(cmd, (-1, 8))\n", - " arr_length = cmd_arr.shape[0]\n", - " cmd_str = \"arrun1,\" + str(arr_length) + \",\" + str(persist) + \",\"+ str(cmd)[1:-1] + \",\\n\"\n", - " cmd_str = cmd_str.replace(\" \", \"\")\n", - "\n", - " num_pts = cmd_arr[:, 2] * 256 + cmd_arr[:, 3]\n", - " act_arr = cmd_arr[:, 6]\n", - " mea_feq = 1 / (cmd_arr[:, 4] * 256 + cmd_arr[:, 5])\n", - " _t, mea_tml, mea_act = 0, [], []\n", - " for _n, _f, _a in zip(num_pts, mea_feq, act_arr):\n", - " mea_tml += (np.arange(_n) * _f + _t).tolist()\n", - " mea_act += [_a] * _n\n", - " _t = mea_tml[-1]\n", - " mea_tml = np.array(mea_tml) * 0.854\n", - " return cmd_str, mea_tml, mea_act\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "cmd: hello\n", - "\n", - "NEW Name Here Ready\n", - "\n", - "cmd: set_gains\n", - "\n", - "Gains set to 0, 5, 5, 1, 5, 5\n", - "\n" - ] - } - ], - "source": [ - "with serial.Serial(\"COM10\", baudrate=115200) as ser:\n", - " ser.write(\"hello\\n\\r\".encode())\n", - " ser.flush()\n", - " time.sleep(.5)\n", - " ser.write(\"set_gains,0,5,5,1,5,5\\n\".encode())\n", - "\n", - " l = ser.readline()\n", - " print(l.decode())\n", - " l = ser.readline()\n", - " print(l.decode())\n", - " l = ser.readline()\n", - " print(l.decode())\n", - " l = ser.readline()\n", - " print(l.decode())" - ] - }, - { - "cell_type": "code", - "execution_count": 201, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Text(0.5, 1.0, 'Protocol preview')" - ] - }, - "execution_count": 201, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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0L59+/wyatQotWnTxrftggULtHXrVt14442SpFatWunSSy/VK6+8ogMHDhx2vhNOOEHh4eHatm1bpfs477zz/O5j8eLF6tSpk373u99pyJAhWrVqlfLz8/Xpp59qyJAhatasmfr27avFixfX8FECGj7ejQU4bN26db53Yx2OmVVa2759u/r06VNpvW3btoqIiFBBQcFR73v79u2Kj4+vtH7wc34O7iM/P1+JiYlH3Vdd56lKXFzcYde2b9/ut37o43TwPq+44gpt3rz5iPdz4MABvfrqq7r11lvVsmVLXXPNNWrWrNlRPw+ooKBABw4cUP/+/VVeXl7p+v9cO3hkbMiQIUpJSdGiRYt86w8++KCSkpLUrFkzyg4araC/lkYICWwOd4LyoUlPT7fi4uJK63/4wx/MzGz48OF+63feeWel82sKCgr8zk85mNdee812795t8fHxfusffPBBlefsDBo06LBz1mSeQJyzs379+qM+lp06dbKKigq/k62PlFNPPdXMzCZOnGhff/21ffHFF1X+Pf7znJ2+ffuamdlVV11VrftYvXq1LVq0yPbu3WvJyckmyU488UQzM1u4cKHt3LnTmjRpEvR/n4QEIUEfgBAS4NS17Bx899OuXbvstttus8GDB9u0adOsvLy80rufli5danl5eXbxxRdbr169rEuXLib9/3dj/fjjj3bNNdfYBRdcYK+++qqZmf35z3/23f7gu7GKiops6tSpNmTIELvkkkvs8ccfr/RurOrME4h3Y40cObJaj+WUKVOsoqLCnnnmGbvsssssKSnJrrrqKnvsscfsL3/5S6Xtv/jiC9u8ebPfO+IO/Xsc+m6sZ5991kpKSuzvf/+7XXTRRTZw4EAbPXq0zZ492yZOnOi37ZNPPukrcVFRUb71DRs2mJnZe++9F/R/m4QEKUEfgBAS4NS17Eiy4447zubMmWM5OTlWUVFhWVlZ9tBDD1lkZKTfdmeccYYtW7bMSkpKzMxs6dKlvuu6detm77//vhUWFtqePXts1apVNmbMmEr31bJlS5s5c6Zt2rTJysvLLS8vzz744ANfcarJPDUpO0899ZRNnDjR1q9fb+Xl5bZ27VobPXp0jR7LSy+91JYsWWI7d+60srIyy8rKsnnz5vmOrPxnxo8f7ysjHo+nyr/HoWVHko0dO9a+/PJLKy4uttLSUlu/fr29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ZbW/ETw/P18ZGRmaOHFiPU8GoDYoOwCOOa/X68uuXbtkZn5rRUVFlV7GSk9P17vvvqvU1FTl5eWpsLBQ999/v5o0aaJHH31U27dv15YtW3TjjTf63Vf79u01d+5c7dixQwUFBXrvvffUqVOnI843atQozZ8/32/tiiuu0L/+9S/t3r1bBQUFWrRokVq0aOG7fv78+Ro9enQAHh0AgUbZAdBgJCcnq3379kpKStIdd9yhv/71r/rwww9VWFioPn366Nlnn9Wzzz6rxMRESVLz5s21dOlSlZSUKCkpSf369VNJSYkWLlyoiIiIKu+jVatW6t69u1asWOFbi4uL0xtvvKGXXnpJp59+ugYOHKh33nlHYWFhvm2+/vprdezYUR07dqzfBwFArQT9tTRCSOPNmDFjrLCwsNL6tGnTbNWqVb7L6enplpWVZWFhYb61devW2eeff+67HB4ebsXFxXb11VebJLvxxhtt3bp1fvuNiIiw0tJSS0lJqXKeHj16mJlZYmKib+3MM880M7OOHTse9vfweDxmZpaUlBT0x5QQ4h+O7ABoMNasWSMz8132er364YcffJcPHDig7du3q127dpKkXr166Xe/+52Ki4t92bFjh5o1a6ZTTjmlyvto3ry5JGnPnj2+te+//16LFy/WDz/8oHnz5mn8+PFq1aqV3+3Kysokye+lLQChoWmwBwCA6tq7d6/fZTOrci08/Lf/jwsPD9fKlSt17bXXVtpXfn5+lfdRUFAgSTruuON8Px84cEApKSnq27evhg4dqltuuUUPPfSQ+vTpo02bNkmSWrdufcT9AggejuwAcNa3336rzp07a9u2bdqwYYNfioqKqrzNhg0btGvXLnXt2rXSdZmZmfrLX/6iM888UxUVFRoxYoTvuu7du6uiokJr1qypt98HQO1QdgA467XXXlNBQYHef/999evXTyeeeKKSkpI0a9YsJSQkVHkbM9PixYvVr18/39o555yj1NRU9erVSx06dNDll1+utm3bat26db5t+vfvr2XLlvm9/AUgNFB2ADirrKxMSUlJys7O1jvvvKN169bppZdeUvPmzQ97ZEeSnn/+eY0aNcr3bquioiIlJSXp448/1s8//6wHH3xQd955pxYuXOi7zejRo/XCCy/U++8EoHaCfpY0IYSEWpYvX26jRo2q1rbDhg2zNWvWWJMmTYI+NyGkcjiyAwBVmDBhgpo2rd57OKKjo3XjjTdq//799TwVgNoI02+tBwAAwEkc2QEAAE6j7AAAAKdRdgAAgNMoOwAAwGmUHQAA4DTKDgAAcBplBwAAOI2yAwAAnEbZAQAATvt/qioqeUXpljEAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# create a trace with 3 different setups:\n", - "# 1. 50 points, 10Hz, no actinic\n", - "# 2. 50 points, 50Hz, 240/255(max) actinic\n", - "# 3. 50 points, 10Hz, no actinic\n", - "# number of lines (1 - 8) points(10 - 2000), freq (1 - 200), actinic 0 - 255\n", - "\n", - "\n", - "\n", - "cmd = []\n", - "cmd += gen_cmd_arr_line(num = 50, freq = 10, actinic = 0)\n", - "cmd += gen_cmd_arr_line(num = 50, freq = 50, actinic = 240)\n", - "cmd += gen_cmd_arr_line(num = 50, freq = 10, actinic = 0)\n", - "\n", - "\n", - "# convert to command for ambit, also pre-calculate the time and actinic of each point\n", - "cmd_str, timeline, act_arr = calc_arr_param(cmd, 0)\n", - "plt.plot(timeline, act_arr, 'w,')\n", - "plt.xlabel(\"Time (s)\")\n", - "plt.ylabel(\"Actinic (AU)\")\n", - "plt.title(\"Protocol preview\")" - ] - }, - { - "cell_type": "code", - "execution_count": 220, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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uko588wzlYceekhRFEX5zW9+o87Xrl07ZcOGDUpdXZ1y2223KWPGjFEmTpyoLFiwQBk5cqQCpPfbgQ8++OCDj/w9ioqKlAMHDiiffvqpAkCZPn26oiiKcumll6rziGucbdu2Ka+++qoyfvx4ZfLkycqePXuUzZs3K8uXL1f+/ve/K2effbZy1VVXKbW1tcqrr76qWc+2bduUXbt2Kdu3b1cuu+wy5ZxzzlGeeOIJRVEU5aabbopbl3w9NWDAAOXAgQPKV199pUyZMkU566yzlPvvv18JBoPKHXfcoc4nztNbt25VnnjiCWXcuHHKRRddpGzfvl359ttvFbvdri6vsrJS2b17t+Z6rrnfCz7a7KPZB8AHH8360IuOF110kaIoijJx4kTNfIMHD1YURVGuueYaddq2bduUhoYGpUePHuq0gQMHKoqiKFVVVUpxcbE6fcKECYqiKMq5556rWbeiKMr111+vWdecOXMURVGU0047TQGg/PznP1cURVFmzZqlma9nz55KQ0ODMm/ePHXaqlWrFEVRlFGjRqXcdofDoZxxxhmKoijKiSeeqE5ftGiRokRuh2kemYiOd911l2a+dLaFDz744IOP5A8hIBohhMdMRceKigqloaFBeeONNzTz9erVS/F6vcqTTz6pThPns8suu0wzr9lz6qRJkxRFUZSBAwcm3NYpU6YoiqIol19+ecJ5MjlfnnLKKZp5N27cqLz11lvq37fddpuiKIpy5plnJlxvOr8d+OCDDz74yN9DnDuuuuoqBYBSWlqq1NbWKqtXr1bnEdc4X3zxhWKz2dTpM2fOVBRFUV555RXNMufPn68oiqKUlZWp07Zt26aEQqG489g777yjHDx4UL0uNLqeeuutt5SdO3dqlgdAWbhwodLY2Ki0b99eAWLnaf15+YILLlAURdEIi6+//rqybdu2Zt//fPDB8mpCdJx77rk4cOAAXn/9dTgcDvXx5ZdfYs+ePRg5cqRm/i+//FLTrP+bb74BALz//vuaXlZiur6cDQCeeuopzd9PP/00gEipmRhTOBzGk08+qRnTjz/+iK+++ipuTDU1NVi1alXcevr27YunnnoKe/bsQSgUQjAYxAcffAAAGDBggJndkzYvvvii5u90t4UQQkhqfvOb32DIkCGaRygUymqZw4YNQ0lJCf7xj39opv/www/497//jTPPPDPu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Xbt2ePLJJ3HllVdi//79CZfx9ttvq//fuHEj1qxZgy1btmDq1KlYsGBB3Pxz587F/Pnz1b/LysooPJK0iRMd7daJjkr0/COc9ytWrECnTp1wxx13oHv37ti4cSPGjx+PnTt3WrZOQgghxArodCQkN1B0JKRAsDO9mpC0yaTv3I8//mg4fyAQwP79+3H88cejb9++eP3119XnxQ/KQCCAY445xrDHY2NjIzZs2IB+/foZrtfv98Mf/XwSkintu3XV/L3ti/WWLVv0BHZIQWSPPPIIHnnkEcvWQQghhOSEPIqOTK8mbQmKjoQUCOKEFQ4Fs1pOgOnVpA0h95175ZVX1OljxozBq6++aviaNWvW4LzzztNMGzt2LNatW4dgMIjNmzfjhBNO0Dx/zz33oKysDDfccAN27dpluFy3240BAwagsrIyu40iJAm9TzwOAPDSfX/FD5s2o+qb7yxbthAd7XY77E5H1jfBCCGEEEsw09OR6dWE5ASKjoQUCKrT0aL0avZ0JG2F+fPn44knnsC6deuwZs0aXHXVVZq+c/fddx969uyJqVOnAgCWLFmC3/72t/jrX/+Kxx57DMOGDcPll1+OSy65BADg8/nw9ddfa9Zx8OBBANBMv//++/H6669j586d6NKlC2677TaUl5dj+fLledhq0hZxFxeh21GRsKIN776P2r3WpqWHAjEnrsvtgS/YaOnyCSGEkEwwFSSD/IiOsuAI0OlICh+KjoQUCEJ0DFmWXk3RkbQNUvWd6969O/r06aPOv337dowfPx4LFizAddddh927d2PmzJl46aWX0lpvr1698Mwzz6Bz587Yu3cvPvnkEwwdOpT97kjOKKmogN3hQMDns1xwBGI3rYCIW95HzZEQQkgrQcmyL75ZHC6d6MiejqTASesInz17NtauXYva2lpUV1fj5ZdfRv/+/TXzlJaWYtGiRdi1axcaGxuxadMmXHPNNUmXe8UVV+CDDz5ATU0Nampq8K9//QunnHKKZp4777wTiqJoHnv27Eln+IQUNEJ0zPaEKXo6uuh0JG2IRx55BH379kVRURGGDBmiKXGeNm0aRo0apZn/gw8+wODBg1FUVIQjjzwSjz76aNLlT5s2DRMnTtRMu+SSS9CzZ094PB706tULF1xwAb755hvrNooQHcLBHvD5crL8cCikuu0d7AtMCCGkFZGvIBm7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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "TIMEOUT = 2\n", - "ambit_port = \"COM9\" #<----change to ambit port\n", - "\n", - "\n", - "fig, axes = plt.subplots(1, 3, sharex = True, figsize = (16, 4))\n", - "x = [] # x axis\n", - "d_temp, d_fluo, d_ambient = [], [], []\n", - "axes[0].set_title(\"Temperature\")\n", - "axes[1].set_title(\"Fluorescence\")\n", - "axes[2].set_title(\"Ambient\")\n", - "axes[1].set_xlabel('Time (s)')\n", - "\n", - "l_temp = axes[0].plot(0)\n", - "l_fluo = axes[1].plot(0)\n", - "l_ambient = axes[2].plot(0)\n", - "\n", - "\n", - "ax_spec = axes[2].inset_axes([0.8, 0.1, 0.15, 0.15])\n", - "ax_spec.set_xticks([])\n", - "ax_spec.set_yticks([])\n", - "\n", - "\n", - "\n", - "\n", - "# get spectrometer data\n", - "with serial.Serial(ambit_port, baudrate=115200, timeout = 1) as ser:\n", - " ser.write(\"get_par\".encode())\n", - " _ = ser.readline()\n", - " par = ser.readline().decode()\n", - " spec = ser.readline().decode()\n", - "if len(spec) > 10 and str.count(spec, ',') == 9:\n", - " spec = np.fromstring(spec, sep = ',', dtype= int)\n", - " ax_spec.bar([415, 445, 480,515, 555, 590, 630, 680, 850], spec[:9] + [spec[-1]], width = [30, 30, 30, 40, 40, 40, 60, 60, 100], color = ['indigo','blue','royalblue','c','lime','gold','orange','red','darkred'])\n", - "\n", - "\n", - "# run PAM\n", - "with serial.Serial(ambit_port, baudrate=115200) as ser:\n", - " watchdog_timer_t = time.time()\n", - " ser.write(cmd_str.encode())\n", - " _str = ''\n", - " data_counter = 0\n", - " _line = ''\n", - " run_msg = ''\n", - " new_data = []\n", - " last_plot = time.time()\n", - " pre_act = -1\n", - " while time.time() < watchdog_timer_t + TIMEOUT:\n", - " # readlines from serial\n", - " if ser.in_waiting > 0:\n", - " c = ser.read()\n", - " if (c > bytes([0])) and (c < bytes([128])):\n", - " watchdog_timer_t = time.time()\n", - " _str += c.decode()\n", - " if (c == b\"\\n\") or (c == b\"\\r\"):\n", - " _line = _str\n", - " _str = ''\n", - " if (\"T:\" in _line) and (\",L:\" in _line): # line contains data\n", - " new_data = [float(n.split(':')[1]) for n in _line.strip('\\n').split(',')]\n", - " data_counter += 1\n", - " elif (\"Done\") in _line:\n", - " TIMEOUT = 0\n", - " else:\n", - " run_msg += _line # error msg if any\n", - "\n", - " if len(new_data) == 6: # add data to lists\n", - " x.append(timeline[data_counter - 1])\n", - " d_temp.append(new_data[0])\n", - " d_fluo.append(new_data[1])\n", - " d_ambient.append(new_data[4] - 500) # this offset can be different for different ambits\n", - " # if actinic intensity changed, plot it on middle figure\n", - " if pre_act != act_arr[data_counter - 1]: \n", - " axes[1].text(x[-1], d_fluo[-1], str(act_arr[data_counter - 1]))\n", - " pre_act = act_arr[data_counter - 1]\n", - " new_data = []\n", - "\n", - "\n", - " # update the figure, plotting takes time, so lower the plotting frequency when data are coming in high rate\n", - " if (ser.in_waiting > 50 and time.time() - last_plot > .5) or (ser.in_waiting < 50 and time.time() - last_plot > .2):\n", - " if data_counter < 2: continue\n", - "\n", - " last_plot = time.time()\n", - " _xlim_left, _xlim_right = axes[0].get_xlim() \n", - " if x[-1] >= _xlim_right - 2: axes[0].set_xlim([_xlim_left, x[-1] + 2])\n", - " autoscale(d_temp, axes[0])\n", - " autoscale(d_fluo, axes[1])\n", - " autoscale(d_ambient[1:], axes[2])\n", - " l_temp[0].set_xdata(x)\n", - " l_temp[0].set_ydata(d_temp)\n", - " l_fluo[0].set_xdata(x)\n", - " l_fluo[0].set_ydata(d_fluo)\n", - "\n", - " # ambient data #1 is also off...\n", - " l_ambient[0].set_xdata(x[1:])\n", - " l_ambient[0].set_ydata(d_ambient[1:])\n", - "\n", - "\n", - " display.display(fig)\n", - " display.clear_output(True)\n", - " \n", - "\n", - "plt.show()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/.vscode/examples/includes.txt b/.vscode/examples/includes.txt deleted file mode 100644 index 030ea49..0000000 --- a/.vscode/examples/includes.txt +++ /dev/null @@ -1,69 +0,0 @@ -. -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\qio_qspi\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\cores\esp32 -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\variants\esp32c3 -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\Wire\src -C:\Users\hjcbj\Documents\Arduino\libraries\Adafruit_BusIO -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\SPI\src -c:\users\hjcbj\appdata\local\arduino15\packages\esp32\tools\esp-rv32\2405\riscv32-esp-elf\include\c++\13.2.0 -c:\users\hjcbj\appdata\local\arduino15\packages\esp32\tools\esp-rv32\2405\riscv32-esp-elf\include\c++\13.2.0\riscv32-esp-elf -c:\users\hjcbj\appdata\local\arduino15\packages\esp32\tools\esp-rv32\2405\riscv32-esp-elf\include\c++\13.2.0\backward -c:\users\hjcbj\appdata\local\arduino15\packages\esp32\tools\esp-rv32\2405\lib\gcc\riscv32-esp-elf\13.2.0\include -c:\users\hjcbj\appdata\local\arduino15\packages\esp32\tools\esp-rv32\2405\lib\gcc\riscv32-esp-elf\13.2.0\include-fixed -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp-rv32\2405\riscv32-esp-elf\include\sys -C:\users\hjcbj\appdata\local\arduino15\packages\esp32\tools\esp-rv32\2405\riscv32-esp-elf\include - - - -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\qio_qspi\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\freertos\FreeRTOS-Kernel\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\freertos\esp_additions\include\freertos -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\freertos\FreeRTOS-Kernel\include\freertos -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_hw_support\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\soc\esp32c3\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_common\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_system\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\newlib\platform_include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\heap\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_rom\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\hal\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_timer\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\log\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_driver_gptimer\include\driver -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_driver_gptimer\include - -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\lwip\lwip\src\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\lwip\port\esp32xx\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\lwip\port\freertos\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\lwip\port\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\freertos\esp_additions\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_netif\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_event\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_driver_usb_serial_jtag\include\driver -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_driver_usb_serial_jtag\include - -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_partition\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\fatfs\vfs -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_driver_gpio\include\driver -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_driver_gpio\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_driver_sdmmc\include\driver -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_driver_spi\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\fatfs\src -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\wear_levelling\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_driver_sdmmc\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_driver_uart\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_ringbuf\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_wifi\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_eth\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\wifi_provisioning\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\protocomm\include\common -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\protocomm\include\security -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\freertos\esp_additions\include\freertos -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\freertos\FreeRTOS-Kernel\portable\riscv\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\riscv\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\esp_rom\include\esp32c3 -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\libraries\Preferences\src -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\freertos\FreeRTOS-Kernel\portable\riscv\include\freertos -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\freertos\config\include -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\freertos\config\include\freertos -C:\Users\hjcbj\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-cfea4f7c-v1\esp32c3\include\freertos\config\riscv\include \ No newline at end of file diff --git a/.vscode/examples/includes_MAC.txt b/.vscode/examples/includes_MAC.txt deleted file mode 100644 index a08a27e..0000000 --- a/.vscode/examples/includes_MAC.txt +++ /dev/null @@ -1,69 +0,0 @@ -. -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/qio_qspi/include -/Users/hjc/Library/Arduino15/packages/esp32/hardware/esp32/3.1.1/cores/esp32 -/Users/hjc/Library/Arduino15/packages/esp32/hardware/esp32/3.1.1/variants/esp32c3 -/Users/hjc/Library/Arduino15/packages/esp32/hardware/esp32/3.1.1/libraries/Wire/src -/Users/hjc/Documents/Arduino/libraries/Adafruit_BusIO -/Users/hjc/Library/Arduino15/packages/esp32/hardware/esp32/3.1.1/libraries/SPI/src -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp-rv32/2405/riscv32-esp-elf/include/c++/13.2.0 -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp-rv32/2405/riscv32-esp-elf/include/c++/13.2.0/riscv32-esp-elf -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp-rv32/2405/riscv32-esp-elf/include/c++/13.2.0/backward -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp-rv32/2405/lib/gcc/riscv32-esp-elf/13.2.0/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp-rv32/2405/lib/gcc/riscv32-esp-elf/13.2.0/include-fixed -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp-rv32/2405/riscv32-esp-elf/include/sys -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp-rv32/2405/riscv32-esp-elf/include - - - -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/qio_qspi/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/freertos/FreeRTOS-Kernel/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/freertos/esp_additions/include/freertos -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/freertos/FreeRTOS-Kernel/include/freertos -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_hw_support/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/soc/esp32c3/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_common/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_system/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/newlib/platform_include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/heap/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_rom/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/hal/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_timer/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/log/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_driver_gptimer/include/driver -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_driver_gptimer/include - -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/lwip/lwip/src/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/lwip/port/esp32xx/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/lwip/port/freertos/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/lwip/port/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/freertos/esp_additions/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_netif/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_event/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_driver_usb_serial_jtag/include/driver -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_driver_usb_serial_jtag/include - -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_partition/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/fatfs/vfs -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_driver_gpio/include/driver -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_driver_gpio/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_driver_sdmmc/include/driver -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_driver_spi/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/fatfs/src -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/wear_levelling/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_driver_sdmmc/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_driver_uart/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_ringbuf/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_wifi/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_eth/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/wifi_provisioning/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/protocomm/include/common -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/protocomm/include/security -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/freertos/esp_additions/include/freertos -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/freertos/FreeRTOS-Kernel/portable/riscv/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/riscv/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/esp_rom/include/esp32c3 -/Users/hjc/Library/Arduino15/packages/esp32/hardware/esp32/3.1.1/libraries/Preferences/src -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/freertos/config/include -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/freertos/config/include/freertos -/Users/hjc/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.3-cfea4f7c-v1/esp32c3/include/freertos/config/riscv/include \ No newline at end of file diff --git a/.vscode/examples/phi2_01.png b/.vscode/examples/phi2_01.png deleted file mode 100644 index cb0ecba..0000000 Binary files a/.vscode/examples/phi2_01.png and /dev/null differ diff --git a/.vscode/examples/phi2_02.png b/.vscode/examples/phi2_02.png deleted file mode 100644 index 692fa89..0000000 Binary files a/.vscode/examples/phi2_02.png and /dev/null differ diff --git a/.vscode/examples/phi2_03.png b/.vscode/examples/phi2_03.png deleted file mode 100644 index 40716e4..0000000 Binary files a/.vscode/examples/phi2_03.png and /dev/null differ diff --git a/.vscode/examples/pm25.ipynb b/.vscode/examples/pm25.ipynb deleted file mode 100644 index b615b72..0000000 --- a/.vscode/examples/pm25.ipynb +++ /dev/null @@ -1,473 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "8cf90bfa", - "metadata": {}, - "outputs": [], - "source": [ - "import serial" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3fa0f942", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "225\t184\t50\t2\t0\t0\t\n", - "233\t186\t49\t2\t0\t0\t\n", - "245\t202\t49\t2\t0\t0\t\n", - "247\t201\t48\t2\t0\t0\t\n", - "230\t190\t41\t1\t0\t0\t\n", - "223\t183\t35\t2\t0\t0\t\n", - "216\t174\t34\t3\t0\t0\t\n", - "224\t177\t30\t2\t0\t0\t\n", - "221\t171\t28\t2\t0\t0\t\n", - "219\t169\t27\t2\t0\t0\t\n", - "224\t172\t26\t2\t0\t0\t\n", - "207\t159\t24\t2\t0\t0\t\n", - "214\t159\t26\t2\t0\t0\t\n", - "212\t157\t24\t2\t0\t0\t\n", - "211\t156\t23\t2\t0\t0\t\n", - "207\t154\t20\t1\t0\t0\t\n", - "209\t152\t20\t0\t0\t0\t\n", - "206\t154\t32\t0\t0\t0\t\n", - "192\t147\t31\t0\t0\t0\t\n", - "208\t165\t33\t1\t0\t0\t\n", - "212\t171\t35\t1\t0\t0\t\n", - "226\t183\t35\t1\t0\t0\t\n", - "216\t174\t35\t1\t0\t0\t\n", - "214\t172\t36\t2\t0\t0\t\n", - "225\t183\t39\t2\t0\t0\t\n", - "223\t183\t41\t2\t0\t0\t\n", - "235\t201\t43\t3\t0\t0\t\n", - "228\t198\t29\t3\t0\t0\t\n", - "253\t221\t32\t3\t0\t0\t\n", - "231\t204\t29\t2\t0\t0\t\n", - "222\t193\t27\t2\t0\t0\t\n", - "211\t186\t32\t3\t0\t0\t\n", - "218\t192\t32\t3\t0\t0\t\n", - "221\t191\t31\t2\t0\t0\t\n", - "218\t188\t29\t2\t0\t0\t\n", - "221\t187\t26\t2\t0\t0\t\n", - "220\t184\t24\t1\t0\t0\t\n", - "213\t180\t27\t1\t0\t0\t\n", - "193\t156\t21\t1\t0\t0\t\n", - "210\t168\t24\t1\t0\t0\t\n", - "208\t170\t24\t1\t0\t0\t\n", - "202\t164\t19\t0\t0\t0\t\n", - "195\t164\t23\t0\t0\t0\t\n", - "194\t165\t28\t0\t0\t0\t\n", - "186\t158\t28\t0\t0\t0\t\n", - "188\t160\t30\t0\t0\t0\t\n", - "181\t153\t27\t0\t0\t0\t\n", - "195\t166\t26\t0\t0\t0\t\n", - "200\t170\t27\t0\t0\t0\t\n", - "188\t158\t21\t0\t0\t0\t\n", - "199\t169\t22\t0\t0\t0\t\n", - "204\t170\t22\t0\t0\t0\t\n", - "197\t159\t18\t0\t0\t0\t\n", - "198\t160\t14\t0\t0\t0\t\n", - "201\t162\t14\t0\t0\t0\t\n", - "208\t166\t17\t0\t0\t0\t\n", - "210\t171\t22\t0\t0\t0\t\n", - "192\t154\t22\t0\t0\t0\t\n", - "197\t164\t25\t0\t0\t0\t\n", - "201\t172\t30\t1\t0\t0\t\n", - "209\t178\t35\t1\t0\t0\t\n", - "207\t171\t36\t1\t0\t0\t\n", - "205\t173\t37\t1\t0\t0\t\n", - "203\t171\t36\t1\t0\t0\t\n", - "212\t176\t39\t1\t0\t0\t\n", - "192\t164\t35\t1\t0\t0\t\n", - "170\t142\t31\t1\t0\t0\t\n", - "170\t141\t30\t1\t0\t0\t\n", - "147\t121\t26\t1\t0\t0\t\n", - "131\t106\t21\t0\t0\t0\t\n", - "98\t76\t14\t0\t0\t0\t\n", - "86\t74\t13\t0\t0\t0\t\n", - "72\t61\t10\t0\t0\t0\t\n", - "60\t51\t11\t0\t0\t0\t\n", - "52\t45\t11\t0\t0\t0\t\n", - "63\t56\t11\t0\t0\t0\t\n", - "75\t63\t13\t0\t0\t0\t\n", - "75\t64\t13\t0\t0\t0\t\n", - "92\t78\t14\t0\t0\t0\t\n", - "101\t87\t18\t0\t0\t0\t\n", - "121\t106\t19\t0\t0\t0\t\n", - "130\t114\t23\t0\t0\t0\t\n", - "146\t129\t22\t0\t0\t0\t\n", - "180\t159\t22\t1\t0\t0\t\n", - "188\t160\t19\t1\t0\t0\t\n", - "192\t161\t19\t1\t0\t0\t\n", - "209\t182\t17\t1\t0\t0\t\n", - "215\t187\t16\t1\t0\t0\t\n", - "211\t185\t16\t1\t0\t0\t\n", - "222\t191\t16\t2\t0\t0\t\n", - "225\t187\t18\t2\t0\t0\t\n", - "254\t207\t20\t3\t0\t0\t\n", - "275\t224\t29\t3\t0\t0\t\n", - "264\t215\t35\t2\t0\t0\t\n", - "272\t228\t35\t2\t0\t0\t\n", - "285\t237\t44\t5\t0\t0\t\n", - "283\t231\t47\t7\t1\t1\t\n", - "285\t233\t53\t7\t1\t1\t\n", - "317\t261\t54\t7\t1\t1\t\n", - "331\t273\t54\t6\t1\t1\t\n", - "340\t285\t53\t6\t1\t1\t\n", - "325\t275\t53\t5\t1\t1\t\n", - "303\t253\t46\t5\t1\t1\t\n", - "304\t252\t40\t6\t1\t1\t\n", - "301\t251\t49\t10\t1\t1\t\n", - "293\t247\t41\t8\t1\t1\t\n", - "302\t251\t41\t6\t0\t0\t\n", - "303\t250\t41\t6\t0\t0\t\n", - "262\t213\t40\t6\t0\t0\t\n", - "240\t197\t37\t6\t0\t0\t\n", - "225\t187\t36\t6\t0\t0\t\n", - "215\t177\t31\t6\t0\t0\t\n", - "209\t175\t30\t6\t0\t0\t\n", - "196\t168\t34\t5\t0\t0\t\n", - "188\t158\t23\t1\t0\t0\t\n", - "198\t161\t21\t0\t0\t0\t\n", - "182\t154\t20\t1\t0\t0\t\n", - "172\t144\t15\t2\t0\t0\t\n", - "187\t157\t18\t3\t0\t0\t\n", - "192\t156\t19\t3\t0\t0\t\n", - "188\t149\t19\t3\t0\t0\t\n", - "196\t161\t20\t3\t0\t0\t\n", - "203\t169\t21\t3\t0\t0\t\n", - "207\t167\t18\t3\t0\t0\t\n", - "216\t181\t23\t3\t0\t0\t\n", - "202\t177\t24\t3\t0\t0\t\n", - "204\t179\t22\t2\t0\t0\t\n", - "216\t189\t23\t1\t0\t0\t\n", - "215\t184\t21\t1\t0\t0\t\n", - "219\t192\t23\t1\t0\t0\t\n", - "211\t186\t22\t1\t0\t0\t\n", - "196\t172\t21\t1\t0\t0\t\n", - "205\t176\t26\t2\t0\t0\t\n", - "202\t175\t25\t2\t0\t0\t\n", - "195\t160\t20\t2\t0\t0\t\n", - "202\t162\t20\t2\t0\t0\t\n", - "194\t154\t28\t2\t0\t0\t\n", - "194\t158\t32\t2\t0\t0\t\n", - "206\t172\t32\t1\t0\t0\t\n", - "222\t186\t41\t2\t0\t0\t\n", - "241\t199\t42\t2\t0\t0\t\n", - "251\t208\t45\t2\t0\t0\t\n", - "247\t208\t41\t1\t0\t0\t\n", - "263\t228\t47\t1\t0\t0\t\n", - "256\t226\t51\t1\t0\t0\t\n", - "257\t232\t55\t1\t0\t0\t\n", - "263\t232\t50\t1\t0\t0\t\n", - "255\t222\t48\t4\t0\t0\t\n", - "255\t222\t52\t4\t0\t0\t\n", - "227\t198\t41\t3\t0\t0\t\n", - "220\t197\t42\t3\t0\t0\t\n", - "228\t196\t40\t3\t0\t0\t\n", - "233\t197\t38\t3\t0\t0\t\n", - "211\t176\t34\t3\t0\t0\t\n", - "215\t182\t33\t3\t0\t0\t\n", - "205\t171\t30\t3\t0\t0\t\n", - "200\t168\t28\t3\t0\t0\t\n", - "205\t169\t24\t0\t0\t0\t\n", - "196\t162\t19\t0\t0\t0\t\n", - "205\t167\t18\t0\t0\t0\t\n", - "211\t171\t18\t0\t0\t0\t\n", - "199\t166\t18\t0\t0\t0\t\n", - "180\t152\t23\t0\t0\t0\t\n", - "188\t155\t26\t0\t0\t0\t\n", - "191\t158\t23\t0\t0\t0\t\n", - "214\t177\t25\t0\t0\t0\t\n", - "219\t182\t24\t0\t0\t0\t\n", - "211\t176\t25\t0\t0\t0\t\n", - "202\t169\t25\t0\t0\t0\t\n", - "207\t176\t28\t0\t0\t0\t\n", - "200\t169\t27\t0\t0\t0\t\n", - "213\t185\t26\t1\t0\t0\t\n", - "212\t179\t22\t1\t0\t0\t\n", - "205\t174\t16\t1\t0\t0\t\n", - "202\t169\t16\t1\t0\t0\t\n", - "181\t152\t13\t1\t0\t0\t\n", - "185\t158\t12\t1\t0\t0\t\n", - "205\t175\t16\t1\t0\t0\t\n", - "204\t174\t16\t1\t0\t0\t\n", - "188\t158\t13\t1\t0\t0\t\n", - "193\t163\t13\t1\t0\t0\t\n", - "177\t146\t16\t0\t0\t0\t\n", - "187\t160\t17\t0\t0\t0\t\n", - "193\t168\t20\t0\t0\t0\t\n", - "188\t165\t21\t0\t0\t0\t\n", - "180\t155\t20\t0\t0\t0\t\n", - "167\t145\t20\t0\t0\t0\t\n", - "147\t130\t16\t0\t0\t0\t\n", - "153\t132\t16\t0\t0\t0\t\n", - "160\t142\t21\t0\t0\t0\t\n", - "153\t137\t21\t0\t0\t0\t\n", - "157\t139\t18\t0\t0\t0\t\n", - "151\t133\t16\t0\t0\t0\t\n", - "154\t134\t13\t0\t0\t0\t\n", - "153\t129\t16\t1\t0\t0\t\n", - "173\t147\t17\t1\t0\t0\t\n", - "175\t149\t22\t1\t0\t0\t\n", - "179\t149\t25\t1\t0\t0\t\n", - "174\t148\t25\t1\t0\t0\t\n", - "178\t149\t22\t1\t0\t0\t\n", - "183\t154\t22\t1\t0\t0\t\n", - "193\t166\t22\t1\t0\t0\t\n", - "190\t163\t22\t1\t0\t0\t\n", - "183\t159\t23\t2\t0\t0\t\n", - "188\t169\t21\t2\t0\t0\t\n", - "170\t155\t22\t2\t0\t0\t\n", - "177\t155\t18\t2\t0\t0\t\n", - "166\t150\t15\t2\t0\t0\t\n", - "168\t152\t16\t3\t0\t0\t\n", - "155\t139\t14\t3\t0\t0\t\n", - "160\t142\t18\t3\t0\t0\t\n", - "158\t133\t21\t3\t0\t0\t\n", - "169\t144\t21\t3\t0\t0\t\n", - "167\t137\t21\t2\t0\t0\t\n", - "148\t118\t19\t1\t0\t0\t\n", - "136\t107\t17\t1\t0\t0\t\n", - "115\t93\t16\t1\t0\t0\t\n", - "116\t90\t15\t1\t0\t0\t\n", - "100\t75\t14\t1\t0\t0\t\n", - "91\t69\t14\t1\t0\t0\t\n", - "71\t51\t9\t1\t0\t0\t\n", - "50\t37\t8\t1\t0\t0\t\n", - "31\t19\t7\t1\t0\t0\t\n", - "18\t11\t5\t1\t0\t0\t\n", - "18\t11\t5\t1\t0\t0\t\n", - "18\t11\t5\t1\t0\t0\t\n", - "18\t11\t5\t1\t0\t0\t\n", - "11\t9\t5\t1\t0\t0\t\n", - "12\t9\t4\t0\t0\t0\t\n", - "13\t9\t3\t0\t0\t0\t\n", - "10\t7\t3\t0\t0\t0\t\n", - "20\t13\t2\t0\t0\t0\t\n", - "37\t27\t5\t0\t0\t0\t\n", - "59\t44\t6\t0\t0\t0\t\n", - "81\t63\t9\t0\t0\t0\t\n", - "105\t80\t14\t0\t0\t0\t\n", - "110\t85\t15\t0\t0\t0\t\n", - "127\t97\t16\t0\t0\t0\t\n", - "141\t112\t23\t0\t0\t0\t\n", - "160\t125\t28\t1\t0\t0\t\n", - "173\t137\t29\t1\t0\t0\t\n", - "182\t148\t31\t2\t0\t0\t\n", - "177\t146\t30\t2\t0\t0\t\n", - "169\t143\t30\t2\t0\t0\t\n", - "163\t137\t28\t2\t0\t0\t\n", - "147\t123\t25\t2\t0\t0\t\n", - "166\t141\t26\t2\t0\t0\t\n", - "170\t150\t25\t2\t0\t0\t\n", - "180\t159\t21\t2\t0\t0\t\n", - "176\t159\t18\t1\t0\t0\t\n", - "189\t170\t21\t1\t0\t0\t\n", - "186\t163\t19\t0\t0\t0\t\n", - "193\t165\t19\t0\t0\t0\t\n", - "202\t173\t20\t1\t0\t0\t\n", - "208\t177\t21\t1\t0\t0\t\n", - "211\t182\t20\t1\t0\t0\t\n", - "202\t167\t25\t1\t0\t0\t\n", - "201\t161\t30\t1\t0\t0\t\n", - "195\t151\t30\t1\t0\t0\t\n", - "196\t152\t29\t1\t0\t0\t\n", - "185\t143\t26\t1\t0\t0\t\n", - "181\t143\t25\t1\t0\t0\t\n", - "176\t142\t28\t1\t0\t0\t\n", - "178\t137\t33\t0\t0\t0\t\n", - "178\t139\t34\t0\t0\t0\t\n", - "178\t137\t35\t0\t0\t0\t\n", - "171\t136\t30\t0\t0\t0\t\n", - "186\t148\t28\t1\t0\t0\t\n", - "178\t145\t27\t1\t0\t0\t\n", - "187\t154\t28\t2\t0\t0\t\n", - "196\t157\t30\t3\t1\t0\t\n", - "203\t166\t31\t3\t1\t0\t\n", - "201\t165\t28\t4\t1\t0\t\n", - "191\t163\t25\t4\t1\t0\t\n", - "185\t159\t23\t4\t1\t0\t\n", - "194\t168\t23\t4\t1\t0\t\n", - "201\t171\t25\t4\t1\t0\t\n", - "188\t163\t26\t3\t1\t0\t\n", - "204\t171\t25\t3\t1\t0\t\n", - "192\t162\t24\t2\t1\t0\t\n", - "178\t154\t22\t1\t0\t0\t\n", - "178\t152\t23\t1\t0\t0\t\n", - "189\t160\t24\t0\t0\t0\t\n", - "195\t163\t21\t0\t0\t0\t\n", - "204\t172\t20\t0\t0\t0\t\n", - "203\t168\t18\t0\t0\t0\t\n", - "209\t176\t16\t0\t0\t0\t\n", - "199\t164\t13\t0\t0\t0\t\n", - "194\t167\t18\t1\t0\t0\t\n", - "204\t177\t19\t1\t0\t0\t\n", - "211\t182\t22\t1\t0\t0\t\n", - "201\t175\t21\t1\t0\t0\t\n", - "200\t172\t19\t1\t0\t0\t\n", - "196\t168\t21\t1\t0\t0\t\n", - "185\t157\t22\t1\t0\t0\t\n", - "175\t154\t25\t2\t0\t0\t\n", - "173\t155\t25\t2\t0\t0\t\n", - "183\t167\t26\t2\t0\t0\t\n", - "176\t155\t22\t2\t0\t0\t\n", - "168\t137\t21\t2\t0\t0\t\n", - "167\t136\t20\t2\t0\t0\t\n", - "169\t137\t18\t2\t0\t0\t\n", - "162\t134\t22\t2\t0\t0\t\n", - "154\t128\t21\t2\t0\t0\t\n", - "169\t137\t21\t2\t0\t0\t\n", - "179\t141\t20\t1\t0\t0\t\n", - "178\t140\t19\t1\t0\t0\t\n", - "171\t133\t19\t2\t0\t0\t\n", - "185\t144\t22\t2\t0\t0\t\n", - "179\t147\t25\t2\t0\t0\t\n", - "175\t139\t22\t2\t0\t0\t\n", - "172\t134\t24\t2\t0\t0\t\n", - "184\t140\t21\t2\t0\t0\t\n", - "193\t149\t20\t2\t0\t0\t\n", - "179\t139\t19\t2\t0\t0\t\n", - "176\t142\t17\t2\t0\t0\t\n", - "168\t131\t17\t2\t0\t0\t\n", - "173\t138\t15\t1\t0\t0\t\n", - "152\t122\t11\t0\t0\t0\t\n", - "160\t124\t9\t0\t0\t0\t\n", - "161\t129\t13\t0\t0\t0\t\n", - "171\t134\t12\t0\t0\t0\t\n", - "163\t130\t14\t0\t0\t0\t\n", - "166\t131\t17\t0\t0\t0\t\n", - "166\t133\t21\t0\t0\t0\t\n", - "164\t129\t22\t0\t0\t0\t\n", - "170\t138\t22\t0\t0\t0\t\n", - "164\t132\t24\t0\t0\t0\t\n", - "175\t141\t27\t0\t0\t0\t\n", - "172\t144\t32\t0\t0\t0\t\n", - "182\t152\t32\t1\t0\t0\t\n", - "177\t154\t32\t1\t0\t0\t\n", - "169\t148\t30\t1\t0\t0\t\n", - "171\t150\t28\t1\t0\t0\t\n", - "172\t147\t26\t1\t0\t0\t\n", - "179\t150\t26\t1\t0\t0\t\n", - "174\t145\t25\t1\t0\t0\t\n", - "178\t150\t28\t1\t0\t0\t\n", - "174\t150\t28\t1\t0\t0\t\n", - "174\t146\t21\t1\t0\t0\t\n", - "179\t148\t24\t0\t0\t0\t\n", - "183\t148\t24\t1\t0\t0\t\n", - "182\t147\t23\t1\t0\t0\t\n", - "182\t145\t25\t1\t0\t0\t\n", - "189\t154\t28\t2\t0\t0\t\n", - "190\t155\t28\t2\t0\t0\t\n", - "200\t162\t29\t2\t0\t0\t\n", - "195\t156\t28\t2\t0\t0\t\n", - "201\t158\t29\t2\t0\t0\t\n", - "198\t159\t33\t2\t0\t0\t\n", - "176\t144\t28\t2\t0\t0\t\n", - "183\t156\t27\t1\t0\t0\t\n", - "191\t162\t27\t1\t0\t0\t\n", - "189\t162\t25\t1\t0\t0\t\n", - "179\t154\t24\t0\t0\t0\t\n" - ] - } - ], - "source": [ - "with serial.Serial(\"COM10\", baudrate=9600) as ser:\n", - " _line = bytearray([])\n", - " _line_counter = 0\n", - " _line_start = False\n", - " while True:\n", - " if ser.in_waiting > 0:\n", - " c = ser.read()\n", - " if c == bytes([0x42]):\n", - " _line_start = True\n", - " _line = bytearray([0x42])\n", - " _line_counter += 1\n", - " else:\n", - " if _line_start:\n", - " _line.append(c[0])\n", - " _line_counter += 1\n", - " \n", - " if _line_counter == 32:\n", - " _line_counter = 0\n", - " _line_start = False\n", - " for n in range(6):\n", - " print(_line[16 + n * 2] * 256 + _line[17 + n * 2], end = '\\t')\n", - " print()\n", - " " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f17821b6", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "230\n", - "185\n", - "34\n", - "1\n", - "0\n", - "0\n" - ] - } - ], - "source": [ - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "2bfa83eb", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'M'" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c.decode()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.vscode/examples/pm25.txt b/.vscode/examples/pm25.txt deleted file mode 100644 index 48b8d20..0000000 --- a/.vscode/examples/pm25.txt +++ /dev/null @@ -1,24 +0,0 @@ -import serial - -with serial.Serial("COM15", baudrate=9600) as ser: - _line = bytearray([]) - _line_counter = 0 - _line_start = False - while True: - if ser.in_waiting > 0: - c = ser.read() - if c == bytes([0x42]): - _line_start = True - _line = bytearray([0x42]) - _line_counter = 1 - else: - if _line_start: - _line.append(c[0]) - _line_counter += 1 - - if _line_counter == 32: - _line_counter = 0 - _line_start = False - for n in range(6): - print(_line[16 + n * 2] * 256 + _line[17 + n * 2], end = '\t') - print() \ No newline at end of file diff --git a/.vscode/examples/pyAMBYTE.ipynb b/.vscode/examples/pyAMBYTE.ipynb deleted file mode 100644 index a3ede11..0000000 --- a/.vscode/examples/pyAMBYTE.ipynb +++ /dev/null @@ -1,679 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import serial\n", - "import numpy as np\n", - "import time\n", - "import matplotlib.pyplot as plt\n", - "from IPython.display import clear_output, display\n", - "\n", - "plt.style.use('dark_background')\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "with serial.Serial(\"COM6\", 115200, rtscts=False) as ser:\n", - " ser.write(b'hello\\n')\n", - " l = ser.readline().decode()\n", - " print(l)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def wait_for_control(ser, n, timeout = .1):\n", - " start_timer = time.time()\n", - " while (time.time() - start_timer < timeout):\n", - " if ser.in_waiting > 0:\n", - " b = ser.read()\n", - " if b[0] < 127:\n", - " print(b.decode(), end = \"\")\n", - " if n < 256:\n", - " if b == bytes([n]):\n", - " return 1\n", - " else:\n", - " print(int.from_bytes(b))\n", - " if n < 256:\n", - " if b == bytes([n]):\n", - " return 1\n", - " return -1\n", - "\n", - "def data_length_capture(ser, timeout = 1):\n", - " start_timer = time.time()\n", - " while (time.time() - start_timer < timeout):\n", - " if ser.in_waiting > 0:\n", - " b = ser.read()\n", - " if b[0] < 127:\n", - " print(b.decode(), end = \"\")\n", - " else:\n", - " if b == bytes([150]):\n", - " t = int.from_bytes(ser.read())\n", - " l1 = int.from_bytes(ser.read())\n", - " l2 = int.from_bytes(ser.read())\n", - " ser.read()\n", - " ser.read()\n", - " return l1 * 256 + l2\n", - " return 0\n", - "\n", - "\n", - "def data_capture(ser, len, timeout = 2):\n", - " start_timer = time.time()\n", - " counter = 0\n", - " while (time.time() - start_timer < timeout):\n", - " if ser.in_waiting > 0:\n", - " b = ser.read()\n", - " if b == bytes([151]):\n", - " b = ser.read(3) \n", - " break\n", - " else:\n", - " if b[0] < 127:\n", - " print(b.decode(), end = \"\")\n", - " \n", - " while (time.time() - start_timer < timeout):\n", - " b = ser.read(4)\n", - " print(int.from_bytes(b, \"little\"))\n", - " counter += 1 \n", - " if counter == len: break\n", - "\n", - " \n", - " return 0" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "info_outputs = \"\"\n", - "other_outputs = \"\"\n", - "with serial.Serial(\"COM6\", 115200) as ser:\n", - " start_timer = time.time()\n", - " ser.write(\"S,\".encode())\n", - "\n", - " print(wait_for_control(ser, 212))\n", - " ser.write(bytes([202]))\n", - " data_size = data_length_capture(ser)\n", - " print(\"data size:\", data_size)\n", - " \n", - " ser.write(bytes([200]))\n", - " data_capture(ser, data_size)\n", - "\n", - " print(wait_for_control(ser, 212))\n", - " ser.write(bytes([202]))\n", - " data_size = data_length_capture(ser)\n", - " print(\"data size:\", data_size)\n", - " \n", - " ser.write(bytes([200]))\n", - " data_capture(ser, data_size)\n", - "\n", - " print(wait_for_control(ser, 212))\n", - " ser.write(bytes([202]))\n", - " data_size = data_length_capture(ser)\n", - " print(\"data size:\", data_size)\n", - " \n", - " ser.write(bytes([200]))\n", - " data_capture(ser, data_size)\n", - "\n", - "\n", - "\n", - " wait_for_control(ser, 999, 2)\n", - "\n", - " " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "1000*4*8/115200" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "info_outputs = \"\"\n", - "other_outputs = \"\"\n", - "with serial.Serial(\"COM6\", 115200) as ser:\n", - " start_timer = time.time()\n", - "\n", - " ser.write(\"S,\".encode())\n", - " \n", - " while (time.time() - start_timer < 1):\n", - " if ser.in_waiting > 1:\n", - " start_timer = time.time()\n", - " l = ser.readline().decode().strip()\n", - " if l[0] == '[':\n", - " info_outputs += l + \"\\n\"\n", - " elif l[:6] == \"Binary\":\n", - " while ser.in_waiting > 0:\n", - " l = ser.read(4)\n", - " print(int.from_bytes(l, 'little'))\n", - "\n", - " else:\n", - " other_outputs += l + \"\\n\"\n", - " else:\n", - " time.sleep(.5)\n", - " \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "int.from_bytes(l, 'little')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cmd = '''\n", - "set_currents(60, 30, 50);\n", - "set_gains(2, 5, 5, 1, 5, 5);\n", - "config();\n", - "\n", - "\n", - "actinic = 100;\n", - "\n", - "\n", - "set_arr(1,1,0,1,50,50,0,2);\n", - "\n", - "set_arr(1,2,0,1,250,100,actinic,2);\n", - "set_arr(2,1,0,1,450,100,actinic,2);\n", - "set_arr(3,1,0,1,50,10,actinic,2);\n", - "set_arr(3,2,0,1,450,100,0,2);\n", - "set_arr(4,1,0,1,50,10,0,2);\n", - "\n", - "\n", - "run_mpf(0, 0);\n", - "run(1,1);\n", - "run_mpf(1, actinic);\n", - "for(i=0;i<4;i++){\n", - "run(2, 1);\n", - "run_mpf(1, actinic);\n", - "}\n", - "\n", - "run(3,0);\n", - "run_mpf(1,0);\n", - "for(i=0;i<4;i++){ \n", - "set_arr(4,1,0,1,50+i*10,5,0,2);\n", - "run(4,0);\n", - "run_mpf(1,0);\n", - "}\n", - "\n", - "\n", - "'''\n", - "cmd_str = \"C:\" + cmd + \"?\"\n", - "cmd_str" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cmd = '''\n", - "set_currents(110, 30, 50);\n", - "set_gains(1, 5, 5, 1, 5, 5);\n", - "config();\n", - "\n", - "\n", - "actinic = 0;\n", - "\n", - "\n", - "set_arr(1,1,0,1,50,25,0,2);\n", - "set_arr(1,2,0,1,50,25,0,2);\n", - "\n", - "run_mpf(0, 0);\n", - "run(1,0);\n", - "\n", - "'''\n", - "\n", - "cmd_str = \"C:\" + cmd + \"?\"\n", - "cmd_str" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "info_outputs = \"\"\n", - "other_outputs = \"\"\n", - "with serial.Serial(\"COM31\", 115200) as ser:\n", - " start_timer = time.time()\n", - " #ser.write(\"mpf, 0, 100, 100\".encode())\n", - " #ser.write(\"arrun, 500, 500, 0, 0, 0\".encode())\n", - " ser.write(cmd_str.encode())\n", - " \n", - " data = {}\n", - "\n", - " while (time.time() - start_timer < 3000):\n", - " if ser.in_waiting > 2:\n", - " start_timer = time.time()\n", - " l = ser.readline().decode().strip()\n", - " if l[0] == '[':\n", - " info_outputs += l + \"\\n\"\n", - "\n", - "\n", - " elif l[:5] == 'Data:':\n", - " _datainfo = l.split(\"\\t\")[0]\n", - " _datatype, _datalength = _datainfo.split(\",\")\n", - " datatype = _datatype.split(\":\")[1]\n", - " datalength = int(_datalength.split(\":\")[1])\n", - "\n", - " if datalength > 0:\n", - " _data = l.split(\"\\t\")[1]\n", - " if datatype in data:\n", - " data[datatype] += np.fromstring(_data, sep = ',', dtype=int).tolist()\n", - " else:\n", - " data.update({datatype: np.fromstring(_data, sep = ',', dtype=int).tolist()})\n", - " \n", - "\n", - " elif l == \"Cmd Done!\":\n", - " break\n", - " else:\n", - " other_outputs += l + \"\\n\"\n", - " else:\n", - " time.sleep(.5)\n", - " \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(info_outputs, other_outputs)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x, y = np.array(data['Fluo']), np.array(data['Fluoref'])\n", - "plt.plot(x/y)\n", - "\n", - "# if \"730\" in data:\n", - "# plt.twinx()\n", - "# plt.plot(data['730'] / data['730ref'], 'm')\n", - "# plt.twinx()\n", - "# plt.plot(data['SUN'], 'y')\n", - "# plt.twinx()\n", - "# plt.plot(data['leaf'], 'r')\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "sig = arr[1:, 1] - 16384\n", - "ref = arr[1:, 0] - 16384\n", - "plt.plot(ref, sig-ref - ref * 0.01)\n", - "plt.plot(ref, sig-ref - ref * 0.01 - 5 * np.sin(ref/3.14/26))\n", - "plt.grid(c = [.2, .2, .2, .2])\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "y[3000]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "info_outputs = \"\"\n", - "other_outputs = \"\"\n", - "with serial.Serial(\"COM31\", 115200) as ser:\n", - " start_timer = time.time()\n", - " #ser.write(\"mpf, 0, 100, 100\".encode())\n", - " #ser.write(\"arrun, 500, 500, 0, 0, 0\".encode())\n", - " ser.write(\"sd\\n\".encode())\n", - " \n", - " data = []\n", - "\n", - " while (time.time() - start_timer < 40):\n", - " if ser.in_waiting > 2:\n", - " start_timer = time.time()\n", - " l = ser.readline().decode().strip()\n", - " if l[0] == '[':\n", - " info_outputs += l + \"\\n\"\n", - "\n", - "\n", - " elif l[:5] == 'Data:':\n", - " data.append(np.fromstring(l[5:], sep = ',', dtype=int))\n", - " \n", - " \n", - "\n", - " elif l == \"Cmd Done!\":\n", - " break\n", - " else:\n", - " other_outputs += l + \"\\n\"\n", - " else:\n", - " time.sleep(.5)\n", - " \n", - "arr = np.array(data)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "n = 1.006\n", - "sig1 = arr[1:, 1] - arr[1:, 0] - (arr[1:, 0] - 16384) * 0.006\n", - "ref1= arr[1:, 3] - arr[1:, 2] - (arr[1:, 2] - 16384) * 0.006\n", - "\n", - "sig2 = arr[1:, 5] - arr[1:, 4] - (arr[1:, 4] - 16384 * 2) * 0.003\n", - "ref2= arr[1:, 7] - arr[1:, 6] - (arr[1:, 6] - 16384 * 2) * 0.003\n", - "\n", - "\n", - "sig3 = arr[1:, 9] - arr[1:, 8] - (arr[1:, 8] - 16384 * 3) * 0.0015\n", - "ref3= arr[1:, 11] - arr[1:, 10] - (arr[1:, 10] - 16384 * 3) * 0.0015\n", - "\n", - "sig4 = arr[1:, 13] - arr[1:, 12] - (arr[1:, 12] - 16384 * 4) * 0.0008\n", - "ref4= arr[1:, 15] - arr[1:, 14] - (arr[1:, 14] - 16384 * 4) * 0.0008\n", - "\n", - "plt.plot(sig1 / ref1 * 1.025 + .1)\n", - "plt.plot(sig2 / ref2)\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.plot((sig1 * 1) / ref1 , ((sig4 * 1) / ref4) - 1.05* (sig1 * 1) / ref1 - .2)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "(sig2.mean() - 60) / sig1.mean()/2, (sig3.mean() - 120) / sig1.mean()/3, (sig4.mean() - 120) / sig1.mean()/4, " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = sig1 / ref1\n", - "y = (sig2 - 50) / (ref2)\n", - "\n", - "y2 = (sig4 - 240) / (ref4)\n", - "plt.plot(x, y - 1.0 * x)\n", - "plt.plot(x, y2 - 1.0 * x)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "np.polyfit(sig1, sig2, deg = 1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.plot(sig1, sig2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "(sig2/sig1).mean(), (ref2/ref1).mean()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "tl = []\n", - "\n", - "for x in range(20):\n", - " tl += [x * 500000]\n", - " \n", - "_z = tl[-1] + 1500\n", - "for x in range(4):\n", - " tl += (np.arange(12) * 120 + x * 1500 + _z).tolist()\n", - "\n", - "_z = tl[-1] + 1500\n", - "for x in range(200):\n", - " tl += [x * 1500 + _z]\n", - "\n", - "\n", - "_z = tl[-1] + 1500\n", - "for _ in range(8):\n", - " _z = tl[-1] + 1500\n", - " for x in range(20):\n", - " tl += [x * 1200 + _z]\n", - "\n", - "_z = tl[-1] + 1500\n", - "for x in range(50):\n", - " tl += [x * 1500 * 4 + _z]\n", - "\n", - "\n", - "_z = tl[-1] + 1500\n", - "inv = 1000\n", - "for x in range(40):\n", - " tl += [x * 1500 * 4 + x * inv + _z]\n", - " inv += 5000" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.plot(tl[:], plot_arr[:, 0] / plot_arr[:, 1], '-')\n", - "#plt.xscale('log')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.plot((np.array(tl[-50:]) - tl[-50])/1e6, plot_arr[-50:, 0] / plot_arr[-50:, 1], '.-')\n", - "#plt.xscale('log')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(outputs)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "def line_parse(l:str)->str:\n", - " l = l.strip()\n", - " if len(l) < 1:return \"\"\n", - " if l[0] == '[':\n", - " print(l)\n", - " return \"\"\n", - " else:\n", - " return l.strip()\n", - "\n", - "with serial.Serial(\"COM4\", 115200, timeout = 5) as ser:\n", - "\n", - "\n", - " ser.write(cmd_str.encode())\n", - " data_ready = False\n", - " checked = False\n", - " ts = time.time()\n", - " while time.time() - ts < 1:\n", - " if ser.in_waiting > 0:\n", - " l = line_parse(ser.readline().decode())\n", - " if l == \"Wake!\":\n", - " ser.write(\"Ready\".encode())\n", - " data_ready = True\n", - " elif data_ready and l.isdigit():\n", - " ts = time.time()\n", - " data_len = int(l)\n", - " ser.write(\"GO\".encode())\n", - " a1 = ser.read(4 * (data_len + 1))\n", - " _a = np.frombuffer(a1, dtype=\">i4\")\n", - " if (_a[:-1].sum() == _a[-1]):\n", - " checked = True\n", - " elif l == \"DONE\":\n", - " ser.write(\"Check\".encode())\n", - " data_ready = False\n", - " print(_a)\n", - "\n", - " elif l == \"Finish\":\n", - " print(l)\n", - " break\n", - " else:\n", - " print(l)\n", - "\n", - " " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "_a[:-1].sum()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "_a" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.7" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/.vscode/examples/sender.ipynb b/.vscode/examples/sender.ipynb deleted file mode 100644 index eb11bab..0000000 --- a/.vscode/examples/sender.ipynb +++ /dev/null @@ -1,1893 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import serial\n", - "import numpy as np\n", - "import time\n", - "import matplotlib.pyplot as plt\n", - "from IPython.display import clear_output, display\n", - "\n", - "plt.style.use('dark_background')\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "NEW Name Here Ready\n", - "\n" - ] - } - ], - "source": [ - "with serial.Serial(\"COM31\", 115200, rtscts=False) as ser:\n", - " ser.write(b'hello\\n')\n", - " l = ser.readline().decode()\n", - " print(l)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 408, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'C:\\nset_currents(60, 30, 50);\\nset_gains(2, 5, 5, 1, 5, 5);\\nconfig();\\n\\n\\nactinic = 100;\\n\\n\\nset_arr(1,1,0,1,50,50,0,2);\\n\\nset_arr(1,2,0,1,250,100,actinic,2);\\nset_arr(2,1,0,1,450,100,actinic,2);\\nset_arr(3,1,0,1,50,10,actinic,2);\\nset_arr(3,2,0,1,450,100,0,2);\\nset_arr(4,1,0,1,50,10,0,2);\\n\\n\\nrun_mpf(0, 0);\\nrun(1,1);\\nrun_mpf(1, actinic);\\nfor(i=0;i<4;i++){\\nrun(2, 1);\\nrun_mpf(1, actinic);\\n}\\n\\nrun(3,0);\\nrun_mpf(1,0);\\nfor(i=0;i<4;i++){ \\nset_arr(4,1,0,1,50+i*10,5,0,2);\\nrun(4,0);\\nrun_mpf(1,0);\\n}\\n\\n\\n?'" - ] - }, - "execution_count": 408, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "cmd = '''\n", - "set_currents(60, 30, 50);\n", - "set_gains(2, 5, 5, 1, 5, 5);\n", - "config();\n", - "\n", - "\n", - "actinic = 100;\n", - "\n", - "\n", - "set_arr(1,1,0,1,50,50,0,2);\n", - "\n", - "set_arr(1,2,0,1,250,100,actinic,2);\n", - "set_arr(2,1,0,1,450,100,actinic,2);\n", - "set_arr(3,1,0,1,50,10,actinic,2);\n", - "set_arr(3,2,0,1,450,100,0,2);\n", - "set_arr(4,1,0,1,50,10,0,2);\n", - "\n", - "\n", - "run_mpf(0, 0);\n", - "run(1,1);\n", - "run_mpf(1, actinic);\n", - "for(i=0;i<4;i++){\n", - "run(2, 1);\n", - "run_mpf(1, actinic);\n", - "}\n", - "\n", - "run(3,0);\n", - "run_mpf(1,0);\n", - "for(i=0;i<4;i++){ \n", - "set_arr(4,1,0,1,50+i*10,5,0,2);\n", - "run(4,0);\n", - "run_mpf(1,0);\n", - "}\n", - "\n", - "\n", - "'''\n", - "cmd_str = \"C:\" + cmd + \"?\"\n", - "cmd_str" - ] - }, - { - "cell_type": "code", - "execution_count": 386, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'C:\\nset_currents(110, 30, 50);\\nset_gains(1, 5, 5, 1, 5, 5);\\nconfig();\\n\\n\\nactinic = 0;\\n\\n\\nset_arr(1,1,0,1,50,25,0,2);\\nset_arr(1,2,0,1,50,25,0,2);\\n\\nrun_mpf(0, 0);\\nrun(1,0);\\n\\n?'" - ] - }, - "execution_count": 386, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "cmd = '''\n", - "set_currents(110, 30, 50);\n", - "set_gains(1, 5, 5, 1, 5, 5);\n", - "config();\n", - "\n", - "\n", - "actinic = 0;\n", - "\n", - "\n", - "set_arr(1,1,0,1,50,25,0,2);\n", - "set_arr(1,2,0,1,50,25,0,2);\n", - "\n", - "run_mpf(0, 0);\n", - "run(1,0);\n", - "\n", - "'''\n", - "\n", - "cmd_str = \"C:\" + cmd + \"?\"\n", - "cmd_str" - ] - }, - { - "cell_type": "code", - "execution_count": 409, - "metadata": {}, - "outputs": [], - "source": [ - "info_outputs = \"\"\n", - "other_outputs = \"\"\n", - "with serial.Serial(\"COM31\", 115200) as ser:\n", - " start_timer = time.time()\n", - " #ser.write(\"mpf, 0, 100, 100\".encode())\n", - " #ser.write(\"arrun, 500, 500, 0, 0, 0\".encode())\n", - " ser.write(cmd_str.encode())\n", - " \n", - " data = {}\n", - "\n", - " while (time.time() - start_timer < 3000):\n", - " if ser.in_waiting > 2:\n", - " start_timer = time.time()\n", - " l = ser.readline().decode().strip()\n", - " if l[0] == '[':\n", - " info_outputs += l + \"\\n\"\n", - "\n", - "\n", - " elif l[:5] == 'Data:':\n", - " _datainfo = l.split(\"\\t\")[0]\n", - " _datatype, _datalength = _datainfo.split(\",\")\n", - " datatype = _datatype.split(\":\")[1]\n", - " datalength = int(_datalength.split(\":\")[1])\n", - "\n", - " if datalength > 0:\n", - " _data = l.split(\"\\t\")[1]\n", - " if datatype in data:\n", - " data[datatype] += np.fromstring(_data, sep = ',', dtype=int).tolist()\n", - " else:\n", - " data.update({datatype: np.fromstring(_data, sep = ',', dtype=int).tolist()})\n", - " \n", - "\n", - " elif l == \"Cmd Done!\":\n", - " break\n", - " else:\n", - " other_outputs += l + \"\\n\"\n", - " else:\n", - " time.sleep(.5)\n", - " \n" - ] - }, - { - "cell_type": "code", - "execution_count": 410, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[234683][V][do_c.cpp:160] set_currents(): [DO_C] set pulsed-620 to: 60, set pulsed-720 to: 30, set far_red to: 50\n", - "[234693][V][do_c.cpp:172] set_gains(): [DO_C] Fluor Gain:2, Fluo ref Gain:5, 730 Gain:5, 730ref Gain:1, Sun Gain:5, Leaf Gain:5\n", - "[234705][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[234717][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[234728][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[234739][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[234749][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[234759][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[234769][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[234780][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[234790][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n", - "[234800][V][do_c.cpp:147] detector_preset(): [DO_C] ADPD detector preset with stored values\n", - "[234808][V][do_c.cpp:47] arr_set(): [DO_C] Set arr0, line 0 to type 1 with 50 x 50Hz samples, actinic:0, sub:2, IR:0\n", - "[234819][V][do_c.cpp:47] arr_set(): [DO_C] Set arr0, line 1 to type 1 with 250 x 100Hz samples, actinic:100, sub:2, IR:0\n", - "[234829][V][do_c.cpp:47] arr_set(): [DO_C] Set arr1, line 0 to type 1 with 450 x 100Hz samples, actinic:100, sub:2, IR:0\n", - "[234840][V][do_c.cpp:47] arr_set(): [DO_C] Set arr2, line 0 to type 1 with 50 x 10Hz samples, actinic:100, sub:2, IR:0\n", - "[234851][V][do_c.cpp:47] arr_set(): [DO_C] Set arr2, line 1 to type 1 with 450 x 100Hz samples, actinic:0, sub:2, IR:0\n", - "[234861][V][do_c.cpp:47] arr_set(): [DO_C] Set arr3, line 0 to type 1 with 50 x 10Hz samples, actinic:0, sub:2, IR:0\n", - "[234872][V][PAM.cpp:298] MPF(): [PAM] RUN MPF with mode:0, pulse current:60, DC current 0\n", - "[234880][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[234886][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[234893][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[234900][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[234906][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[234912][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[234918][V][PAM.cpp:303] MPF(): [PAM] Memory allocation completed\n", - "[234927][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[234938][V][PAM.cpp:341] MPF(): [PAM] Phase-0 Started\n", - "[244943][V][PAM.cpp:352] MPF(): [PAM] Phase-0 Completed\n", - "[244948][V][PAM.cpp:359] MPF(): [PAM] Phase-1 Config\n", - "[244953][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[244961][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 1\n", - "[244970][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 2\n", - "[244978][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 3\n", - "[244987][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 4\n", - "[244996][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 5\n", - "[245004][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 6\n", - "[245013][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 7\n", - "[245022][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 8\n", - "[245030][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 9\n", - "[245039][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 10\n", - "[245048][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 11\n", - "[245061][V][PAM.cpp:369] MPF(): [PAM] Phase-1 LED ON\n", - "[245075][V][PAM.cpp:386] MPF(): [PAM] Phase-1 Completed; Phase-2 start\n", - "[245384][V][PAM.cpp:414] MPF(): [PAM] Phase-2 Completed; Phase-3 Start\n", - "[245391][V][PAM.cpp:421] MPF(): [PAM] LED set to 220\n", - "[245457][V][PAM.cpp:421] MPF(): [PAM] LED set to 190\n", - "[245523][V][PAM.cpp:421] MPF(): [PAM] LED set to 160\n", - "[245589][V][PAM.cpp:421] MPF(): [PAM] LED set to 130\n", - "[245655][V][PAM.cpp:421] MPF(): [PAM] LED set to 100\n", - "[245722][V][PAM.cpp:421] MPF(): [PAM] LED set to 70\n", - "[245788][V][PAM.cpp:421] MPF(): [PAM] LED set to 40\n", - "[245853][V][PAM.cpp:421] MPF(): [PAM] LED set to 10\n", - "[245919][V][PAM.cpp:444] MPF(): [PAM] Phase-3 Completed; Phase-4 Start\n", - "[245926][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[246236][V][PAM.cpp:484] MPF(): [PAM] Phase-4 Completed\n", - "[246241][V][PAM.cpp:488] MPF(): [PAM] Phase-minus1 Started\n", - "[262367][V][PAM.cpp:507] MPF(): [PAM] Measurement Completed\n", - "[262733][V][PAM.cpp:526] MPF(): [PAM] All Completed\n", - "[262742][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[262749][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[262754][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[262761][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[262767][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[262780][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[262791][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[262801][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[262811][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[262822][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[262832][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[262842][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[262852][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n", - "[262862][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:50, freq: 50, actinic: 0, subfactor 2\n", - "[262872][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:250, freq: 100, actinic: 100, subfactor 2\n", - "[262883][V][PAM.cpp:134] run_arr_type1(): [PAM] Sample & Ref: 300, optionals: 37\n", - "[262890][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[262897][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[262903][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[262910][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[262916][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[262923][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[262930][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 1200 bytes\n", - "[262936][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[262942][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 1200 bytes\n", - "[262949][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[262955][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 148 bytes\n", - "[262961][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[262967][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 148 bytes\n", - "[262974][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[262980][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 148 bytes\n", - "[262986][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[262992][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 148 bytes\n", - "[262998][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[263004][V][PAM.cpp:150] run_arr_type1(): [PAM] Memory allocation completed\n", - "[266279][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[266291][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[266297][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[266303][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[266309][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[266316][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[266322][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[266329][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[266335][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[266341][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[266347][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[266354][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[266360][V][PAM.cpp:298] MPF(): [PAM] RUN MPF with mode:1, pulse current:60, DC current 100\n", - "[266368][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[266375][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[266381][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[266388][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[266394][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[266401][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[266407][V][PAM.cpp:303] MPF(): [PAM] Memory allocation completed\n", - "[266413][V][PAM.cpp:359] MPF(): [PAM] Phase-1 Config\n", - "[266418][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[266426][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 1\n", - "[266435][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 2\n", - "[266444][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 3\n", - "[266452][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 4\n", - "[266461][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 5\n", - "[266470][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 6\n", - "[266478][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 7\n", - "[266487][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 8\n", - "[266496][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 9\n", - "[266504][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 10\n", - "[266513][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 11\n", - "[266527][V][PAM.cpp:369] MPF(): [PAM] Phase-1 LED ON\n", - "[266541][V][PAM.cpp:386] MPF(): [PAM] Phase-1 Completed; Phase-2 start\n", - "[266850][V][PAM.cpp:414] MPF(): [PAM] Phase-2 Completed; Phase-3 Start\n", - "[266857][V][PAM.cpp:421] MPF(): [PAM] LED set to 220\n", - "[266923][V][PAM.cpp:421] MPF(): [PAM] LED set to 190\n", - "[266990][V][PAM.cpp:421] MPF(): [PAM] LED set to 160\n", - "[267056][V][PAM.cpp:421] MPF(): [PAM] LED set to 130\n", - "[267122][V][PAM.cpp:421] MPF(): [PAM] LED set to 100\n", - "[267188][V][PAM.cpp:421] MPF(): [PAM] LED set to 70\n", - "[267254][V][PAM.cpp:421] MPF(): [PAM] LED set to 40\n", - "[267319][V][PAM.cpp:421] MPF(): [PAM] LED set to 10\n", - "[267385][V][PAM.cpp:444] MPF(): [PAM] Phase-3 Completed; Phase-4 Start\n", - "[267392][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[267701][V][PAM.cpp:484] MPF(): [PAM] Phase-4 Completed\n", - "[267706][V][PAM.cpp:507] MPF(): [PAM] Measurement Completed\n", - "[268029][V][PAM.cpp:526] MPF(): [PAM] All Completed\n", - "[268040][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[268047][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[268052][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[268059][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[268065][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[268078][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[268089][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[268099][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[268109][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[268120][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[268130][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[268140][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[268150][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n", - "[268160][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:450, freq: 100, actinic: 100, subfactor 2\n", - "[268171][V][PAM.cpp:134] run_arr_type1(): [PAM] Sample & Ref: 450, optionals: 56\n", - "[268178][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[268185][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[268191][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[268198][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[268204][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[268211][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[268218][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 1800 bytes\n", - "[268224][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[268230][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 1800 bytes\n", - "[268237][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[268243][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[268249][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[268255][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[268262][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[268268][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[268274][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[268280][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[268287][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[268293][V][PAM.cpp:150] run_arr_type1(): [PAM] Memory allocation completed\n", - "[272581][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[272590][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[272595][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[272602][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[272608][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[272615][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[272621][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[272628][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[272633][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[272640][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[272646][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[272653][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[272659][V][PAM.cpp:298] MPF(): [PAM] RUN MPF with mode:1, pulse current:60, DC current 100\n", - "[272667][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[272674][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[272680][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[272687][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[272693][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[272699][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[272705][V][PAM.cpp:303] MPF(): [PAM] Memory allocation completed\n", - "[272712][V][PAM.cpp:359] MPF(): [PAM] Phase-1 Config\n", - "[272716][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[272725][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 1\n", - "[272734][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 2\n", - "[272742][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 3\n", - "[272751][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 4\n", - "[272760][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 5\n", - "[272768][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 6\n", - "[272777][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 7\n", - "[272786][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 8\n", - "[272794][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 9\n", - "[272803][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 10\n", - "[272812][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 11\n", - "[272825][V][PAM.cpp:369] MPF(): [PAM] Phase-1 LED ON\n", - "[272839][V][PAM.cpp:386] MPF(): [PAM] Phase-1 Completed; Phase-2 start\n", - "[273148][V][PAM.cpp:414] MPF(): [PAM] Phase-2 Completed; Phase-3 Start\n", - "[273155][V][PAM.cpp:421] MPF(): [PAM] LED set to 220\n", - "[273221][V][PAM.cpp:421] MPF(): [PAM] LED set to 190\n", - "[273288][V][PAM.cpp:421] MPF(): [PAM] LED set to 160\n", - "[273354][V][PAM.cpp:421] MPF(): [PAM] LED set to 130\n", - "[273420][V][PAM.cpp:421] MPF(): [PAM] LED set to 100\n", - "[273486][V][PAM.cpp:421] MPF(): [PAM] LED set to 70\n", - "[273552][V][PAM.cpp:421] MPF(): [PAM] LED set to 40\n", - "[273618][V][PAM.cpp:421] MPF(): [PAM] LED set to 10\n", - "[273683][V][PAM.cpp:444] MPF(): [PAM] Phase-3 Completed; Phase-4 Start\n", - "[273690][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[273999][V][PAM.cpp:484] MPF(): [PAM] Phase-4 Completed\n", - "[274004][V][PAM.cpp:507] MPF(): [PAM] Measurement Completed\n", - "[274327][V][PAM.cpp:526] MPF(): [PAM] All Completed\n", - "[274338][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[274345][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[274350][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[274357][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[274363][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[274376][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[274387][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[274397][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[274407][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[274418][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[274428][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[274438][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[274448][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n", - "[274459][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:450, freq: 100, actinic: 100, subfactor 2\n", - "[274469][V][PAM.cpp:134] run_arr_type1(): [PAM] Sample & Ref: 450, optionals: 56\n", - "[274476][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[274483][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[274489][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[274496][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[274503][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[274509][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[274516][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 1800 bytes\n", - "[274522][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[274528][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 1800 bytes\n", - "[274535][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[274541][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[274547][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[274553][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[274560][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[274566][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[274572][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[274578][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[274585][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[274591][V][PAM.cpp:150] run_arr_type1(): [PAM] Memory allocation completed\n", - "[278879][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[278888][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[278893][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[278900][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[278906][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[278913][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[278919][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[278926][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[278931][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[278938][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[278944][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[278951][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[278957][V][PAM.cpp:298] MPF(): [PAM] RUN MPF with mode:1, pulse current:60, DC current 100\n", - "[278965][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[278972][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[278978][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[278985][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[278991][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[278997][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[279003][V][PAM.cpp:303] MPF(): [PAM] Memory allocation completed\n", - "[279010][V][PAM.cpp:359] MPF(): [PAM] Phase-1 Config\n", - "[279014][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[279023][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 1\n", - "[279032][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 2\n", - "[279040][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 3\n", - "[279049][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 4\n", - "[279058][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 5\n", - "[279066][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 6\n", - "[279075][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 7\n", - "[279084][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 8\n", - "[279092][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 9\n", - "[279101][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 10\n", - "[279110][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 11\n", - "[279123][V][PAM.cpp:369] MPF(): [PAM] Phase-1 LED ON\n", - "[279137][V][PAM.cpp:386] MPF(): [PAM] Phase-1 Completed; Phase-2 start\n", - "[279446][V][PAM.cpp:414] MPF(): [PAM] Phase-2 Completed; Phase-3 Start\n", - "[279453][V][PAM.cpp:421] MPF(): [PAM] LED set to 220\n", - "[279519][V][PAM.cpp:421] MPF(): [PAM] LED set to 190\n", - "[279585][V][PAM.cpp:421] MPF(): [PAM] LED set to 160\n", - "[279652][V][PAM.cpp:421] MPF(): [PAM] LED set to 130\n", - "[279718][V][PAM.cpp:421] MPF(): [PAM] LED set to 100\n", - "[279784][V][PAM.cpp:421] MPF(): [PAM] LED set to 70\n", - "[279850][V][PAM.cpp:421] MPF(): [PAM] LED set to 40\n", - "[279915][V][PAM.cpp:421] MPF(): [PAM] LED set to 10\n", - "[279981][V][PAM.cpp:444] MPF(): [PAM] Phase-3 Completed; Phase-4 Start\n", - "[279988][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[280297][V][PAM.cpp:484] MPF(): [PAM] Phase-4 Completed\n", - "[280302][V][PAM.cpp:507] MPF(): [PAM] Measurement Completed\n", - "[280625][V][PAM.cpp:526] MPF(): [PAM] All Completed\n", - "[280636][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[280643][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[280648][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[280655][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[280661][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[280674][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[280685][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[280695][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[280705][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[280716][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[280726][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[280736][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[280746][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n", - "[280756][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:450, freq: 100, actinic: 100, subfactor 2\n", - "[280767][V][PAM.cpp:134] run_arr_type1(): [PAM] Sample & Ref: 450, optionals: 56\n", - "[280774][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[280781][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[280787][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[280794][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[280800][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[280807][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[280814][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 1800 bytes\n", - "[280820][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[280826][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 1800 bytes\n", - "[280833][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[280839][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[280845][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[280851][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[280858][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[280864][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[280870][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[280876][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[280883][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[280889][V][PAM.cpp:150] run_arr_type1(): [PAM] Memory allocation completed\n", - "[285177][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[285186][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[285191][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[285198][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[285204][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[285211][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[285217][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[285224][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[285229][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[285236][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[285242][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[285249][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[285255][V][PAM.cpp:298] MPF(): [PAM] RUN MPF with mode:1, pulse current:60, DC current 100\n", - "[285263][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[285270][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[285276][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[285283][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[285289][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[285295][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[285301][V][PAM.cpp:303] MPF(): [PAM] Memory allocation completed\n", - "[285308][V][PAM.cpp:359] MPF(): [PAM] Phase-1 Config\n", - "[285312][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[285321][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 1\n", - "[285330][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 2\n", - "[285338][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 3\n", - "[285347][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 4\n", - "[285356][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 5\n", - "[285364][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 6\n", - "[285373][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 7\n", - "[285382][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 8\n", - "[285390][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 9\n", - "[285399][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 10\n", - "[285408][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 11\n", - "[285421][V][PAM.cpp:369] MPF(): [PAM] Phase-1 LED ON\n", - "[285435][V][PAM.cpp:386] MPF(): [PAM] Phase-1 Completed; Phase-2 start\n", - "[285744][V][PAM.cpp:414] MPF(): [PAM] Phase-2 Completed; Phase-3 Start\n", - "[285751][V][PAM.cpp:421] MPF(): [PAM] LED set to 220\n", - "[285817][V][PAM.cpp:421] MPF(): [PAM] LED set to 190\n", - "[285883][V][PAM.cpp:421] MPF(): [PAM] LED set to 160\n", - "[285950][V][PAM.cpp:421] MPF(): [PAM] LED set to 130\n", - "[286016][V][PAM.cpp:421] MPF(): [PAM] LED set to 100\n", - "[286082][V][PAM.cpp:421] MPF(): [PAM] LED set to 70\n", - "[286148][V][PAM.cpp:421] MPF(): [PAM] LED set to 40\n", - "[286213][V][PAM.cpp:421] MPF(): [PAM] LED set to 10\n", - "[286279][V][PAM.cpp:444] MPF(): [PAM] Phase-3 Completed; Phase-4 Start\n", - "[286286][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[286595][V][PAM.cpp:484] MPF(): [PAM] Phase-4 Completed\n", - "[286600][V][PAM.cpp:507] MPF(): [PAM] Measurement Completed\n", - "[286923][V][PAM.cpp:526] MPF(): [PAM] All Completed\n", - "[286934][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[286941][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[286946][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[286953][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[286959][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[286972][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[286983][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[286993][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[287003][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[287014][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[287024][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[287034][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[287044][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n", - "[287054][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:450, freq: 100, actinic: 100, subfactor 2\n", - "[287065][V][PAM.cpp:134] run_arr_type1(): [PAM] Sample & Ref: 450, optionals: 56\n", - "[287072][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[287079][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[287085][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[287092][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[287098][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[287105][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[287112][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 1800 bytes\n", - "[287118][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[287124][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 1800 bytes\n", - "[287131][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[287137][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[287143][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[287149][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[287156][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[287162][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[287168][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[287174][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 224 bytes\n", - "[287181][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[287187][V][PAM.cpp:150] run_arr_type1(): [PAM] Memory allocation completed\n", - "[291475][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[291484][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[291489][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[291496][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[291502][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[291509][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[291515][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[291522][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[291527][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[291534][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[291540][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[291547][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[291553][V][PAM.cpp:298] MPF(): [PAM] RUN MPF with mode:1, pulse current:60, DC current 100\n", - "[291561][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[291568][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[291574][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[291581][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[291587][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[291593][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[291599][V][PAM.cpp:303] MPF(): [PAM] Memory allocation completed\n", - "[291606][V][PAM.cpp:359] MPF(): [PAM] Phase-1 Config\n", - "[291610][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[291619][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 1\n", - "[291628][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 2\n", - "[291636][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 3\n", - "[291645][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 4\n", - "[291654][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 5\n", - "[291662][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 6\n", - "[291671][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 7\n", - "[291680][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 8\n", - "[291688][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 9\n", - "[291697][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 10\n", - "[291706][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 11\n", - "[291719][V][PAM.cpp:369] MPF(): [PAM] Phase-1 LED ON\n", - "[291733][V][PAM.cpp:386] MPF(): [PAM] Phase-1 Completed; Phase-2 start\n", - "[292042][V][PAM.cpp:414] MPF(): [PAM] Phase-2 Completed; Phase-3 Start\n", - "[292049][V][PAM.cpp:421] MPF(): [PAM] LED set to 220\n", - "[292115][V][PAM.cpp:421] MPF(): [PAM] LED set to 190\n", - "[292181][V][PAM.cpp:421] MPF(): [PAM] LED set to 160\n", - "[292248][V][PAM.cpp:421] MPF(): [PAM] LED set to 130\n", - "[292314][V][PAM.cpp:421] MPF(): [PAM] LED set to 100\n", - "[292380][V][PAM.cpp:421] MPF(): [PAM] LED set to 70\n", - "[292445][V][PAM.cpp:421] MPF(): [PAM] LED set to 40\n", - "[292511][V][PAM.cpp:421] MPF(): [PAM] LED set to 10\n", - "[292577][V][PAM.cpp:444] MPF(): [PAM] Phase-3 Completed; Phase-4 Start\n", - "[292584][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[292893][V][PAM.cpp:484] MPF(): [PAM] Phase-4 Completed\n", - "[292898][V][PAM.cpp:507] MPF(): [PAM] Measurement Completed\n", - "[293221][V][PAM.cpp:526] MPF(): [PAM] All Completed\n", - "[293232][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[293239][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[293244][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[293251][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[293257][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[293270][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[293281][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[293291][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[293301][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[293312][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[293322][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[293332][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[293342][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n", - "[293352][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:50, freq: 10, actinic: 100, subfactor 2\n", - "[293363][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:450, freq: 100, actinic: 0, subfactor 2\n", - "[293373][V][PAM.cpp:134] run_arr_type1(): [PAM] Sample & Ref: 500, optionals: 62\n", - "[293380][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[293387][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[293393][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[293400][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[293406][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[293413][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[293420][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 2000 bytes\n", - "[293426][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[293432][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 2000 bytes\n", - "[293439][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[293445][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 248 bytes\n", - "[293451][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[293457][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 248 bytes\n", - "[293464][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[293470][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 248 bytes\n", - "[293476][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[293482][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 248 bytes\n", - "[293488][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[293494][V][PAM.cpp:150] run_arr_type1(): [PAM] Memory allocation completed\n", - "[302035][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[302045][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[302051][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[302058][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[302064][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[302071][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[302076][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[302083][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[302089][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[302096][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[302102][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[302109][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[302115][V][PAM.cpp:298] MPF(): [PAM] RUN MPF with mode:1, pulse current:60, DC current 0\n", - "[302123][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[302129][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[302136][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[302142][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[302148][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[302155][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[302161][V][PAM.cpp:303] MPF(): [PAM] Memory allocation completed\n", - "[302167][V][PAM.cpp:359] MPF(): [PAM] Phase-1 Config\n", - "[302172][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[302181][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 1\n", - "[302189][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 2\n", - "[302198][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 3\n", - "[302207][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 4\n", - "[302215][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 5\n", - "[302224][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 6\n", - "[302233][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 7\n", - "[302241][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 8\n", - "[302250][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 9\n", - "[302259][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 10\n", - "[302267][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 11\n", - "[302281][V][PAM.cpp:369] MPF(): [PAM] Phase-1 LED ON\n", - "[302295][V][PAM.cpp:386] MPF(): [PAM] Phase-1 Completed; Phase-2 start\n", - "[302604][V][PAM.cpp:414] MPF(): [PAM] Phase-2 Completed; Phase-3 Start\n", - "[302611][V][PAM.cpp:421] MPF(): [PAM] LED set to 220\n", - "[302677][V][PAM.cpp:421] MPF(): [PAM] LED set to 190\n", - "[302743][V][PAM.cpp:421] MPF(): [PAM] LED set to 160\n", - "[302810][V][PAM.cpp:421] MPF(): [PAM] LED set to 130\n", - "[302876][V][PAM.cpp:421] MPF(): [PAM] LED set to 100\n", - "[302942][V][PAM.cpp:421] MPF(): [PAM] LED set to 70\n", - "[303008][V][PAM.cpp:421] MPF(): [PAM] LED set to 40\n", - "[303073][V][PAM.cpp:421] MPF(): [PAM] LED set to 10\n", - "[303139][V][PAM.cpp:444] MPF(): [PAM] Phase-3 Completed; Phase-4 Start\n", - "[303146][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[303456][V][PAM.cpp:484] MPF(): [PAM] Phase-4 Completed\n", - "[303462][V][PAM.cpp:507] MPF(): [PAM] Measurement Completed\n", - "[303784][V][PAM.cpp:526] MPF(): [PAM] All Completed\n", - "[303795][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[303802][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[303808][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[303814][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[303820][V][do_c.cpp:47] arr_set(): [DO_C] Set arr3, line 0 to type 1 with 50 x 5Hz samples, actinic:0, sub:2, IR:0\n", - "[303831][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[303843][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[303854][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[303865][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[303875][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[303885][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[303895][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[303906][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[303916][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n", - "[303926][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:50, freq: 5, actinic: 0, subfactor 2\n", - "[303936][V][PAM.cpp:134] run_arr_type1(): [PAM] Sample & Ref: 50, optionals: 6\n", - "[303943][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[303950][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[303956][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[303963][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[303969][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[303976][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[303983][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 200 bytes\n", - "[303989][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[303995][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 200 bytes\n", - "[304001][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[304007][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 24 bytes\n", - "[304014][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[304020][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 24 bytes\n", - "[304026][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[304032][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 24 bytes\n", - "[304038][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[304044][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 24 bytes\n", - "[304051][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[304057][V][PAM.cpp:150] run_arr_type1(): [PAM] Memory allocation completed\n", - "[312530][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[312540][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[312546][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[312553][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[312559][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[312566][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[312571][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[312578][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[312584][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[312591][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[312597][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[312604][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[312609][V][PAM.cpp:298] MPF(): [PAM] RUN MPF with mode:1, pulse current:60, DC current 0\n", - "[312617][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[312624][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[312631][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[312637][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[312643][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[312650][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[312656][V][PAM.cpp:303] MPF(): [PAM] Memory allocation completed\n", - "[312662][V][PAM.cpp:359] MPF(): [PAM] Phase-1 Config\n", - "[312667][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[312676][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 1\n", - "[312684][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 2\n", - "[312693][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 3\n", - "[312702][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 4\n", - "[312710][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 5\n", - "[312719][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 6\n", - "[312728][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 7\n", - "[312736][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 8\n", - "[312745][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 9\n", - "[312754][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 10\n", - "[312762][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 11\n", - "[312776][V][PAM.cpp:369] MPF(): [PAM] Phase-1 LED ON\n", - "[312790][V][PAM.cpp:386] MPF(): [PAM] Phase-1 Completed; Phase-2 start\n", - "[313099][V][PAM.cpp:414] MPF(): [PAM] Phase-2 Completed; Phase-3 Start\n", - "[313106][V][PAM.cpp:421] MPF(): [PAM] LED set to 220\n", - "[313172][V][PAM.cpp:421] MPF(): [PAM] LED set to 190\n", - "[313238][V][PAM.cpp:421] MPF(): [PAM] LED set to 160\n", - "[313304][V][PAM.cpp:421] MPF(): [PAM] LED set to 130\n", - "[313371][V][PAM.cpp:421] MPF(): [PAM] LED set to 100\n", - "[313437][V][PAM.cpp:421] MPF(): [PAM] LED set to 70\n", - "[313503][V][PAM.cpp:421] MPF(): [PAM] LED set to 40\n", - "[313568][V][PAM.cpp:421] MPF(): [PAM] LED set to 10\n", - "[313634][V][PAM.cpp:444] MPF(): [PAM] Phase-3 Completed; Phase-4 Start\n", - "[313641][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[313951][V][PAM.cpp:484] MPF(): [PAM] Phase-4 Completed\n", - "[313956][V][PAM.cpp:507] MPF(): [PAM] Measurement Completed\n", - "[314279][V][PAM.cpp:526] MPF(): [PAM] All Completed\n", - "[314290][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[314297][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[314303][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[314310][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[314315][V][do_c.cpp:47] arr_set(): [DO_C] Set arr3, line 0 to type 1 with 60 x 5Hz samples, actinic:0, sub:2, IR:0\n", - "[314326][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[314338][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[314349][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[314360][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[314370][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[314380][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[314391][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[314401][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[314411][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n", - "[314421][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:60, freq: 5, actinic: 0, subfactor 2\n", - "[314431][V][PAM.cpp:134] run_arr_type1(): [PAM] Sample & Ref: 60, optionals: 7\n", - "[314438][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[314445][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[314451][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[314458][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[314465][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[314471][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[314478][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 240 bytes\n", - "[314484][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[314490][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 240 bytes\n", - "[314497][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[314503][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 28 bytes\n", - "[314509][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[314515][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 28 bytes\n", - "[314521][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[314527][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 28 bytes\n", - "[314534][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[314540][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 28 bytes\n", - "[314546][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[314552][V][PAM.cpp:150] run_arr_type1(): [PAM] Memory allocation completed\n", - "[324752][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[324760][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[324766][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[324773][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[324778][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[324785][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[324791][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[324798][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[324804][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[324811][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[324817][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[324823][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[324829][V][PAM.cpp:298] MPF(): [PAM] RUN MPF with mode:1, pulse current:60, DC current 0\n", - "[324837][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[324844][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[324850][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[324857][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[324863][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[324870][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[324876][V][PAM.cpp:303] MPF(): [PAM] Memory allocation completed\n", - "[324882][V][PAM.cpp:359] MPF(): [PAM] Phase-1 Config\n", - "[324887][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[324895][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 1\n", - "[324904][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 2\n", - "[324913][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 3\n", - "[324921][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 4\n", - "[324930][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 5\n", - "[324939][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 6\n", - "[324947][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 7\n", - "[324956][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 8\n", - "[324965][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 9\n", - "[324973][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 10\n", - "[324982][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 11\n", - "[324996][V][PAM.cpp:369] MPF(): [PAM] Phase-1 LED ON\n", - "[325010][V][PAM.cpp:386] MPF(): [PAM] Phase-1 Completed; Phase-2 start\n", - "[325319][V][PAM.cpp:414] MPF(): [PAM] Phase-2 Completed; Phase-3 Start\n", - "[325326][V][PAM.cpp:421] MPF(): [PAM] LED set to 220\n", - "[325392][V][PAM.cpp:421] MPF(): [PAM] LED set to 190\n", - "[325459][V][PAM.cpp:421] MPF(): [PAM] LED set to 160\n", - "[325525][V][PAM.cpp:421] MPF(): [PAM] LED set to 130\n", - "[325591][V][PAM.cpp:421] MPF(): [PAM] LED set to 100\n", - "[325657][V][PAM.cpp:421] MPF(): [PAM] LED set to 70\n", - "[325723][V][PAM.cpp:421] MPF(): [PAM] LED set to 40\n", - "[325788][V][PAM.cpp:421] MPF(): [PAM] LED set to 10\n", - "[325854][V][PAM.cpp:444] MPF(): [PAM] Phase-3 Completed; Phase-4 Start\n", - "[325861][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[326171][V][PAM.cpp:484] MPF(): [PAM] Phase-4 Completed\n", - "[326176][V][PAM.cpp:507] MPF(): [PAM] Measurement Completed\n", - "[326499][V][PAM.cpp:526] MPF(): [PAM] All Completed\n", - "[326510][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[326517][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[326522][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[326529][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[326535][V][do_c.cpp:47] arr_set(): [DO_C] Set arr3, line 0 to type 1 with 70 x 5Hz samples, actinic:0, sub:2, IR:0\n", - "[326545][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[326558][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[326569][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[326580][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[326590][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[326600][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[326610][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[326620][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[326631][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n", - "[326641][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:70, freq: 5, actinic: 0, subfactor 2\n", - "[326651][V][PAM.cpp:134] run_arr_type1(): [PAM] Sample & Ref: 70, optionals: 8\n", - "[326658][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[326664][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[326671][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[326678][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[326684][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[326691][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[326697][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 280 bytes\n", - "[326704][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[326710][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 280 bytes\n", - "[326716][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[326722][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 32 bytes\n", - "[326729][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[326735][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 32 bytes\n", - "[326741][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[326747][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 32 bytes\n", - "[326753][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[326759][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 32 bytes\n", - "[326766][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[326772][V][PAM.cpp:150] run_arr_type1(): [PAM] Memory allocation completed\n", - "[338688][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[338704][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[338710][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[338716][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[338722][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[338729][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[338735][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[338742][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[338748][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[338754][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[338760][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[338767][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[338773][V][PAM.cpp:298] MPF(): [PAM] RUN MPF with mode:1, pulse current:60, DC current 0\n", - "[338781][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[338788][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[338794][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[338801][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[338807][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[338813][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[338819][V][PAM.cpp:303] MPF(): [PAM] Memory allocation completed\n", - "[338826][V][PAM.cpp:359] MPF(): [PAM] Phase-1 Config\n", - "[338831][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[338839][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 1\n", - "[338848][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 2\n", - "[338857][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 3\n", - "[338865][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 4\n", - "[338874][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 5\n", - "[338882][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 6\n", - "[338891][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 7\n", - "[338900][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 8\n", - "[338908][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 9\n", - "[338917][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 10\n", - "[338926][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 11\n", - "[338939][V][PAM.cpp:369] MPF(): [PAM] Phase-1 LED ON\n", - "[338953][V][PAM.cpp:386] MPF(): [PAM] Phase-1 Completed; Phase-2 start\n", - "[339262][V][PAM.cpp:414] MPF(): [PAM] Phase-2 Completed; Phase-3 Start\n", - "[339269][V][PAM.cpp:421] MPF(): [PAM] LED set to 220\n", - "[339335][V][PAM.cpp:421] MPF(): [PAM] LED set to 190\n", - "[339401][V][PAM.cpp:421] MPF(): [PAM] LED set to 160\n", - "[339468][V][PAM.cpp:421] MPF(): [PAM] LED set to 130\n", - "[339534][V][PAM.cpp:421] MPF(): [PAM] LED set to 100\n", - "[339600][V][PAM.cpp:421] MPF(): [PAM] LED set to 70\n", - "[339666][V][PAM.cpp:421] MPF(): [PAM] LED set to 40\n", - "[339731][V][PAM.cpp:421] MPF(): [PAM] LED set to 10\n", - "[339797][V][PAM.cpp:444] MPF(): [PAM] Phase-3 Completed; Phase-4 Start\n", - "[339804][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[340114][V][PAM.cpp:484] MPF(): [PAM] Phase-4 Completed\n", - "[340119][V][PAM.cpp:507] MPF(): [PAM] Measurement Completed\n", - "[340442][V][PAM.cpp:526] MPF(): [PAM] All Completed\n", - "[340453][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[340460][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[340465][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[340472][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[340478][V][do_c.cpp:47] arr_set(): [DO_C] Set arr3, line 0 to type 1 with 80 x 5Hz samples, actinic:0, sub:2, IR:0\n", - "[340489][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[340501][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[340512][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[340523][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[340533][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[340543][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[340553][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[340563][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[340574][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n", - "[340584][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:80, freq: 5, actinic: 0, subfactor 2\n", - "[340594][V][PAM.cpp:134] run_arr_type1(): [PAM] Sample & Ref: 80, optionals: 10\n", - "[340601][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[340608][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[340614][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[340621][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[340627][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[340634][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[340641][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 320 bytes\n", - "[340647][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[340653][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 320 bytes\n", - "[340659][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[340665][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 40 bytes\n", - "[340672][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[340678][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 40 bytes\n", - "[340684][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[340690][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 40 bytes\n", - "[340696][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[340702][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 40 bytes\n", - "[340709][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[340715][V][PAM.cpp:150] run_arr_type1(): [PAM] Memory allocation completed\n", - "[354358][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[354374][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[354379][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[354386][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[354392][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[354399][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[354405][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[354412][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[354418][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[354424][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[354430][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[354437][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[354443][V][PAM.cpp:298] MPF(): [PAM] RUN MPF with mode:1, pulse current:60, DC current 0\n", - "[354451][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[354458][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[354464][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[354471][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[354477][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[354483][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[354489][V][PAM.cpp:303] MPF(): [PAM] Memory allocation completed\n", - "[354496][V][PAM.cpp:359] MPF(): [PAM] Phase-1 Config\n", - "[354500][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[354509][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 1\n", - "[354518][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 2\n", - "[354526][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 3\n", - "[354535][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 4\n", - "[354544][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 5\n", - "[354552][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 6\n", - "[354561][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 7\n", - "[354570][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 8\n", - "[354578][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 9\n", - "[354587][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 10\n", - "[354596][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 11\n", - "[354609][V][PAM.cpp:369] MPF(): [PAM] Phase-1 LED ON\n", - "[354623][V][PAM.cpp:386] MPF(): [PAM] Phase-1 Completed; Phase-2 start\n", - "[354932][V][PAM.cpp:414] MPF(): [PAM] Phase-2 Completed; Phase-3 Start\n", - "[354939][V][PAM.cpp:421] MPF(): [PAM] LED set to 220\n", - "[355005][V][PAM.cpp:421] MPF(): [PAM] LED set to 190\n", - "[355072][V][PAM.cpp:421] MPF(): [PAM] LED set to 160\n", - "[355138][V][PAM.cpp:421] MPF(): [PAM] LED set to 130\n", - "[355204][V][PAM.cpp:421] MPF(): [PAM] LED set to 100\n", - "[355270][V][PAM.cpp:421] MPF(): [PAM] LED set to 70\n", - "[355336][V][PAM.cpp:421] MPF(): [PAM] LED set to 40\n", - "[355402][V][PAM.cpp:421] MPF(): [PAM] LED set to 10\n", - "[355467][V][PAM.cpp:444] MPF(): [PAM] Phase-3 Completed; Phase-4 Start\n", - "[355474][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[355784][V][PAM.cpp:484] MPF(): [PAM] Phase-4 Completed\n", - "[355790][V][PAM.cpp:507] MPF(): [PAM] Measurement Completed\n", - "[356112][V][PAM.cpp:526] MPF(): [PAM] All Completed\n", - "[356123][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[356130][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[356136][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[356142][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - " Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "Data sent\n", - "\n" - ] - } - ], - "source": [ - "print(info_outputs, other_outputs)" - ] - }, - { - "cell_type": "code", - "execution_count": 414, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\hjcbj\\AppData\\Local\\Temp\\ipykernel_12784\\944574406.py:2: RuntimeWarning: invalid value encountered in divide\n", - " plt.plot(x/y)\n" - ] - }, - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 414, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGdCAYAAADAAnMpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAABjMklEQVR4nO3dd3QUVRsG8CcdCAkdEkIv0g1NmlQRFFQQkCIWDKACKqKoH4gKgjSlSVOUjg1EUVBKEGlCAEPvIJ0AoZOQhNT5/khmMpud3czszu5seX7n5DA77d4JSfbdW97rA0AAERERkUF8ja4AEREReTcGI0RERGQoBiNERERkKAYjREREZCgGI0RERGQoBiNERERkKAYjREREZCgGI0RERGQof6MroFbZsmWRmJhodDWIiIhIg5CQEFy5csXqOW4RjJQtWxZxcXFGV4OIiIhsEBERYTUgcYtgRGwRiYiIYOsIERGRmwgJCUFcXFy+791uEYyIEhMTGYwQERF5GA5gJSIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQ7nVQnlEROS5SpSLQMu+PZF46zb+XrDU6OqQE7FlhIiIXEKRMqXQ+qXeaNylk9FVISdjMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIbSHIy0atUKq1evRlxcHARBQNeuXa2e/+ijj+Kff/7BzZs3kZycjOPHj2PYsGG21peIiIg8jOakZ8HBwTh48CAWLVqEX3/9Nd/zk5KSMHv2bBw6dAhJSUlo2bIl5s2bh6SkJHz77bc2VZqIiDyXj4+P0VUgJ9McjKxfvx7r169Xff6BAwdw4MAB6fWFCxfQvXt3tGrVisEIEREROX/MSP369dGiRQts3brV4jmBgYEICQkx+SIiIs8mCILRVSCDOC0YuXTpEh48eIDY2FjMmTMHCxYssHjuyJEjkZCQIH3FxcU5q5pERETkZE4LRlq1aoXGjRtj0KBBGDZsGPr06WPx3IkTJyI0NFT6ioiIcFY1iYiIyMmctmrv+fPnAQBHjhxBmTJlMGbMGPz000+K56alpSEtLc1ZVSMiIiIDGZJnxMfHB0FBQUYUTURERC7Gpqm91apVk15XrlwZkZGRuH37Ni5duoQJEyYgIiIC/fr1AwAMGTIEFy9exIkTJwAALVu2xHvvvYdZs2bp9AhERETkzjQHI40bN8aWLVuk19OnTwcALF68GFFRUQgPD0eFChWk476+vpg4cSIqV66MjIwMnDlzBiNGjMC8efPsrz0RERG5Pc3ByNatW60mpImKijJ5PXv2bMyePVt7zYiIiMgrcG0aIiIiMhSDESIicg1Meua1GIwQERGRoRiMEBERkaEYjBAREZGhGIwQERGRoRiMEBERkaEYjBAREZGhGIwQEZFLsZZYkzwTgxEiIiIyFIMRIiIiMhSDESIicglMwOq9GIwQERGRoRiMEBERkaEYjBAREZGhGIwQERGRoRiMEBERkaEYjBARkWth0jOvw2CEiIiIDMVghIiIiAzFYISIiFwDs555LQYjREREZCgGI0RERGQoBiNERERkKAYjREREZCgGI0RERGQoBiNERORSfJj0zOswGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIpcggBlYvRWDESIiIjIUgxEiIiIyFIMRIiIiMhSDESIiIjIUgxEiInItzHnmdRiMEBERkaEYjBAREZGhGIwQERGRoRiMEBERkaEYjBARkUsQBGZg9VYMRoiIiMhQDEaIiIjIUAxGiIiIyFAMRoiIyKX4+DDrmbdhMEJERESGYjBCREREhmIwQkRERIbSHIy0atUKq1evRlxcHARBQNeuXa2e361bN0RHR+P69eu4d+8edu7ciY4dO9pcYSIiIvIsmoOR4OBgHDx4EG+++aaq81u3bo2NGzeic+fOaNSoETZv3ow1a9agfv36WosmIiJPxqRnXstf6wXr16/H+vXrVZ//zjvvmLweNWoUunbtimeeeQYHDhzQWjwRERF5GM3BiL18fHwQEhKC27dvWzwnMDAQQUFB0uuQkBBnVI2IiIgM4PQBrMOHD0dwcDBWrFhh8ZyRI0ciISFB+oqLi3NiDYmIiMiZnBqM9OnTB2PGjEHv3r1x48YNi+dNnDgRoaGh0ldERIQTa0lERMZi0jNv47Ruml69emHBggXo2bMnNm3aZPXctLQ0pKWlOalmREREZCSntIz06dMHixcvRt++fbF27VpnFElERERuQnPLSHBwMKpVqya9rly5MiIjI3H79m1cunQJEyZMQEREBPr16wcgOxBZunQp3n77beza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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "x, y = np.array(data['Fluo']), np.array(data['Fluoref'])\n", - "plt.plot(x/y)\n", - "\n", - "# if \"730\" in data:\n", - "# plt.twinx()\n", - "# plt.plot(data['730'] / data['730ref'], 'm')\n", - "# plt.twinx()\n", - "# plt.plot(data['SUN'], 'y')\n", - "# plt.twinx()\n", - "# plt.plot(data['leaf'], 'r')\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "sig = arr[1:, 1] - 16384\n", - "ref = arr[1:, 0] - 16384\n", - "plt.plot(ref, sig-ref - ref * 0.01)\n", - "plt.plot(ref, sig-ref - ref * 0.01 - 5 * np.sin(ref/3.14/26))\n", - "plt.grid(c = [.2, .2, .2, .2])\n" - ] - }, - { - "cell_type": "code", - "execution_count": 418, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "389" - ] - }, - "execution_count": 418, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "y[3000]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 254, - "metadata": {}, - "outputs": [], - "source": [ - "info_outputs = \"\"\n", - "other_outputs = \"\"\n", - "with serial.Serial(\"COM31\", 115200) as ser:\n", - " start_timer = time.time()\n", - " #ser.write(\"mpf, 0, 100, 100\".encode())\n", - " #ser.write(\"arrun, 500, 500, 0, 0, 0\".encode())\n", - " ser.write(\"sd\\n\".encode())\n", - " \n", - " data = []\n", - "\n", - " while (time.time() - start_timer < 40):\n", - " if ser.in_waiting > 2:\n", - " start_timer = time.time()\n", - " l = ser.readline().decode().strip()\n", - " if l[0] == '[':\n", - " info_outputs += l + \"\\n\"\n", - "\n", - "\n", - " elif l[:5] == 'Data:':\n", - " data.append(np.fromstring(l[5:], sep = ',', dtype=int))\n", - " \n", - " \n", - "\n", - " elif l == \"Cmd Done!\":\n", - " break\n", - " else:\n", - " other_outputs += l + \"\\n\"\n", - " else:\n", - " time.sleep(.5)\n", - " \n", - "arr = np.array(data)" - ] - }, - { - "cell_type": "code", - "execution_count": 382, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 382, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiwAAAGdCAYAAAAxCSikAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAABvaUlEQVR4nO3deVxU9f7H8RcwDOuwC8OOC7ig4p67ZmppJS5dyzY1K7PVNm/+uqn3VmabaXVbLLOyVSsXygXNfV9SU9xRXNgURAHZme/vj9G5ES6AwJmBz/Px+D4ezpnvDO/jAebD93zP99gBCiGEEEIIK2avdQAhhBBCiOuRgkUIIYQQVk8KFiGEEEJYPSlYhBBCCGH1pGARQgghhNWTgkUIIYQQVk8KFiGEEEJYPSlYhBBCCGH1dFoHqE5BQUHk5ORoHUMIIYQQlWAwGEhJSblmnzpTsAQFBZGcnKx1DCGEEEJUQXBw8DWLljpTsFweWQkODpZRFiGEEMJGGAwGkpOTr/vZXWcKlstycnKkYBFCCCHqGJl0K4QQQgirJwWLEEIIIayeFCxCCCGEsHpSsAghhBDC6knBIoQQQgirJwWLEEIIIayeFCxCCCGEsHpSsAghhBDC6knBIoQQQgirJwWLEEIIIayeFCxCCCGEsHpSsAghhBDC6knBIoSodu3aNeb554fg5uasdRQhRB0hBYsQolp17tyUteum8fY7D7Fp89s0bhyodSQhRB0gBYsQotpER4fx62+TcXNzxmRStGoVwbbt02nXrrHW0YQQNk4KFiFEtXB21rNk6RR8fAwczyjmq6M+nMgsxtvbnedfGKJ1PCGEjZOCRQhRLYYN60poaAOSU86x9GwA54sc2JnjC8BNXVponE4IYeukYBFCVIsxD/cHIH5rMoWl9iTt2cu2rQcAaBTRAHd3Fy3jCSFsnBQsQogbFhkZRO/erSgtLeWkg3mS7fZFS1g48wtyis2/ZmJiGmoZUQhh46RgEULcsIce6gfA72sS8GjUDFNpKftWrSX18FHO5OsA6NCpqZYRhRA2TgoWIcQN0ekcGDnqFgDW7TsPwLGdu8nNzCIn8xynzxUB0LVnK60iCiHqAClYhBBVotfreOyxAeza/T5GozdpaVlc9IsE4M8Vqy399h9OB6BNm0aa5BRC1A2VLlh69OjB4sWLSU5ORilFbGzsNfvPmTMHpVS5tm/fPkufkSNHXrGPk5NT5fdICFErvvv+RT76+HGio8PIzc3n7U/WEhbTCpPJxN7f11r67dh+CIBGod44O+u1iiuEsHGVLljc3NzYs2cPTz75ZIX6P/PMMxiNRksLCQkhMzOT+fPnl+l34cKFMv2MRiOFhYWVjSeEqAWjRt3C0KFdKSoqZvwzs4hu+wLet9wFwMbvfyL7bIal777tCeSV2OHgYE+rVuFaRRZC2DhdZV+wbNkyli1bVuH+2dnZZGdnWx7Hxsbi7e3NnDlzyvRTSpGenl7ZOEKIWhYe7s+MmY8CMHnSd3z43yU8NvsD3Ly9OLX/IHHvflimf8qlibcRhmLatWvM9u1Hyjzv5OZK7ITx7IlfxaGNW2ptP4QQtqXW57CMGTOGlStXcvLkyTLb3d3dSUpK4tSpU8TFxdGmTZtrvo9er8dgMJRpQoia9+msJ/HwcGXDhv28/fYv3PH8kzRu35aC3IvMfeEVSouLy/RPT0wiPc8BgJu6Rpd7v273DOOmoXcSO+GZWskvhLBNtVqwGI1GBgwYwOeff15m+8GDBxk1ahSDBg1ixIgRFBQUsHHjRpo0aXLV95o4caJl9CY7O5vk5OSaji9EvdekSSD9+7elpNTEY09+RodBA+n1wD0A/PDKa2SeOl3uNSVFRRw5mQVAh05R5Z5vO7A/9igCGoVj8POt2R0QQtg0VdWmlFKxsbEV7v/SSy+ps2fPKkdHx2v2s7OzU7t27VIzZ868ah+9Xq8MBoOlBQUFKaWUMhgMVd4fadKkXbtNmnKfMqk4dSx7mXpz51r11h/r1bt7N6v+jz10zdc9/fEbyqTiVHHpIuXt7W7ZbmzSSP14bK0qKIlTf2SsUG0H9NN8H6VJk1a7zWAwVOjzu1ZHWB566CHmzp1L8d+GjP9OKcX27duJjIy8ap+ioiJycnLKNCFEzXpwtHmBuP3nHNHp9Tg46tgTv4oVn8655usSdh4ko8ABB3t7br21nWV7n+F3MDAkB70DtPYpoMeAbjWaXwhhu2qtYOnVqxeRkZHMnj27Qv3btGlDampqDacSQlRUTExDGoX5UmKCL/67gHeGPcD3L7/KdxP/jVLqmq9NOXyU4znmS5oHDOwAgL29Pf9+9hbcHM2vtbeDB4e0qdF9EELYripd1hwTE0NMTAwADRs2JCYmhtDQUACmTp3KV199Ve51Y8aMYcuWLSQkJJR7btKkSfTv39/yXrNnz6ZNmzZ88sknlY0nhKghD44239zwWI6eTQuWkHr4KDsWL6GkqOi6rz2xZy+HM0wA3DGoM/b29rz27mM09rOnqBTGPj4LgDbBDsR0Kj8xVwghoJLnmnr16qWuZM6cOQpQc+bMUatXry7zGg8PD3Xx4kX18MMPX/E9p0+frpKSklRBQYFKT09Xy5YtU507d66Rc2DSpEkzN71ep1q0CLvic23aNFL//OddqnPnpsre3l6Fh/urlDPfK5OKU3M2fV+lr9d/7CiVV/KrMqk4dd/I/iq/aJEyqTj1ya/vK0DtT/5JmVScWrLmXdW0Q2vV96H71chHBqpZs55ULVuGa/7/JU2atJpplfj81j5sLe+wNGnSQH3wwVhlUnHq/vtvLvfcwUOfKJOKUyYVp/IKFlj+nV/yq+r/yANV+no6vV7tSVmoTCpOFZSY3+9E9m8quHmUAtRz7020fJ3i0jh1sfhXy+PNW6ff8P627t9HNWrfRvP/d2nSpJVtVjnpVghhHRwdddx3f28A/vnSXWWea906gqioYEpKTRSUgLOTjlIFKXk6Via7sy0uvkpfs6SoiB+/Nr9W7wAmBWMfmk7ygcMA/DpvJWtS3ThfZI+DPbjoFLnF9pSa4KZOkXTocPVJ+NfjFxbCyHdfZ9zsD+kwaGCV30cIoR0pWISoh/r0aY2XlzsA0dFh9O3bxvLc3ffeDEDSRWc+PejLW8vOMfm3HL477M73363mfFrVV6T+fPp3ln/P/W49y3763fL4+K4/2ZBYxGf73Ln7mZ/o2eOf9Lz9bQ5nm+8p9tTTd1b564a3bM5tITk8EJXNou+fYvXWDxl4Ryfs7Oyq/J5CiNqn+XBQdTQ5JSRN2rVbaMsWyuDrowA1a9aT5tM9+T8rk4pTi+MmWfqdOjtfmVSc+mHXzyo0urllu529fbXk+Pjjx9XWbdOVr69Huee8jAHKyxhgeWyvc1Cz9yw3n0YqWqACAryq9DWfm/6/001/bSnnflJd+rTX/NhIk1afm5wSEkJYNO16E+O/n81T38zCxd2V2MGdARj/zGeYTCbuuKMjTZuHcs/4UQT7OVNqglfHvcaphAOW91AmU7VkGTfuI27q9ByZmdnlnjufll5mBMdUUsq6VX+QkqdD76jjnXfH8PTTd3LnnZ0q9TXbtG0MwL7DZ5j04UY2n4KCUjuM3k589sVzN7ZDQohaIQWLEHWcnb09dzz3BAC+IcE8+/YLNGjgSWZmNrNnx7Nmo/lmhMu2zuKFl+8DYGdCKglbdmmW+a8ObdzC7kwXAO67rzczZj7KosWvEB0dVuH3aNqkAQDrNxzgtaem0S3sTmJHzqLEBC3CPehzS5urvtajgZ+cOhLCCkjBIkQd125gf4KaRlJcUAjAsLu6A7B48TZ6jbqPNGMHSk0QbiihnV8BAJ+9/5Nmef/u0MatHL6g54+zepYs3UlyciYAnS7dl8jDw5Wff/k/7rmn5xVf7+JhIMTHvGjdprW7LdtXfv8rf6SY/z3tnUeu+Nq2A/rx1rpFjHj15XLPObm6ctOQ2/EONFZ114QQlSAFixB1mE6v57anHgVg+cefU3xgCy28zQu9efQcxO3jHye90Il/fbGPvfvMd1AvLi5h4YItmmX+u6zUNNKPn2RtugcTp68hbukeANq0aQTAiPtuZsiQLrw+bfQVXx/ctAl+ziUA7Nh60LJdmUx89s1GSkzQoU0YN9/cuszrevdty8/fPc2jzc4x65+dmfvLf+jcuSmOjjqaNAlk097P2fjzY3y16Vvuf+s/tLmtLyEtmuHo7FQT/w1C1Hs6rQMIIWpOt7uH0qqJHw75Zxja3sAT/2gCKFLydJwucCEvO5u4dz5k24I43tU58OCDfUhJOXfF+SVaOrhhC/4Nwxn2rxeJaOwK5NKxc3MAbr/LfFVTeJgvrq5O5OUVlnlth+5tcbSHwmITR46klHlu1fyl7H3kFtr6FjBv/ku8OW0+x4+n8/gTt5cpYFx1ivuGtOW+IW3Jzy9Ep9Ph6OgAQI+gAo4O6EvbAeb7LJ1PP8O0O4ZbRrSEENVDChYh6ihHZycmTr6P/o0uAPYQbV5/5PM5q5g5ZxMpR09wPi3dch+gkpJSvvhihYaJr+7Qpi30fOBuDL4+nMk3j5a0ahWOnZ0dbVuHA2BvZ0en7q1ZE7+9zGs7djEXNkkpFzD9beJw0q4/WbE/j9COjvj5Gnjr7Ycsz5kUJGQ58fy4GQx77G56tzMS7FyAu4t5BOV4jiM+drl4ujtRuncdSUWehLWOxivAn8DIxpzcu7/G/j+EqI+kYBGijuoyfAgxQTqghO07jnDwwGl+/mkjixdv1TpapR3evJ0N381HKUVJQQH3vjsQNxdHYto0IsjX2dKvz+3dyxUs0c1DANiXcLrc+yql2PTrKvTed9Pcq5CODfLR2ysSspz4M8uZrfGbWDlvCX9s2s3477/AzasBPk6l2AH79p3AsG8Zr7xyN90b6Xix8zjGzppJVJdOGBs3koJFiGomBYsQdZDexZnYx+4nwKUEk0lxx8B/c/bsBa1jVZmptJQFb0wHwDvISObUOwlwKeX5/7uPv17A06FzszKvs7Ozo1GIJ1DK9i1XLiC2LfiVbncPZXcqzFuwl3OnU/ELC0HnpLd8zXOnU3hr8Ai6j/gH3e4ZhqOTE/MmTyU/LZkXXxzCTTc1pVu3FqQlHieqSycCGjeskf8HIeozKViEqIO6Dh9KqzBX4CJbtx6y6WLl77JS0jh5poCAcEeGDmoPQHEpODpAsyYBZfr6hAQT4GY+5bVpzZUv0047ksiUm++kuLCQksKrzzvJzcxi2YezWPnZVzi5OHPxvPn/dO7Xq3jk0dv45NMn+PTnfYAioIkULEJUN7lKSIg6pnGHttz21KM08jBfDbR4ke2dArqe3buPAeCiN/8KW7nJ/DjQU4dnQANLv+YdWmJwNM9b2XPpNVeSn519zWLlr0oKCy3FCsCbb/5MVlYu0dFhvD9pIHeE5mBsFFGp/RFCXJ8ULELUISEtmvLQB2/j4qwn1MX8ARwXt03jVNVvzbLNZR5/PPMXSkoVTg6KHnf0smyPHRULQPq5fHJy8msky7FjaTRvNo6ZMxZRVFRCpGcRLZv44eTqWiNfT4j6SgoWIeoIJ1dXxvz3XZzd3dCnHcRRZ8+xY2ns339S62jVbvlfbppYalKs/G0zJ1NzAOjZ9yYAmnRqT48u5js8b//L+is14cyZ8zz77OfMm7cegIaGIvxllEWIaiUFixB1RKu+vfHw86XgTDIRpeYrYuJs8IqgijiXcZ6zF8ynvJJScykoKGLP3iQA2nWIxM3bi3/88wlifMwr9875bGmt5Fry2w7AXLAYG0fUytcUor6QgkWIOqL9HbfSPeAiz/fQcWv/tgDMn79R41Q15/KqtWtWmIuELZeW3Q9t4MKk3xcT2yMMJwfFn3tPsHBh7azcu3z5H5SWmvBzLqVVhxa18jWFqC+kYBGiDvBo4Ee33m3M64g4OrB580GGDH6dTZsOXP/FNmraa9+xYcN+3n1zHgA7tx8CwMchnwZudrS5NLoy+ZVvLIvj1bSsrFz2HjoDwM09m12ntxCiMuSyZiHqgLYD+tHQw7wC7KJFWxgy+HWNE9W89esT6Nnjn5bHe/YkAeDr7sCoqPMA/LErkUWLave+SMtX7KZNi9vo2LzB9TsLISpMRliEqAPa33EbEe7mOR1Ll+zUOI02MjOzmTzpWw4fTgbAZDIx8Z9f1XqOn75fBUAjX3u8fL1q/esLUVdJwSKEjTM2aUTD6CYEuppHWJYv/0PjRNp59dUfaNb0Mbw87yYsdDQrVlx5sbiatHPrAbLyQWcPg+/uU+tfX4i6SgoWIWxcp6F3EuZejL0dHDp0mhMnzmgdSXPZ2XmkpJzT7OsfPGW+xLprz9bX6SmEqCgpWISwYXoXZzrF3k64ezEAy5fV39EVa7L3z+MAxLSP0jiJEHWHFCxC2LA2t/XDxcOdUBfzKq71+XSQNVnzm3kBufBgT9y8vbQNI0QdIQWLEDas6z1D8XEqxdvFjoKCItau3ad1JAFsXWueO+PjZCKm380apxGibpCCRQgbFdqyBaEtmhHlngeYL/PNy6vYDfxEzTpx4iyFRSXo7OGWYf21jiNEnSAFixA2qstdsbg4mGjraz4d9N8Pf9M4kbjMZDJx+EgqAG06NsPg56txIiFsnxQsQtggOzs7WvTuTocG+TjrHdix4wiL6+h9g2zVvj+PAeDnYiKmv5wWEuJGScEihA0KiW6OMcCLGB/z6MrkSd9qnEj83YH9pwDwcSolqnMnjdMIYfukYBHCBjXv0YUOfnk42sPmzQdZurR+rm5rzQ4c+F/B4hUYoHEaIWyfFCxC2KC2t3Sjpbf55n7/nvKdxmnElRw4cBq4VLAEyH2FhLhRUrAIYWMMvj4M6BaO3gES9p8iPr72l58X13fkSAolJaU4OSj8G3jg6OykdSQhbJoULELYmOheXWnrax5defftnzVOI66muLiExETzlUK+TqV4BvhrnEgI2yYFixA25oGHBmBwNHEuu4DvvlurdRxxDfv/MvHWSwoWIW5IpQuWHj16sHjxYpKTk1FKERsbe83+vXr1QilVrjVt2rRMv6FDh5KQkEBBQQEJCQkMHjy4stGEqBcG9QwHYM7X6ygqKtE4jbiWg3+deGuUibdC3IhKFyxubm7s2bOHJ598slKvi4qKwmg0WtqRI0csz3Xu3Jkff/yRuXPnEhMTw9y5c5k3bx6dOsmlgEL8lZungWCDAuC/M37SOI24noMHzRNvvZ1K8ZSJt0LcEF1lX7Bs2TKWLVtW6S905swZLly4cMXnxo8fz4oVK5g2bRoA06ZNo1evXowfP55777230l9LiLoqIso8umJScOJYmsZpxPUcO5YOgKdeRliEuFG1Nodl165dpKSksHLlSnr37l3muS5duhAfH19m2/Lly+natetV30+v12MwGMo0Ieq68CYhAFwsMp9aFdbt2KWi0uBowjdI5rAIcSNqvGBJTU3lkUceYdiwYQwdOpRDhw7x+++/06NHD0sfo9FIenp6mdelp6djNBqv+r4TJ04kOzvb0pKTk2tsH4SwFqERQQBk5xVrnERURFpaFgWFxdjbQUTE1X+fCSGur9KnhCrr8OHDHD582PJ4y5YthIaG8sILL7B+/XrL9r//tWhnZ3fNvyDfeOMNpk+fbnlsMBikaBF1XlCoeR7E+Wy5K7MtUEpx8lQmUU2MhIXIDRCFuBGaXNa8ZcsWIiMjLY/T0tLKjab4+/uXG3X5q6KiInJycso0Ieq6wEDzh15m1kWNk4iKSjyaAoC/txN6F2eN0whhuzQpWNq2bUtqaqrl8ebNm+nXr1+ZPv3792fTpk21HU0Iq9aggScAGRnZGicRFXX0sHnk11NvksXjhLgBlT4l5ObmRpMmTSyPGzZsSExMDOfOnePUqVNMnTqV4OBgRo4cCcAzzzxDUlISCQkJ6PV67r//fu666y6GDh1qeY+ZM2eybt06JkyYwKJFi4iNjaVv37507969GnZRiLrD18cNgPS0LI2TiIq6PPH28pVCZ5NOapxICNtU6YKlQ4cOrFmzxvL4vffeA+DLL79k9OjRBAYGEhYWZnler9fzzjvvEBwcTH5+PgkJCQwcOJClS5da+mzevJl77rmH1157jVdffZXExETuvvtutm3bdgO7JkTd4+3lCkBq8lmNk4iK+l/BYpKbIApxg1RdaAaDQSmllMFg0DyLNGk11U5eiFMmFadGjRuseRZpFWvR0WHKpOJUXvGvqu/Y0ZrnkSbN2lpFP7/lXkJC2Ah7nQMGZ/OP7MljckWcrTh+3HzxgLNOERwRqHEaIWyXFCxC2Ah3Hx9cHEwAnEyUgsVW5OUVkpmVB0DjxkEapxHCdknBIoSN8A8xoncw/zs9PUvbMKJSTp7OBCA8zE/jJELYLilYhLARYY3Ny/KXlCpycvI1TiMqIzHRvIxDsNFT4yRC2C4pWISwEWENzfMfsvNKNE4iKutgQhIAfgYdvqEh2oYRwkZJwSKEjQgKNd/t93yOLMtvaxKPmFe79dSX0qhda43TCGGbpGARwkYEBvkAcO58nsZJRGVZ1mJxLCWijRQsQlSFFCxC2Ah/fy8Azp6VZfltzdGj5jksHnoTzTpev2DpNPgOXlz4nRQ3QvyFFCxC2Ag/X3dArhCyRamp5zh8OAV7O+jYKghXT4+r9jX4+hD70niMjRsyasYbeAcar9pXiPpEChYhbISXhwsAqckZGicRVfFr3FYAGhqKrjlycuuTj+DsZr5nlMHXh9Ez38TR2alWMgphzaRgEcJGeLrrAUg+maZxElEVv/22A4AIQxGN2rW6Yp/AqMbcNOROAL6ZMImczHMEN4/iticfrbWcQlgrKViEsAHOBnfczPUKJ4+laBtGVMmGDfu5mFeEm07R85b2V+wz6MVnsHdwYPfy39m1dAVx73wAQMO2MbUZVQirJAWLEDbAw88XV50C5JSQrSouLmHVmn0AdGkbioOjY5nnO98VS1TnjhQXFvLbjI8ASDt6DACfYLkHkRBSsAhhAzwD/HG9dB+hM2cuaJxGVNWC+esAaOJt4h+T/4mTqysAPiFBDHrxaUCx5P1POHfaPIoW5u/MyMgsujZxlXksot7TaR1ACHF9UW2jcbj058XZs1Kw2KqlS3cCEOBSQp+7bqNxh3Yk79nN2H+0pnFgIR66XIb+px+3bFhDcnImn3/yGD5OpbTwLsA70MiZ4yc03gMhtCMFixA2oHmHFgBczC+moKBI4zSiqtLTz7N+fQI9ekRzmzGTxaYAHu3WlVD3YqAUsCc83J+ly/7N7yt307x5KGBecM4nOFAKFlGvySkhIWxAZHQkAJmZORonETdqzEMzycjIJsTTnvtCUgh1LyavoIQnJvxAh/bjOX48ncjIIB4bN9DyGjdHhTFC7kEk6jcpWISwcm7eXkSFme/yeyBB/sK2dUePpnLnHf8hP78QT4MzhYXF3DlwMh+//S1//JHIbbdOspz2+/77tVzMLwagectGWsYWQnNSsAhh5UJbNifM3XwaKH75To3TiOqwdeshhg19g02bDjD8H9NYvfpPy3NHjqTQs8dLvPjCFzw29r8kp5tH1RpHBmsVVwirIHNYhLByjVq3IMTN/Ff2ypV7NE4jqsuyZTtZtuzKBeihQ6c5dOg0ACdPZhAV4UNEmF9txhPC6sgIixBWrmefdjjaw7kL+ezbJ6eE6pujR5IBCDJe/f5DQtQHUrAIYeU6tw8DYP3GgyilNE4jatuBhOMANPDQo3OStVhE/SUFixBWzDvQSJMG5jO3vy7cpHEaoYXD+82jap76UnyC5M7Nov6SgkUIK+Lq6YGzu5vlcfNOMRhdSgCIv8p8B1G3HTtmvtmlp74U7yBZol/UXzLpVggr4R1o5LmfvsLRyYk/fovnQvoZRj85DHu7ElIy8jh16qzWEYUGTpw4g0kpHO3taNy8IYc2btE6khCakIJFCCtx6xOP4Ophnlh509A78XEq4dbwbACW/bZVy2hCQ0VFJWReKKSBlzPNoiNYonUgITQip4SEsAKBUY1pf+dtAPw4aSqFCZsZHn4OT72JxMRU/v3KXI0TCi0lp5kXkmvcJEjjJEJoRwoWIazA7eMfx97enl1LV2DIPsn42HBc9PasW7ePzje9IKeD6rkTSebjHxbqq3ESIbQjBYsQGoto05rmPbpSWlzC+a2rWLT4FZyd9SxcuIX+/V4hMzNb64hCY0cOnwIgyN+gcRIhtCMFixAaa9iuNQAnt2zkmy/G4erqxG+/beeeu9+kqKhE43TCGuz/MxEAP4MjvqFyE0RRP0nBIoTGDL4+APSIcsbDw5WdO49y17A3pFgRFof+shZL2wF9NU4jhDakYBFCYwY/X/T2JgZ0DgXg1f/8QGFhscaphDU5ejQVAIOjiW6Db9U4jRDakIJFCI0ZfH1o5VOAu5ue/ftPEhe3TetIwsqcPXuBPXuOY2cHXVoZCYxqonUkIWqdFCxCaMyrgQ/tfAsAeOftX+R+QeKKfpq/AYAmnoW0G9hP4zRC1L5KFyw9evRg8eLFJCcno5QiNjb2mv2HDBlCfHw8Z86c4cKFC2zatIn+/fuX6TNy5EiUUuWak9zoS9QDHaK8cXc0kZp2nu++W6t1HGGlfv7ZfC+pcLdibrqzH3Z2dhonEqJ2VbpgcXNzY8+ePTz55JMV6t+zZ09WrFjBwIEDad++PatXryYuLo42bdqU6XfhwgWMRmOZVlhYWNl4QtgUB52OIB89AEuX7pSJtuKqDh48zcGDp3Gwh/aR3rTqd7PWkYSoVZVemn/ZsmUsW7aswv2fffbZMo9ffvllYmNjufPOO9m9e7dlu1KK9PT0ysYRwqa5+5pHVwCOH0vVOI2wdj//tJGX/3U3TTyKuPvf/0fakUTOHD+hdSwhakWtz2Gxs7PDYDBw7ty5Mtvd3d1JSkri1KlTVxyB+Tu9Xo/BYCjThLA1Bl9f3HXmguX0qQyN0whr97/TQgV4eLgwasY0nNxcNU4lRO2o9YLl+eefx83NjXnz5lm2HTx4kFGjRjFo0CBGjBhBQUEBGzdupEmTq8+EnzhxItnZ2ZaWnJxcG/GFqFYGXx8Ml0ZYTp+WgkVc2+7dxzh6NAW9zp4REZl0bBVErwdHaB1LiFqjqtqUUio2NrbC/e+55x6Vm5urbrnllmv2s7OzU7t27VIzZ868ah+9Xq8MBoOlBQUFKaWUMhgMVd4fadJqu3UacqfKL/lVmVScato0RPM80qy/devWQp04+YUyqThlUnFqxoKr/56UJs0WmsFgqNDnd62NsAwfPpzZs2czfPhwfv/992v2VUqxfft2IiMjr9qnqKiInJycMk0IWxMQ4o+TgwIgOTlT4zTCFmzcuJ+W0U+wepN5uf6O0f4aJxKidtRKwXLPPffw5Zdfcu+997JkyZIKvaZNmzakpsokRFG3hUUYAcjNKyY3N1/jNMJW5OTkE//7XgC8PZw1TiNE7aj0VUJubm5l5pY0bNiQmJgYzp07x6lTp5g6dSrBwcGMHDkSMBcrX3/9Nc888wxbtmwhICAAgPz8fLKzzXehnTRpElu2bOHIkSN4eHjw9NNP06ZNG5544onq2EchrFZoWAMAzmTmapxE2JrTJ8x/0Hm4OWqcRIjaUekRlg4dOrB7927LJcnvvfceu3fv5j//+Q8AgYGBhIWFWfqPHTsWR0dHPvroI9LS0ixt5syZlj5eXl7MmjWLAwcOEB8fT3BwMD179mT79u03uHtCWLegQG8A0tIvaJxE2JpTx8wXGrjp7dC7yCiLqB80n3BTHa2ik3akSbOmtvzAz8qk4tQPC6ZonkWabbVGjYzKpOJUQUmc8g0J1jyPNGlVbVY36VYIUZ6ftwsAJ5LSNE4ibM3Zs+ZROb0D+IcaNU4jRM2TgkUIjeicnPB0Nf8IHj8q6wiJysnJyae4VAEQ1jhE4zRC1DwpWITQiMHXG8OlVW6lYBFVkZ1XDEBoRKDGSYSoeVKwCKERg5+v5T5CssqtqIrz2QUABIY00DiJEDVPChYhNNIgyB8XnXlI//RpWTROVN65rDwAjIG+GicRouZJwSKERhpGhgJQUGTiwoWLGqcRtujyxFv/Bp4aJxGi5knBIoRGIhqar+zIvDSsL0RlpadnAeDr465xEiFqnhQsQmgkJNQPgDMZMroiqibl9FkAvDxl4ThR90nBIoQG7OzsaNg4GJBVbkXVJZ9MB8DD1RF7BweN0whRs6RgEUID/ceNITTMfJfdg3uPapxG2KrkJPP9hFx1CndfH43TCFGzpGARogYMffkFJi6Zj3/D8HLPte7fh/7jxuDjVArA4X2JtR1P1BHp6ecBcNGZ8PCTgkXUbVKwCFHN7B0c6DT4DvxCQxg9802c3d0sz3kHGrnn1ZcxOJYS7FoEwKpVf2oVVdi4y1cJuepMGPz8NE4jRM2SgkWIatYgIgxHZycA/BuGc9+0f2Nnb/5Ru2vSP3FydSUo/wQO9nasWrWHI0dStIwrbNiZM+aCxdEe/EMCNE4jRM3SaR1AiLomuHkUAOdTUrmlhRv3P9SCzs0/54cf1tKse2dKCguICTSfDvps1nItowobd/FiAUXFpegdHQhtGKR1HCFqlIywCFHNgptF4edUwkOtCukZUkQDl1LG9GnAf159EFBc2LSc4CBvMjKyWbBgs9ZxhY3LyikEIChYlucXdZsULEJUs+BmUQwMzSE80J2MjGx+XrgNgC4BeTwQfoZHY80jMF99+TtFRSVaRhV1QNbl5fmDZHl+UbdJwSJENYtqHYmvcykmk4mY1k/xjyGv8ugjH1BUVIKfwYGwsAaYTCY++0xOB4kbdzYjGwB/fy9tgwhRw2QOixDVyDvISCN/Z6CYhISTpKaeA+Dzz+NZuHALMTENad48lMTEVA4fTtY2rKgT0tPOAY0wBvqg0+spKSrSOpIQNUIKFiGqUXCzpgS5mk/zbN50sMxzGRnZ/P77Hn7/fY8W0UQdlXTkJNABD3c9zXt2Ze/KNVpHEqJGyCkhIapRcPMoAl2LAdi8+eB1egtx485eurTZ1cFE+ztu0ziNEDVHChYhqlFYiygCXC6NsEjBImrB5cXjXHQmmvfsiqunh8aJhKgZckpIiGrUvmMUOnvIOp8nc1RErbi8eJyuOB+doydtbutLQW4uLXp2Y/138zmxZ5/GCYWoHlKwCFFN3H28iQoxABfZvPmA1nFEPXHqVAYAfm7gaK+InfAMOr0egJjb+rLhu/ksff8TivILtIwpxA2TU0JCVJPmPbpY5q9s2rBf4zSivti7N4kjR1JwcdLRyD0fnV5PcUEhBzZsxt7enp73382dzz+ldUwhbpgULEJUk1Z9bybw0vyVTZtkhEXUnrlfrwKgwYWjbP5pIW8NuZfPxz3HvClvANCofRsN0wlRPaRgEaIaOLu70aFXBzz0JkpLTWzffkTrSKIe+eabNQC0jzayZfZsvJ0Uzz8/hJS95vkrPsFynyFh+2QOixDVoEWvbkR4m//9xx+JXLwo8wVE7UlKSmft2n306tWSN98axa23tsPHx4C3z3xMJhN6F2cMvj7kZJ7TOqoQVSYjLEJUg9b9+hDqZp6/skoWhhMauHxaaMSIXvj4GAAYObIPOWfOAOATIqMswrZJwSLEDdK7uNCs202EuV8qWFb9qXEiUR/99NNG8vLMd26Oi9vG2bMXCA72JUCZR1XktJCwdVKwCHGDmnXvTAMPRwyOJgoLi9kgVwgJDWRn5zH8H9N4dvxnDB3yumXEpUOY+cy/r4ywCBsnBYsQNyioaSRhbuYbzm3efJD8/EKNE4n6asmSHcycuZjSUhOzZ68AoF0TL9x0JhlhETZPChYhbpCXMYBQd5m/IqzLgQOn2LhxPw4O9rTwKpARFmHzpGAR4gb5BAb8b8KtzF8RVuSrL38HoJGhSEZYhM2TgkWIG9SsaSAuOkXuxUK2bTusdRwhLC7fgNPXuRQvYwPsdQ4aJxKi6ipdsPTo0YPFixeTnJyMUorY2NjrvqZnz57s2LGD/Px8EhMTGTt2bLk+Q4cOJSEhgYKCAhISEhg8eHBlowlR6+zs7WkWZr477uYthygpKdU4kRD/c+hQMkVFxTg5KLyc7fA2GrWOJESVVbpgcXNzY8+ePTz55JMV6h8REcGSJUtYv349bdu2ZerUqbz//vsMHTrU0qdz5878+OOPzJ07l5iYGObOncu8efPo1KlTZeMJUasMvj54OdsBkLD3uMZphCirpKSUgwdPA+DnXCJrsQibp6ralFIqNjb2mn2mTZum9u/fX2bbxx9/rDZt2mR5/MMPP6glS5aU6bN06VL13XffVTiLwWBQSillMBiqvD/SpFW2hbWOVvuzliuTilPPPnvtnwVp0rRoX899TplUnFqX9rvqfJd8j0qzvlbRz+8an8PSpUsX4uPjy2xbvnw5HTp0QKfTXbNP165dazqeEDfEJ9CIu6MJgOTkTI3TCFHevr0nAPBzKpGJt8Km1fi9hIxGI+np6WW2paen4+joiJ+fH2lpaVftY7zG+Va9Xo+Tk5PlscFgqN7gQlSAV6ARw6WC5fRpKViE9dm7NwmABs6lcmmzsGm1cpWQUqrMYzs7u3Lbr9Tn79v+auLEiWRnZ1tacnJyNSYWomK8AwNw08kIi7Beey+NsHg7lRLeqhnjZn/IqxuWE31zD42TCVE5NV6wXB5B+St/f3+Ki4vJzMy8Zp+/j7r81RtvvIGHh4elBQcHV394Ia4jtGEwuks/RSkpcidcYX1On87g/IU87O0gsrGRJp3a4+rpwaj33qDbPcO0jidEhdV4wbJ582b69etXZlv//v3ZsWMHJSUl1+yzadOmq75vUVEROTk5ZZoQtS0sIgCAjHO5FBeXaJxGiCvbd+m0kJ9TCX/8tpxtC37F3sGBoS+/QPs7btM2nBAVVKXLmmNiYoiJiQGgYcOGxMTEEBoaCsDUqVP56quvLP0/+eQTwsPDeffdd2nWrBmjR49mzJgxvPPOO5Y+M2fOpH///kyYMIGmTZsyYcIE+vbty4wZM25w94SoWcHBvgCkpGRpnESIq/tzz3EAji9bxLcvTeHHSa+z8YefAWjRu7uW0YSolEpdftSrVy91JXPmzFGAmjNnjlq9enWZ1/Ts2VPt3LlTFRQUqGPHjqmxY8eWe99hw4apAwcOqMLCQrV//341ZMiQGrksSpq06mpOrq4q/vQqZVJxavGvkzXPI03a1drYsbcpk4pTvy2ZYtnWsk9P9e7ezeqZ72Zrnk9a/W4V/fyu9FVCa9eutUyavZLRo0eX27Zu3Trat29/zff9+eef+fnnnysbRwjNeAUGWC5pPnXijMZphLi6P/9MAqB16wjLBQ3nklMB8A6S1W+FbZB7CQlRRd5/KVjkCiFhzfbuPUF+fiHBwb5MmzYSgKzUNMC8WrOjs9O1Xi6EVZCCRYgq8g4M/MsaLBkapxHi6nJz8xn32EcAvDhhGM8+G0t+dg75ObkAeAfKKIuwflKwCFFFXoEBuMsaLMJGfP31Kia+ZL4g4t3pD3P4yKd09zuPq86ET3CgxumEuL4aX+lWiLrKOzAAN1nlVtiQN9/8CTc3J16cMIwmTYJoAhgy8/AOkoJFWD8ZYRGiioIbh+PsoAAZYRG2Y9Kkb/HzvZfXX/sRAF+nUhlhETZBChYhqsBe50Bk8wgAsrPzyc3N1zaQEJVw8WIBy5b9AYCnvlTmsAibIAWLEFVgbNwILzcHAE6fPqtxGiEqLzHRfFmzwdGEn4ywCBsgBYsQVRDSopnlkmaZvyJsUVpaFvn5RdjbQURjGWER1k8KFiGqIDS6mazBImze8ePmG8yGBHigc5K1WIR1k4JFiCoIadHsf5c0yxoswkYdOZIMXJ7HEqBxGiGuTQoWISrJXudAYFRjPPSlgIywCNt1LNG82q2n3oSPXNosrJwULEJUkE6vBy5PuHUk3K0YgG3bjmgZS4gqO3bMXLB46Uvxlom3wsrJwnFCVECDiDCen/81BzZs5tCmrbT2ycfBHjZs2M+uXYlaxxOiShIvj7A4lsoIi7B6UrAIUQHRvXvg6OxE6769adqpLa19CgB4f+ZijZMJUXWXL2321JfiI3dtFlZOTgkJUQHhraMt/24b5oSrTpF6JpsFCzZrmEqIG5OUdAaTSaF3gLBGwVrHEeKapGARogLCY1oCkHdoF50a5AEw67MVlJaatIwlxA0pLi4hJe08ABER/tqGEeI65JSQENfhFeCPT4AftwReILplKGAiO7eI99/9SetoQtywxMRUQoK8CfJ3x0Gno7SkROtIQlyRjLAIcR1hraOJ9i4g2sd8VdB3362lXcwTZGXlapxMiBt35NApALydFJ4BDTROI8TVyQiLENcR3rolLbwKAZjw4he8884CjRMJUX3+txZLKV7GAM4lp2qcSIgrkxEWIa6jY/cYAl1LKCk1MXfuaq3jCFGtEssULDKPRVgvKViEuAZ7nQN9OoUCsHrtftLTz2sbSIhqdvnSZi99KV5GubRZWC8pWIS4huCmkbT0M09C/GLWEo3TCFH9Lt8A0c1R4R8iBYuwXlKwCHENg4b3wUNvIr+wlEWLtmodR4hql5WVy8W8IgAaNZG1WIT1koJFiGu4c2A7ANbtPE1BQZHGaYSoGadSsgCIiJCrhIT1koJFiGtoGekHwJIlOzVOIkTNOZF0BoDgQC9tgwhxDVKwCHEVPg28CPRwAGDpwrUapxGi5lxei8XPQ4/exUXjNEJcmRQsQlzFgKG9sbODc/lwNOGY1nGEqDGJR5IBubRZWDcpWIS4ipv7muevHD6ZrXESIWrW5SuFPBxNeBkDNE4jxJVJwSLEVXRo2xCAHXuStA0iRA1LSrpUsFxa7VYIayQFixBXYG9vT1SYBwBrVuzSOI0QNSvp0qRbV50iMCJI4zRCXJkULEJcQes2jXB2tKeo1I61SzdqHUeIGpWdnUf2RfNl+02iQjVOI8SVScEixBXcNqg7AKezIVNuBifqgeTUCwA0bCinhIR1krs1C3GJg6Mjw6dM5Hz6GXre3BSAA8fOaZxKiNpx4sQZmjdpQGiwt9ZRhLgiKViEuCTm1j70GtqfTg3yaeVdAMC2nUc1TiVE7Ug8mgK3RGP0c9c6ihBXVKVTQuPGjePYsWPk5+ezY8cOunfvftW+c+bMQSlVru3bt8/SZ+TIkVfs4+TkVJV4QlTJoNF38WDkedr4FuBgD0ez9SxZuEnrWELUikP7kwDwdrXD1dND2zBCXEGlC5bhw4czY8YMXn/9ddq2bcv69etZunQpoaFXnqj1zDPPYDQaLS0kJITMzEzmz59fpt+FCxfK9DMajRQWFlZtr4SopMDIRjx8WwjODoqDR9L4fEsRPx3UcXi7XCEk6odjR1MA8HAsxSc4UOM0QlyZqkzbsmWL+uijj8ps279/v5o6dWqFXh8bG6tKS0tVWFiYZdvIkSNVVlZWpXL8vRkMBqWUUgaD4YbeR1r9bHN+e0+ZVJy6WLBQhYY2UPY6B+Xs7qZ5LmnSaqs1bx6qTCpO5ZX8qlr3u1nzPNLqT6vo53elRlgcHR1p37498fHxZbbHx8fTtWvXCr3HmDFjWLlyJSdPniyz3d3dnaSkJE6dOkVcXBxt2rSpTDQhqqxl64bcd1sTAF57dwmnTp3FVFJKQe5FjZMJUXtOnDCvxeLsoAiPCtc4jRDlVapg8fPzQ6fTkZ6eXmZ7eno6RqPxuq83Go0MGDCAzz//vMz2gwcPMmrUKAYNGsSIESMoKChg48aNNGnS5KrvpdfrMRgMZZoQVTH5zbHo7OHw2VKm/eszreMIoYm8vELOZZsnmzdr2UjjNEKUV6VJt0qpMo/t7OzKbbuSUaNGcf78eRYuXFhm+9atW/n222/5888/2bBhA8OHD+fw4cM89dRTV32viRMnkp2dbWnJyclV2RVRz+kcddx6cwsAPv9qXYW+j4Woq46fyASgebNgjZMIUV6lCpaMjAxKSkrKjab4+/uXG3W5koceeoi5c+dSXFx8zX5KKbZv305kZORV+7zxxht4eHhYWnCw/ICJyhs5/l7cnezIK4aPX5XRFVG/HThwGoCGoT4aJxGivEoVLMXFxezcuZN+/fqV2d6vXz82bbr25Z+9evUiMjKS2bNnV+hrtWnThtTUq68wWlRURE5OTpkmRGU9NPYOANZuS+JitnwPifpt9x+HAQjxc8bewUHjNEKUV6nZvMOHD1eFhYVq9OjRqlmzZmr69OkqJyfHctXP1KlT1VdffVXudV9//bXavHnzFd9z0qRJqn///qphw4YqJiZGzZ49WxUVFamOHTtW+yxjadIut+geXdTF4l+VScWp22O7ap5HmjStW+/erZRJxalzhb8pn+BAzfNIqx+top/flV7pdt68efj6+jJp0iQCAwPZt28fAwcOtFz1ExgYSFhYWJnXeHh4MGzYMJ555pkrvqeXlxezZs3CaDRy4cIFdu3aRc+ePdm+fXtl4wlxXY06tOXm0ffR/9Z2uOguciG3iGW/btE6lhCa27fvBACejiaCm0RwTu6jJayIHebKxeYZDAays7Px8PCQ00PiqrwC/Jm49CccHXXcGZZDY48iPv/idx4dM0PraEJYhQt5CzC46Bg1YQFfv/2F1nFEPVDRz2+5W7OoV7rcPRQnvY5bvFJo7FFEaWkpsz7+TetYQliNE6nmD4zWMQ01TiJEWVKwiHpD5+REl7sGMTg8m1YhThQUFPGPu6axY8cRraMJYTWOHjMvIBfVRJbnF9ZFChZRb7Qd0JeoYHfC3Iu5eLGAW/tPYuFCmbsixF8lJJjnsUSEeGkbRIi/kYJF1BvdR/yDlt7mlTy/+3YN69cnaJxICOuza/shAIJ8nDROIkRZUrCIeiE8piWNWkbSxMN8B/AvvlipcSIhrNOWdbsA8HYGnwBfjdMI8T+VvqxZCFvUMfZ2ojyL0DvAgQOn2Lr1kNaRhLBKKafOklcErnro/49bySjSk7BmPamHE7WOJuo5GWERdZ6DoyMxt/Yh+tLpoDlfrNA4kRDWLSXLPBL5+MtjGPDUWIa9/KLGiYSQgkXUA817dCXC35Ug1xJKSkqZO3e11pGEsGq7DpivFOpuzOOO0Gwat4zEzl4+LoS25DtQ1Hl9R9zBoPBsABYu3EJ6+nltAwlh5Z4ePZVvfztISUkpkZ5FDGxcgl9YiNaxRD0nBYuo04yhRibe1wKDo4mjx9J5bOx/tY4khNVLTzrNA3e8yIDbJgMQ7l5MaPMojVOJ+k4KFlGnvffxMzRwMZGdb6Jfn//j3Dm5bYMQFbV69V7yCkrQOyi69G6ndRxRz0nBIuq0nt2aAjB99iZOnDijcRohbIvJZGLvoXQAOneWERahLSlYRJ0V1TqKQC9HAL768EeN0whhmzZtPABA6yh/jZOI+k4KFlFnjXhsGADJ54o4cShJ2zBC2Kj43zYDEO7jgJu3l7ZhRL0mBYuos/re2hGAzVsOa5xECNu1Ye2flJoUHnoTbbvLPBahHSlYRJ0U0qIZTQKdAVj0oyzDL0RVXbxYwKmz+QD0va2TxmlEfSYFi6hT7HUO+DcM57aH76WBcykAq1bs0jiVELZt9/4UAG7q1ETjJKI+k3sJiTrDxcODF3/5Bs+ABkS4F2Fvl83J01mkpp7TOpoQNm3DugQG39yE6MZ+WkcR9ZiMsIg6o3GHNngGNKCkqAiPvDQAfl+xU+NUQti+5YvWAxDk5YDB013jNKK+koJF1BlBUebh6qQNawgkA4B1a/dpGUmIOiFh1yHyisHeDjr36ah1HFFPScEi6oygZlG0883jnbGtadEijIsXC1i5crfWsYSoE1IzzRNvb+rZVuMkor6SgkXUGR06RtErMA8XJx0bN+6nd6+JJCdnah1LiDrh6HHzqGWrmMYaJxH1lRQsok5wNrjToqEXACt//5Me3f/Jzp1HtQ0lRB2SsPcYAE0ay4q3QhtSsIg6IahpJEaXYgDWrNqjcRoh6p4dm/YCEOznioNOLjAVtU8KFlEnBDeNJNC1BICtWw9pnEaIumfbevMfAj7OJgIjG2mcRtRHUrCIOqF5uxZ46k2YTIrt249oHUeIOicp6QzFJQqdPXTo1UHrOKIekoJF1AmdOkUCcOxkJtnZeRqnEaLuMZlMJJ/JBaB9l5YapxH1kRQswuY56HQ0b2JegXPLloMapxGi7jp8NBWA6OhwjZOI+kgKFmHz/BuFE+yuAFi/are2YYSow/7cZT7d2jDMB3sHB43TiPpGChZh80KaNyXAxTzhdssWmXArRE3ZtfUAAH6u4N9QRllE7ZKCRdi8zr3a4eSgyC8sISHhpNZxhKiz9u83/3z5OJUS0ba1xmlEfSMFi7B5Xbo1ByDhUBomk0njNELUXYcPp2AyKVx0ig63dNU6jqhnpGARNs3R2YnmjcwTbjeu26txGiHqtvz8QpJTsgDo2C0Ge53MYxG1RwoWYdNCo5sT5FYKwNrf/9A4jRB1355d5ltehPo6Et5aLm8WtadKBcu4ceM4duwY+fn57Nixg+7du1+1b69evVBKlWtNmzYt02/o0KEkJCRQUFBAQkICgwcPrko0Uc807dQGX2dzwbJ162GN0whR923aZJ54G+RaTLNunTVOI+qTShcsw4cPZ8aMGbz++uu0bduW9evXs3TpUkJDQ6/5uqioKIxGo6UdOfK/1Ug7d+7Mjz/+yNy5c4mJiWHu3LnMmzePTp06VX6PRL3S65Z22NtBeuZFUlPPaR1HiDpvw4b9AAS7FtO0200apxH1japM27Jli/roo4/KbNu/f7+aOnXqFfv36tVLKaWUp6fnVd/zhx9+UEuWLCmzbenSpeq7776rcC6DwaCUUspgMFRqf6TZdluR+KsyqTgVt/zK33/SpEmr3ubsrFcFhQuUScWpzw9tUO4+3ppnkmbbraKf35UaYXF0dKR9+/bEx8eX2R4fH0/XrteeMb5r1y5SUlJYuXIlvXv3LvNcly5dyr3n8uXLr/ueon7zDQkm3M8RgLW/79Q4jRD1Q0FBETt2mEfIg1yLieoqI+GidlSqYPHz80On05Genl5me3p6Okaj8YqvSU1N5ZFHHmHYsGEMHTqUQ4cO8fvvv9OjRw9LH6PRWKn3BNDr9RgMhjJN1C/hbVoS6FIMwOaNBzROI0T9senSz1uwazGN27fVOI2oL3RVeZFSqsxjOzu7ctsuO3z4MIcP/28y5JYtWwgNDeWFF15g/fr1VXpPgIkTJzJlypQqpBd1RYeeHXBzVJSUmvjjj0St4whRb2zYsJ8XXhxKkGsJ3kFX/8NSiOpUqRGWjIwMSkpKyo18+Pv7lxshuZYtW7YQGRlpeZyWllbp93zjjTfw8PCwtODg4Ap/fWH7HHQ6bu7fEYDDx86Sn1+ocSIh6o/LVwr5OpdiDPHXOI2oLypVsBQXF7Nz50769etXZnu/fv3YtGlThd+nbdu2pKamWh5v3ry53Hv279//mu9ZVFRETk5OmSbqjw6xA2kS7AbI+itC1LaMjGyOJKYBEN3IV+M0or6o9Cmh6dOnM3fuXHbs2MHmzZt59NFHCQsL45NPPgFg6tSpBAcHM3LkSACeeeYZkpKSSEhIQK/Xc//993PXXXcxdOhQy3vOnDmTdevWMWHCBBYtWkRsbCx9+/a95vouov6y1zlwy8MjCXEzz1/ZsknmrwhR2zasTyCysZEIXwec3d0oyL2odSRRD1T6EqRx48ap48ePq4KCArVjxw7Vo0cPy3Nz5sxRq1evtjx+8cUX1ZEjR1ReXp7KzMxU69atUwMGDCj3nsOGDVMHDhxQhYWFav/+/WrIkCE1clmUNNtvHWMHqrlH1iuTilP5Bb8oPz8PzTNJk1bf2rhxA5VJxanDF5argMYNNc8jzXZbJT6/tQ9byzsszYabo7OTmvjbfLUnM16ZVJz65tsXNM8kTVp9bAMHdlAmFafS8paopl1v0jyPNNttNbIOixBaGz5lIkERQTT1NE+y/fSTpRonEqJ+Sko6A4CHownPAJl4K2qeFCzCZvR6cATtbr+VKEM+egfYv/8k69cnaB1LiHrpxAlzweKsUwRFBGmcRtQHUrAImxDSohl3PPcEDnaKRioFgFmfLtM4lRD118WLBWTnmkc6G0eGaJxG1AdSsAibcNOwQbjp7RjgdZLGYd7k5ubz9dertI4lRL2Wkp4NQHi4nBISNU8KFmH17HUOdBnYmxGNzxMZ7Mb587kMjn2N8+flMkohtHTyVAYAIcHeGicR9YEULMLqRXXuSMcIJzz1Jo4fT6dL5xdZtepPrWMJUe8dP2ZeANTYwF3jJKI+kIJFWL02t/UjwlAEwLvv/MKhQ6c1TiSEADhy4CQAPm4OOLm5apxG1HVSsAirpnNyon3/ngS5lgCwbJkswy+EtUg8kgxcurTZv4HGaURdJwWLsGrNu3cm0t8RBzs4fDiZY8fStI4khLjk8qXNBn0pXkaZeCtqlhQswqq1HdifCIP5nkHLZXRFCKtyuWBx0ykCQmUtFlGzpGARVsvJ1ZUWPbsS4W6ev7J06U6NEwkh/iorK5e8QvPp2sjm4RqnEXWdFCzCakX36YHRU4eH3kRBQRFr1+7TOpIQ4m/SM8zLCzRsHKhxElHXScEirFbb2/oRfml0Zc2aveTnF2qcSAjxd8mp5wEIC5VJt6JmScEirJKrpwdNu95ElKe5YFkmp4OEsEonLt0EMTjQU+Mkoq6TgkVYpVZ9e+PvDoGuJRQXl/D99+u0jiSEuILEI+Z1kQJ83TROIuo6KViEVWo7oB/R3gUAxMVt4+zZCxonEkJcyYE/jwLg5WKHV4Bc2ixqjhQswuoYIxvTtHN7mnuZ56x8MXuFxomEEFdzLNG8PL+n3kRA44YapxF1mRQswurcPPo+GhuKcNUpkpMzWb5c1l8RwlodP54OgLujiZCmjTVOI+oyKViEVfEONNJ2QF9a+5hPB3315e+Ulpo0TiWEuJqMjGwu5psXd2zZPkrjNKIuk4JFWJWbR43gtvACwtyLKSkp5Ysv5HSQENbuVHIWAM2bR2gbRNRpUrAIq+Hi4cHk5/rT0ruQ0lITIx98T+4dJIQNOHo0BYCICJl0K2qOFCzCatw6pDet/EooNSnuHfE233+/VutIQogKOLD3OAANPB1x9/XWOI2oq6RgEVajecsIAI6l5TN//gZtwwghKuzIYfNaLF76UgIaRmgbRtRZUrAIqxHeyHy31/SMXI2TCCEq4+hR86XNXvpSubRZ1BgpWITVCA7yASAl5ZzGSYQQlZGYaJ5r5uFoIrCJFCyiZkjBIqxGgL8HACdPpGucRAhRGcnJmRQVl+JgD01bNtE6jqijpGARVqOBtwsAx48ma5xECFEZJpOJU6czAYhqFqJxGlFXScEirIKdvT1erg4AHE44pnEaIURlHT5knngb1MAdFw8PjdOIukgKFmEVPBr44a5XABzaJwWLELbm6GHzyKinvpTh/56IvYODxolEXSMFi7AKkS0a42AHJqVITcnUOo4QopIuXynkqSumdd/ejJg6CTt7+YgR1Ue+m4RViIw2X1mQnVcq9w4SwgYlXrprsykzmdLiEtoN7M/Ql1/QOJWoS6RgEVahUZR5ol7G+QKNkwghquLypc3BAQa+eWkyJpOJrsOH0HfsaI2TibpCChZhFcIjjACkn83ROIkQoiqOH0/DZDJhMLhy5s9dLJj6LgADnnyUjrEDNU4n6gIpWIRVCAw0338kWeavCGGTiopKOHjQfKXQvPkvcSA+npWzvgQgdsJ4uXJI3LAqFSzjxo3j2LFj5Ofns2PHDrp3737VvkOGDCE+Pp4zZ85w4cIFNm3aRP/+/cv0GTlyJEqpcs3Jyakq8YQNMja4tGjccVk0Tghb9cjDH5CVlUv37i1Yu24au3+aT/LBw7h4GLjl4Qe1jidsXKULluHDhzNjxgxef/112rZty/r161m6dCmhoaFX7N+zZ09WrFjBwIEDad++PatXryYuLo42bdqU6XfhwgWMRmOZVlhYWKWdErbH18tcnCYeOaVxEiFEVW3efJBePV8iOTmTli3Dmf3F0/w242MAut97F17GAI0TClunKtO2bNmiPvroozLb9u/fr6ZOnVrh99i3b5965ZVXLI9HjhypsrKyKpXj781gMCillDIYDDf0PtJqvzm7u6nMgt+UScWpW/q10zyPNGnSbqw1axai8vJ/ViYVp0aP7qse+/wD9e7ezeqe1/6leTZp1tcq+vldqREWR0dH2rdvT3x8fJnt8fHxdO3atULvYWdnh8Fg4Ny5sje4c3d3JykpiVOnTl1xBEbUXV6BRtwdzZcyH09M0TiNEOJGHTx4mkmvfAPA9PceZud33wLQ/s4BuHl5ahlN2LBKFSx+fn7odDrS08vOM0hPT8doNFboPZ5//nnc3NyYN2+eZdvBgwcZNWoUgwYNYsSIERQUFLBx40aaNLn6TbT0ej0Gg6FME7YpPDIcx0vficnJMulWiLpg+vRFbN58EE9PN6a8cBtpicext7cnok0rraMJG1WlSbdKqTKP7ezsym27knvuuYcpU6Zw9913c/bsWcv2rVu38u233/Lnn3+yYcMGhg8fzuHDh3nqqaeu+l4TJ04kOzvb0pKT5YZ5tiqyhXnRuJy8EgoLizVOI4SoDiaTiTEPzaSgoIgBA9oTgfkPXSlYRFVVqmDJyMigpKSk3GiKv79/uVGXvxs+fDizZ89m+PDh/P7779fsq5Ri+/btREZGXrXPG2+8gYeHh6UFBwdXfEeEVWkeYz7OZ7PyNE4ihKhOBw+eZvIk8+mg+/uG4q4rJVwKFlFFlSpYiouL2blzJ/369SuzvV+/fmzatOmqr7vnnnv48ssvuffee1myZEmFvlabNm1ITU296vNFRUXk5OSUacL22Nnb07JjSwBOJKVpnEYIUd2mT1/I1q2HMLg5cUtQLmHRLXDQ6bSOJWxUpWbzDh8+XBUWFqrRo0erZs2aqenTp6ucnBwVFhamADV16lT11VdfWfrfc889qqioSI0bN04FBARYmoeHh6XPpEmTVP/+/VXDhg1VTEyMmj17tioqKlIdO3as9lnG0qyrRbRprbacWalMKk59/OkTmueRJk1a9bfmzUNVYdECZVJxavahDSqsVQvNM0mznlYjVwkBzJs3j/HjxzNp0iR2795Nz549GThwICdPngQgMDCQsLAwS/+xY8fi6OjIRx99RFpamqXNnDnT0sfLy4tZs2Zx4MAB4uPjCQ4OpmfPnmzfvr2y8YSNadmnJ0Gu5nkr27ce1jiNEKImHDhwinXrEgCIcC8iom1rjRMJW6V5dVUdTUZYbLO9snS+KiqNUyYVpxo3DtQ8jzRp0mqmvfjiUGVScepo9jL14Luva55HmvW0GhthEaK6BDRuSMum/ujszZczX749vRCi7lm27A8AQt2KadJeRlhE5UnBIjTTsk9PQt3Mp4PWrNmrcRohRE3auzeJlJRzONpD8zAPfIIDtY4kbIwULKLWOTg60nfsaPo9OpqQSwXLWilYhKjzli/bCUC4oZiwli00TiNsjRQsolbZOzjwxJyPGPDkozi76AlwlhEWIeqLy6eFItyL8Azw1ziNsDVSsIhaZWzSiPCYlhTlF7Dv61k4OtiRkpLJ0aMyf0WIum7lyt2UmhR+zqU0aiKLfYrKkYJF1KqgpuZVbU/u20+wSyEAa9bs0zKSEKKWZGXlcvi4+X5h7WJCNU4jbI0ULKJWBTU139AyL/kE993fG4B1a6VgEaK+SDppLlgCjV7aBhE2RwoWUauCoiJx15UyZXQrmjQJIiUlk19+ufptHYQQdUtKSgYAfj7uGicRtkZu6CBqVUizxgxrmI2PkxcnTpzhlj4vk5GRrXUsIUQtST51BgAfTxeNkwhbIyMsotZ4+DcgMtgNH6dSMjKy6dnjJY4dkxseClGfnEhMAcDgYo/exVnjNMKWSMEiak1Q0yZEuJsvY/7tt+2cOnVW40RCiNp2+kQ6AK46EwY/P43TCFsiBYuoNUFRkYS5FwGwIn6XxmmEEFpIS8sCwE1nwtNfChZRcVKwiFrTtG0zAlxKAVixYre2YYQQmrhcsLg4KHwCGmicRtgSKVhErenR2bwGy/5DaZw9e0HjNEIILWRm5lBqUtjZQUgjWTxOVJwULKJW6JycaNXQA4Dly3dqnEYIoRWTycSFXPOp4bAIuQGiqDgpWEStCGzSiAiDecLtrwtl3RUh6rPMrDwAgkJkDouoOClYRK0Y9eQw3BwVhcUmNm7cr3UcIYSGzlxae8lo9NY4ibAlsnCcqFGhoQ2YPWc8fW9pDcC6LUcpKirROJUQQktpqeeAcBo08NA6irAhUrCIGvXRx+Poe0trShVsS8zj3iH/1jqSEEJjyZfWYPL1ktVuRcXJKSFRY7y83Ojfvx0APyZ68sA9b5CZKcvwC1HfnUxKBcDTTYfOyUnjNMJWSMEiakzskC44OjpwtsCBVUu3cGznbq0jCSGswOkk8y053HQmPBr4apxG2AopWESNeWLC/QAcPGvH4nc/0DiNEMJapKefB8BVp/CQ5flFBUnBImpEnwfuok2U+S+nd175lIwTpzROJISwFmlp5wHz/YRkhEVUlBQsotq5eXny9ORH0dnDydRs4r5aqHUkIYQVubw8v7ODwi8oQOM0wlZIwSKqXfd7/0F0AwXA158v0TiNEMLaXLhwkaJi833FwhoFaZxG2AopWES1amD04e3Jg2nsYV56+6efNmqcSAhhjc5dKAAgNMKocRJhK6RgEdXGaPRm95//paW/otSkeOmlr/jzzyStYwkhrNDlibeNosK0DSJshhQs4oZ5GQMY+vIL/LDsHQIbuHOhyJ6x/4rjrTd/0jqaEMJKnT6ZDkBQcAP0LrKAnLg+KVjEDbv92cf5x0OD6Bnjj1Lw414TX7/7ldaxhBBWLPnkGQDc9RDcLFLjNMIWSMEiboi7jzcx/Xpzc2AuAEs3JvHagy9SUlSkcTIhhDW7fKWQm6OJkOjmGqcRtkDuJSRuSMfYgbQNKMXfpZSsrFxGDXmZjAxZfl8IcW1JSeZTQt76UsJaSsEirk9GWESV2dnZcfO9g+nqnwfApFe+kWJFCFEhO3cmAuDvUkJoi2YapxG2QAoWUWWRnTsyqJ0XLjrF3r0n+OSTpVpHEkLYiP37T5KfX4STgyKqaTDO7m5aRxJWTgoWUWX3jX+AVt7mtRSeevITSktNGicSQtiK0lITu3aZR1kCXEoIbt5U40TC2knBIqqkdf8+PHR7E+zs4JfFO1i3bp/WkYQQNmbH9iOAuWAJlYm34jqqVLCMGzeOY8eOkZ+fz44dO+jevfs1+/fs2ZMdO3aQn59PYmIiY8eOLddn6NChJCQkUFBQQEJCAoMHD65KNFEL3H28+b/pzxPsVkJBUQnPPP6h1pGEEDZox46jwOWCReaxODg64uHfQOsYVk1Vpg0fPlwVFhaqMWPGqGbNmqn33ntP5eTkqNDQ0Cv2j4iIULm5ueq9995TzZo1U2PGjFGFhYVq6NChlj6dO3dWxcXF6qWXXlJNmzZVL730kioqKlKdOnWqcC6DwaCUUspgMFRqf6RduwU0ilC3PDJS3fbko2rgM+PUHc8+oZ6e+6k6m79EmVScen3qSM0zSpMmzTZbs2YhyqTiVGFpnJq0YqGyd3BQgHL19FDdRtylnFxdK/2edvb2qmWfnmrQhGeUlzFA832scG47OzV21kz19u4NqtUtvTTPU5utop/fdpf+UWFbtmzhjz/+4PHHH7ds279/PwsXLuT//u//yvWfNm0agwYNokWLFpZtH3/8MTExMXTt2hWAH374AQ8PDwYOHGjps3TpUrKysrj33nsrlMtgMJCdnY2Hhwc5OTmV2aVKc3B0xFRSglKV+q+rUQ6OjpQWF5ffrtPhFxZC+rEkyzZndzdi+veh3e23ond1YeWsL0lYvR4AY2RjdI46CvPyuWnoIHo+cDcOurJXv7fyLqBvcC7nsi7SKOIhsrPzanTfhBB1k729PVnnf8BgcOHrI158PPFttv0Sx2OzP6RJx3ZsW/grP77yOnZ2dvxj8kv4hgaze9nv7Fu9DnsHe/QuLmSeSsZUWoq9gwNdhg+h96h78QkKBOD4H3v476hxZX5Xt+jVnS7DB7Ph2/kc2rTVst3O3p5Og2/HLzyUFZ98QVG+eX7e1X63XqZzcuLWcQ+Rn5PLqtlzK7TfjTq0Jbp3dxp3bMcfvy1n3dc/cNOwQQyfMhGAgtyLzBjxEGeTTl75a+r1V13rys7OrsKfTf4Nw8lKTaO4oPCqfRwcHTGVlqJMNTdHsaKf35Vah8XR0ZH27dszbdq0Mtvj4+MtxcffdenShfj4+DLbli9fzpgxY9DpdJSUlNClSxfee++9cn3Gjx9/1Sx6vR4nJyfLY4PBUJldqRKdkxO9R91Ln4ce4MzxJD5//Hlyz2XV+Nf9u8Yd2pKdkWn5Zu754D3EvvgMf65YTdy7H3AuORUAJ1dXxs6aSXhMS75+4V/sWf477r7ePPvjl3gF+Fve76H33+LI1h34hgTjExxY7usd2LCZKI9CRt/eBIOrIzoHe8CO/0z5VooVIUSVmUwm/vgjkV69WhLgUsJtTz6Kk5srTTq2A6DDnQNY/cU3RMS04qZhgwBo0qk9d02aYHmP7IxMdi9dSZOb2hMU1QSAi+cvoNPradguho6xt7Nt4a8AeAcZue/NKTi7udGiZzd2L1tJwtoN2GFHrwdHENw8CgDfkGDmvvAvmna7ifvfepVzp1P4/l//IftsJoNfepYWPbux6cef2fjDL9w3bQqN2rcBoLS4hLVff0/PB+5h4DOPcS45lZN7E9i28DeO7diFnZ0d974xmXa332rJH9qiGV4B/nQafAcAOZnnMPj6MHL6VN6/72FL4XRZnzEPcPv4x0k5dIR9q9axb/U6kg8cJqhpJENffoGwVi1I3LGL3ctWsnflGvIulF1qwtngTrd7htHhzgH4Nwwn49Rp5r7wCqf3Hyx3fCLatGb0zGmYTCa2LfiVrT8vsny+aKFSIyyBgYGkpKTQtWtXNm/ebNk+ceJERo4cSbNm5c9BHjp0iC+//JI33njDsq1Lly5s2rSJwMBA0tLSKCwsZNSoUXz//feWPiNGjGDOnDk4OztfMcvkyZOZMmVKue01NcISGt2cB999vcwH+pnjJ/jkkae4kH62TL/8nBwyTp6u9gzOBnfuemUCbQf0o+DiRWaOGIPJZOLFX75Bp9cDUFxYyJafFrFj8RIGvfgMjTu0BeDsiVO8NXgEsRPG033EXWSlprHxh59x9TDQa+S9llGUwrx88nNycHZ343xqOnHTP2Roj1Bee/2BMln27TtBh/bjKSoqqfb9FELUH++88xDPPT+ELUklbM79352bL39wH9ywhdDoZrh5e7EnfhUNwkMJahpJaXEJptJSHJ3/94frxfMXWP7fz9i64Fe63T2UQS8+zcXzF3jzzru5eP4CYz97n6jOHclKTcPTvwH2Dg5lsuRlZ6N3dkan1/PnyjW06NUNnaMjACXFxRRezMPNy9PS32QyYW9vT3FhIY5OTphKS9kTv4q2A/qVeV9TaSk/v/Y23oFG+j46ipLiYnYtWUF+dg49H7jb0u/kvv18Of4lxn//BR4N/Di0aStfjn/JUrR4+Dfg/36bX2afAc6nn8HDz7fc/pQWl3B463a2/RLH3pVr8DIG8Mgn7xHQKKJMv5LiYuLe+YAN3823bGveoysPvvs6epf/fQabTCbmvvgKf8avuvoBrYIaGWG57O/DTdcbgrpS/79vr+x7vvHGG0yfPt3y2GAwkJycfP3wVRAY1ZhHP52Bq6cHWalprJo9lz5jHsC/YTjjv/+C7YuWkJ54vEyFnnsuiwvpZ3Fyc8XJzRVnNzccHHUk7d7L7uW/k554HJ3ekfycXE7u3V9muC2kRVMGTXgGnaOe5R99zpGt22l7Wz9ue+pRy1Cns5sbI6dPJTfrPDq9nsQduzCVlBLZuQM97htOj/uGA5Cfk4uppIQG4aHc+vgjdLlrMADfv/wqidv/wM7Ojj+WxNOqTy9SDicS4pzHqRPpJCScxMvLjU8+fZLhw82Tqme8t4gZMxahFKSmnqOkpLRG/r+FEPXH5Ym3nsWZQABgR9Kevfz0nzd5bv7XNOveGYDUI4l8889JmEpKLadEHHQ6mvXoQut+N5NzNpNVX8y1jCis/24eHQYNIKhpJI/P+Yj0Y0lEde5IUX4Bnzz8FE6urvQedS9u3l7onPSkHDzCik/n0KJnV+557RVa9+0NwJ74VTjoHGjZpxc6L09SDh1h44+/cPOo+/ALC+F8+hk+e+xZej04gk5D7rAUK0s/+JTkA4fpMGgAbW7ryz8mv2TZ5/lTprFj8RIAzqelM+jFpyktKWH+lGlcSD/LV8+9zKOfvkfTrjcxdtb7fP7EC+RnZ3Pb4w/j6OzE8T/2sPmnRbS8uQdNu3W2jJjvXraSNV99T5NO7Whza19CWjSlefcuNO/ehZTDR3H38sTDvwHn09JZ+sEsjm7dQew/x9O6380MmfgcDcJDWfzOB/QeeS+3PvEwDjodB9ZuZOvCX+k8LJbGHdqSuG1nLXxXXFmlCpaMjAxKSkowGo1ltvv7+5Oenn7F16SlpV2xf3FxMZmZmdfsc7X3BCgqKqKoFu5X0yAijLGz3sfV04Ok3XuZNXY8hXl57F+7kbGzZuLfMJxbHn7Q0r8wLx97B3vcfbxx9/Eu936N2rexDB9660swOJrYe/wC+9duJD87G48GDWh3e39LpfzoJ++Rl52Nq4cHABknT7PorZncNWkCxiaNACjKL+CHf73KueRUmna9ia53D6F5z26UFBXx+ePPE9aqBbETnqHvIyMBOLhhC2n797M4bhLt2zcmdtBrxH/yBU8/fSczZr4AwKpVe2jSJIiwsAYUF5fw9FOf8umny2rs/1kIUT9t3HiAkpJSmkcGkH/sBCvTvZk3aSrpx5LYtSSem+68lRIFP7/6FqZLfyRdnr9RWlJCwur1hDnncebMhTKnP0wlpcz/z5uM+/xDjE0aWX5fLnn/E8sI+LcvTQHAwcEek0mhlGL7oiX4hYfR95GRbP0ljvn/noYymWjVtzfuPt5s+yWO0pISti/8jeY9upC0ey+557L46dW38A0NJqJNK355/R22/LQIgAPrN5F+LIlbH38YgPhPvrAUKwBrv/6e0/sPUlJcTMoh82XeF5NPcOLHWbQYMZqINq14/qev2PjDz3QcfDsAcdM/5MSefeyMW4rOyYnG7duQdyGbUwkHAHAqyOKRfoFs/moJiYVexD40jBcG+OKqU7y/MpGZo54l+4z5zMBXz/0ffUbfy/3/HMuDYwcx4oF+nCr1BOxIXL2SiXcEcqHbEEaPmsG3SZnlTjHVpipNut25cydPPPGEZVtCQgKLFi266qTbO++8k+joaMu2jz76iDZt2pSZdGswGLj99tstfZYsWcL58+c1nXSrd3FmwqLv8Q4MgNREjv36E39sP8S2bYcpLTWhc3Iiund32tzWF2PjhuxZsYp9cb/i5uxAUFRjWsc0pFNMKB7ueiZMnMuZs7lE39yDVrf0oksLH/7RzgVHBzu2nnFh0xlXzIfD7I/flpOdkUn3EXeh0+vJyTzHurk/suG7+ThgIqRlcx6Z9QEOOh2/zfio3GQvdx9vsIPczCx0ej0Tl8wnINCPYmXHxw+OZdZ799KzZ0sAzpw5z0v//JJZnz2FTudgGeYEOHIkhfvufYcdO45Uy/+pEEL83X339ebz2U/j5OTIvoSTfDt3NYcPJzNyzK3cPqAd+4+d45YuT5ORkU3TpiHcfHMrkpMzKS4uZdLke+jcuRkmk4m33/qFSZO+pbj4f6eqvYz+RHXuQETbNlw8fx7/84cZMLADb077iZ07j9KtWwt+nDeBc+dyGThgCqdPZwAQ3jgEU1EhSimSkzPLjPh7eLhecf6ewcMNRxcXzqVnlHuueY+ueDTwZesvcWXeJze3ANOlEfbo6DBenDCMu+/ugZOTI6WlJo6cg4xSdxRwociBX37exJfP/R+OjjoGDuxAdnYeGzbst+yzk5MjW7e9S+vWDQHz6fsmTQJxdjZPG/ju+/Xcf+9bgLlQe+ihfvz7P/dhNP7vD+yNp+x55vH3+deYDgwebB7hKioq5uX/m8v06Qur/YKTin5+V7pgGT58OHPnzuWxxx5j8+bNPProozzyyCNER0dz8uRJpk6dSnBwMCNHmv+aj4iIYN++fXz66ad89tlndOnShU8++YQRI0bwyy+/AOY5LevWrePll19m0aJFxMbG8tprr9G9e3e2bdtWrTtcWYt/f4fe3SJxd/rfkjVZWbkcP24e/UlOzuTzz5azbdth/v3v+xjzcD8c/nYeEWDz5oP07jWRkpJS/vWvu/n3f+4r8/yC1cf5aulRlEmxf91Gkvfuw8/PgzyTI8HNoziwYQt6B3jxxaG8OGEYJ06c4ekpv+LgY+TA0l8Z+cDNNG4SSGioHydPZrBm9Z/s3XuC4uISoqKC+efkB+jYOgSTSZGdfREvL3fOn8/l1KkMWrWKsOT45pvVvPx/c3nssdvQ6Rx49dUfyc3Nr7b/TyGEuJJu3Vrwy4L/o0EDzys+f/x4Oqt+38PIUbeg05X9HZufX4iLi3lex65dibw57SfWrNnHP/85jMefuJ309PN8NmsZHTpGEhtr/gAuLi7hm7mrufe+3jg5meepHDuWxhOPf8xTT9/JwIEdLO9/9GgKL/3zK86cOc+70x+mffvGvPbqj0yZ8h3+/l688+5D9O7dipAQP/LyCnl4zPv88MO6MhmNRm8KC4vJyjLf2X78+Fjeens0J0+e5aP//kZUVHCZz49jx9Jo1KjsmQeAVWv38+rkubw7/WHatWsMQE5OHgsWbGHK5O946qk7ePa5wWRmZuPgYI+XlzsA69bto2fPlphMJjq0f5bCwmK+/+FFS2Fz4cJFklMv0KJZEACzPl3Go2Nvo7i4hFWr/uTWW80ToR995AM+/zy+XK4bUWMFC5gXjpswYQKBgYHs27ePZ599lvXrzZfFzpkzh4iICG6++WZL/549e/Lee+8RHR1NSkoKb775Jp9++mmZ9xw2bBivvfYajRo1IjExkZdffpkFCxZUOFNNFSwrf3+NPn1iyM7OY8eOI7Rp0wgfn/JXJP11VOLChYuUlppITs5kzeq9PPDgzXh5ufPVV78TEuLHLbfEADD93QWcOHGWme8/CsDPP29i7KMfcsstMXzy6RN4e7tTWFjM6dMZFBeX4ufngZ+fh+VrJiamMv3dhbz2+gN4e7tXar+ysnLp3+8VTp3KYOOmt2jcOJDdu4/RresE8vOvfombEELUlIAAL+66qxv9+relefNQ4pf/wZIlO5j5/qM0aRJk6bd+fQKurk4Yjd4s+W07kyZ9S9euzZn12ZP4+npc4ytAYWGx+Q/I3q0s2xYt2kJ0dFiZr2EymSgqKkGncyhXIF0WF7eNm26Kwt/fq8z20tJSxj32EXZ2dox+qC+tWzfE1dWJkpJS5s3bwMXcfB559LYrvufPP2/irTd/Yvv2I0RFBTNyZB+MRm+cnJ0YMqQzLi56S99z53IoLi4hIMDbsm+Xi6877/gPW7ce4plnBnH8eDpffLGCud88z3339WbPnuNERPjj6enGuXM5/HvK93zyyVKKi0t4990xPPvcYMvXePU/PzB58rc8/HB/7r2vN7f2n1RmBKs6VObzW/NFY6qj1dTCcT16RKsePaKVTmde0Mje3l516BCpbr21nbrttvZq2rSRKiPzO2VScWr7jvdU9+4tyr3H7bd3VCYVZ2k5ufPVqFG3WJ4fM6a/Kij8RZlUnDp/4UdLv5LSRWVeZ1JxKvHY52rMmP7qyNFZZbZv3TZdvfTSXer++29WU6c+qDZtfludTv5SpaZ9rY4mfqZee+0BFRbWQBmN3qpz56bKw+N/CzKFhPipiRP/oYxGb82PozRp0qT9vfn4GNTPv/yf+n3V66pnz5ZX7efv76WmTLlXJad8pUwqTu358wM1YEB7de+9vdSatW+oDRvfUq1aRShA3X//zerAwY/Vv/99n7Kzs1NBQT4qYf9HyqTi1A8//lM1bhyoAOXu7qKmTLlX5V78SRWXLFSffPKEevrpO1Vh0QLL79/de95XvXu3Uj4+BjVr1pPlfm+bVJwqLllYbtuECcPUww/3V5s2v61WrHxNdetW/vPjr61581C1848ZyqTi1JKlU1RgoI+ys7NTXbo0U/ErXrW870cfjbvi6yMiAlR+wS+WfmvXTVN+fh5l+uh0Dmr1mjeUScWp/Qc+Vk5OjjV+fCvx+a39N2Mt73C1NxcXJ9W0aYiys7O7ap9//etuZVJxasvWd1VkZFC559u2baz2H/jY8o09Zcq9ysnJUYWH+6uuXZurHj2iVdeuzZVer1OACg72VQcOfmzpe7mgkiZNmrT63nQ6B9W4caCyt7ev1Osu/8690nO+vh4qNLSB5fEtt8SoXbvfV2+9NbrMh7qdnZ364IOxyqTi1KHDn6pnn41VkZFBytFRp9q2bazmfvO8Opr4mfrHP7pXed9atAi74nO33tpOvfDCEOXsrL/q6ydPHqFMKk7N/uIZy+fJlfb1tdceUE2bhtTK8aqxlW6tVW2udFtV4eH+nDqVYZlg9XcuLk489FBftmw5xM6dR6/7fnq9Di8vd86cOV/NSYUQQtyI4GBfUlLOWdWK6Jf5+nqQmand1T5/V6NzWKyRLRQsQgghhCirop/fVbpbsxBCCCFEbZKCRQghhBBWTwoWIYQQQlg9KViEEEIIYfWkYBFCCCGE1ZOCRQghhBBWTwoWIYQQQlg9KViEEEIIYfWkYBFCCCGE1ZOCRQghhBBWTwoWIYQQQlg9KViEEEIIYfWkYBFCCCGE1dNpHaC6GQwGrSMIIYQQooIq+rldZwqWyzucnJyscRIhhBBCVJbBYCAnJ+eqz9sBqvbi1KygoKBr7mxVGAwGkpOTCQ4Orvb3thb1YR+hfuyn7GPdUB/2EerHfso+Vvw9UlJSrtmnzoywANfd2RuRk5NTZ7/ZLqsP+wj1Yz9lH+uG+rCPUD/2U/bx+q+9Hpl0K4QQQgirJwWLEEIIIayeFCzXUVhYyJQpUygsLNQ6So2pD/sI9WM/ZR/rhvqwj1A/9lP2sfrUqUm3QgghhKibZIRFCCGEEFZPChYhhBBCWD0pWIQQQghh9aRgEUIIIYTVk4LlOsaNG8exY8fIz89nx44ddO/eXetIVfbSSy+xbds2srOzSU9PZ8GCBURFRZXpM2fOHJRSZdrmzZs1Slx5kydPLpc/NTW1XJ/k5GTy8vJYvXo1LVq00Cht1Rw/frzcPiql+PDDDwHbPIY9evRg8eLFJCcno5QiNja2XJ/rHTe9Xs/777/P2bNnyc3NZdGiRQQHB9fWLlTItfZTp9Mxbdo0/vzzT3Jzc0lOTuarr74iMDCwzHusXr263PH9/vvva3tXrup6x7Ii35/Wfiyvt49X+vlUSvHCCy9Y+lj7cazI5wXU7s+lFCzXMHz4cGbMmMHrr79O27ZtWb9+PUuXLiU0NFTraFXSq1cv/vvf/9K5c2f69euHTqcjPj4eV1fXMv2WLl2K0Wi0tIEDB2qUuGr27dtXJn+rVq0sz02YMIHnnnuOJ598ko4dO5KWlsaKFStwd3fXMHHldOzYscz+9e3bF4D58+db+tjaMXRzc2PPnj08+eSTV3y+IsdtxowZDBkyhHvuuYfu3bvj7u7Or7/+ir299fyau9Z+urq60q5dO1599VXatWvH0KFDiYqKYvHixeX6zpo1q8zxHTt2bG3Er5DrHUu4/ventR/L6+3jX/fNaDQyevRoTCYTP//8c5l+1nwcK/J5ocXPpZJ25bZlyxb10Ucfldm2f/9+NXXqVM2zVUfz8/NTSinVo0cPy7Y5c+aoBQsWaJ6tqm3y5Mlq165dV30+JSVFTZgwwfJYr9errKws9eijj2qevartvffeU0eOHKkzx1AppWJjYyt13Dw8PFRhYaEaPny4pU9gYKAqKSlR/fv313yfKrqff28dOnRQSikVGhpq2bZ69Wr13nvvaZ6/qvt4ve9PWzuWFTmOCxYsUCtXriyzzZaOI1z586K2fy6to1y1Qo6OjrRv3574+Pgy2+Pj4+natatGqaqXp6cnAOfOnSuzvXfv3qSnp3Po0CFmzZpFgwYNtIhXZZGRkSQnJ3Ps2DG+//57GjZsCEDDhg0JDAwsc0yLiopYu3atzR5TR0dH7r//fr744osy2239GP5VRY5b+/bt0ev1Zfqkpqayb98+mz22YP4ZNZlMnD9/vsz2++67j7Nnz7Jv3z7efvttmxohhGt/f9a1Y+nv78/tt9/O7Nmzyz1nS8fx758XWvxc1qmbH1YnPz8/dDod6enpZbanp6djNBo1SlW9pk+fzvr160lISLBsW7p0KfPnz+fEiRM0bNiQV199lVWrVtG+fXuKioo0TFsxW7du5cEHH+Tw4cMEBATwr3/9i02bNhEdHW05blc6puHh4VrEvWGDBw/Gy8uLL7/80rLN1o/h31XkuBmNRgoLC8t9sNvyz6uTkxPTpk3ju+++K3NjuG+//Zbjx4+TlpZGy5YteeONN4iJiaF///4apq24631/1rVjOXLkSHJycvjll1/KbLe14/j3zwstfi6lYLkOpVSZx3Z2duW22aIPP/yQ1q1bl5tEPG/ePMu/ExIS2LFjBydOnOD2229nwYIFtR2z0pYtW2b59759+9i8eTOJiYmMHDmSLVu2AHXrmI4ZM4alS5eWmVhs68fwaqpy3Gz12Op0On744Qfs7e15/PHHyzz3+eefW/6dkJDAkSNH2LlzJ23btmXXrl21HbXSqvr9aavH8qGHHuLbb78tt2y9LR3Hq31eQO3+XMopoavIyMigpKSkXBXo7+9frqK0Ne+//z6DBg3i5ptvJjk5+Zp909LSOHHiBJGRkbWUrnrl5eWxd+9eIiMjSUtLA6gzxzQsLIy+ffuW+cV3JbZ+DCty3NLS0nBycsLLy+uqfWyFTqdj3rx5NGzYkH79+pUZXbmSP/74g6KiIps+vn/9/qxLx7J79+40a9bsuj+jYL3H8WqfF1r8XErBchXFxcXs3LmTfv36ldner18/Nm3apFGqG/fBBx8wdOhQ+vTpQ1JS0nX7+/j4EBoaWu7SYFuh1+tp3rw5qampHD9+nNTU1DLH1NHRkV69etnkMR09ejRnzpzht99+u2Y/Wz+GFTluO3fupKioqEwfo9FIy5YtberYXi5WIiMj6du3b7n5ZVcSHR2NXq+32eP79+/PunIswTwCumPHDv7888/r9rXG43itzwutfi41n31srW348OGqsLBQjR49WjVr1kxNnz5d5eTkqLCwMM2zVaX997//VVlZWapnz54qICDA0pydnRWg3Nzc1Ntvv606d+6swsPDVa9evdTGjRvVqVOnlLu7u+b5K9Lefvtt1bNnTxUREaE6deqkFi9erC5cuGA5ZhMmTFBZWVlq8ODBKjo6Wn377bcqOTnZZvbvcrOzs1NJSUnqjTfeKLPdVo+hm5ubiomJUTExMUoppcaPH69iYmIsV8dU5Lh99NFH6uTJk6pPnz6qTZs2auXKlWrXrl3K3t5e8/2ryH46ODiohQsXqpMnT6rWrVuX+Rl1dHRUgGrUqJF65ZVXVPv27VV4eLgaMGCA2r9/v9q5c6fV7Oe19rGi35/Wfiyv9/0KKIPBoHJzc9XYsWPLvd4WjuP1Pi9Ak59L7f9jrLmNGzdOHT9+XBUUFKgdO3aUuaTL1trVjBw5UgHK2dlZLVu2TKWnp6vCwkKVlJSk5syZo0JCQjTPXtH2/fffq+TkZFVYWKhOnz6tfvrpJ9W8efMyfSZPnqxSUlJUfn6+WrNmjYqOjtY8d2Vbv379lFJKRUZGltluq8ewV69eV/zenDNnToWPm5OTk3r//fdVRkaGunjxolq8eLHV7fe19jM8PPyqP6O9evVSgAoJCVFr1qxRGRkZqqCgQB05ckTNmDFDeXt7a75vFdnHin5/WvuxrMj36yOPPKIuXryoPDw8yr3eFo7j1Vz+vLjcavPn0u7SP4QQQgghrJbMYRFCCCGE1ZOCRQghhBBWTwoWIYQQQlg9KViEEEIIYfWkYBFCCCGE1ZOCRQghhBBWTwoWIYQQQlg9KViEEEIIYfWkYBFCCCGE1ZOCRQghhBBWTwoWIYQQQlg9KViEEEIIYfX+HwOw5k9hWMmIAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "n = 1.006\n", - "sig1 = arr[1:, 1] - arr[1:, 0] - (arr[1:, 0] - 16384) * 0.006\n", - "ref1= arr[1:, 3] - arr[1:, 2] - (arr[1:, 2] - 16384) * 0.006\n", - "\n", - "sig2 = arr[1:, 5] - arr[1:, 4] - (arr[1:, 4] - 16384 * 2) * 0.003\n", - "ref2= arr[1:, 7] - arr[1:, 6] - (arr[1:, 6] - 16384 * 2) * 0.003\n", - "\n", - "\n", - "sig3 = arr[1:, 9] - arr[1:, 8] - (arr[1:, 8] - 16384 * 3) * 0.0015\n", - "ref3= arr[1:, 11] - arr[1:, 10] - (arr[1:, 10] - 16384 * 3) * 0.0015\n", - "\n", - "sig4 = arr[1:, 13] - arr[1:, 12] - (arr[1:, 12] - 16384 * 4) * 0.0008\n", - "ref4= arr[1:, 15] - arr[1:, 14] - (arr[1:, 14] - 16384 * 4) * 0.0008\n", - "\n", - "plt.plot(sig1 / ref1 * 1.025 + .1)\n", - "plt.plot(sig2 / ref2)\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 377, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 377, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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HR0Na76RFGru93ryyScVeel53YHpy5QzMzPR1NS0oO8Va+seCr3Ue6j0UO+REo/1HqrFqnXNzz5nBMbM5ck7d+4Us9kshw8fFpfL5T/bvr6+Xj799FP/+JlLdBsaGsRsNsvOnTsDLtEtKCgQn88ndXV1kpWVJXV1dTF3qWYouVdVVYnX65Xa2lrFpZgpKSn+Me+8846UlpbK2rVrJScnR5qamsTr9cqGDRs0zzec3Pfs2SMWi0UyMjLk8ccfl/r6ehERKS8v1/26z8Rnn30mXV1dQY8ZL+u+bNkyycnJkZycHBER2bt3r+Tk5PjfjkDP9a42dz3Vu9rc9VLvavOeiUWqde0fIEbwqK2tlatXr4rb7Za+vj4pLCz039fc3Czt7e2K8Vu2bJH+/n5xu91y5coV2bVrV8Axt2/fLufPnxePxyPDw8OKYoqlUJN7e3u7BNPc3Owfc/jwYRkZGRG32y12u12+/fZb2bRpk+Z5hpv7vn375PLly/Lnn3+Kw+GQM2fOyLZt2xJi3YHp9y/5448/5OWXXw56vHhZ95lLrWf7GdZzvavNXU/1rjZ3vdR7KD/vi1Xrhv99QURERJTQeE4REREREdgUEREREQFgU0REREQEgE0REREREQA2RUREREQA2BQRERERAWBTRERERASATRERERERADZFRERERADYFBEREREBYFNEREREBIBNEREREREA4L89hPxqliuoMwAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot((sig1 * 1) / ref1 , ((sig4 * 1) / ref4) - 1.05* (sig1 * 1) / ref1 - .2)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 344, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(1.0466826219966743, 1.1690711183330815, 1.3755932879073696)" - ] - }, - "execution_count": 344, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(sig2.mean() - 60) / sig1.mean()/2, (sig3.mean() - 120) / sig1.mean()/3, (sig4.mean() - 120) / sig1.mean()/4, " - ] - }, - { - "cell_type": "code", - "execution_count": 345, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 345, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "x = sig1 / ref1\n", - "y = (sig2 - 50) / (ref2)\n", - "\n", - "y2 = (sig4 - 240) / (ref4)\n", - "plt.plot(x, y - 1.0 * x)\n", - "plt.plot(x, y2 - 1.0 * x)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 232, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ 4.78982041, 224.95172753])" - ] - }, - "execution_count": 232, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.polyfit(sig1, sig2, deg = 1)" - ] - }, - { - "cell_type": "code", - "execution_count": 231, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 231, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkMAAAGdCAYAAAAR5XdZAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA88UlEQVR4nO3de1jUdfr/8RuUwYSB0VSGlMxa/ZmKUASIohgl0klTN3W3NtsObrXWtqHkYQs7WnlIKd22bdcN0narXRNPiZaaKeqKiKKt9S3TQg5SDCdxEH3//ig/9XEGFR34zOH5uK77upx73uA9n6uaV+/3HPxERAkAAICP8jd6AAAAACMRhgAAgE8jDAEAAJ9GGAIAAD6NMAQAAHwaYQgAAPg0whAAAPBphCEAAODT2ho9gNEuu+wyqampMXoMAADQDGazWY4cOeKS3+XTYeiyyy6T4uJio8cAAAAXoGvXri4JRD4dhk7vCHXt2pXdIQAAPITZbJbi4mKXPXf7dBg6raamhjAEAICP4gXUAADApxGGAACATyMMAQAAn0YYAgAAPo0wBAAAfBphCAAA+DTCEAAA8GmEIQAA4NMIQwAAwKcRhgAAgE8jDAEAAJ9GGAIAAD6NMAQAAFzu8v59ZdiD90pIl85Gj3JOfGs9AABwqdEzJsug8WO02+te/7uB05wbYQgAALhE28BAeWnnRl0vf8UaY4ZpBo7JAADARbsiKtIhCE2Pv0G+Ly4xZqBmIAwBAICLMnrGZHnk7Te02/krP5S0yASxHztm4FTnj2MyAABwQZwdi71+/yPyxfadxgx0gQhDAACg2a6IitTtBon8cCzmKbtBP8cxGQAAaJYzj8V2rVrrUcdiZ2JnCAAAnJeAdoHy4n836nqvP/CofLHtv8YM5CKEIQAAcE5Oj8UG3CD2Os/cDfo5jskAAMBZjZqepj8WW537w7GYFwQhEXaGAABAE5wdi/1l4qPyeZ5nH4udiTAEAAAcODsWm5FwoxyvrTNoopbDMRkAANA581is4MdjMW8MQiLsDAEAgB85Pxb7g3yet8OYgVoJYQgAAPjUsdiZOCYDAMDHORyLrVnn1cdiZ2JnCAAAH2W6pJ3M2rFB13vjwT/KgS3bDJrIGIQhAAB80BXR/eWR7L/oejMGDpPjNbUGTWQcjskAAPAxo2dM1gWhwtyPfzgW88EgJMLOEAAAPsN0ySUya8fHut5fH/qj/O9T3zoWOxNhCAAAH3BlTLT8/h9/1vX+NChF6qtrDJrIfRCGAADwcmOfni7xo2/TbheszpW3n8gwcCL3QhgCAMBLBQa1lxe2faTreeN3i10swhAAAF6o54BYefCvmbqer3yIYnMRhgAA8DJ3vfS0XHNzinZ7x7KV8q+nnjdwIvdGGAIAwEu0MwfL81vX6XoLf/uwfLWzwKCJPANhCAAAL9B7cII8sGierjctLlka6usNmshzEIYAAPBwv13wovRLTtJub/nnv+U/z88xcCLPQhgCAMBDtQ8NkWc/XavrZd71gBwqLDJoIs9EGAIAwAP1vX6w3Jv5sq43NXaonDhuN2giz0UYAgDAw0x8/RX5f4MGaLc3/mOprJj7qoETeTbCEAAAHiK4Ywd5etNqXe+VcffIt/sPGDSRdyAMAQDgAaKG3yB3z3lOu33q5EmZGnu9nDxxwsCpvANhCAAAN/f7f/xZroyJ1m6v+8ti+fC1N4wbyMv4N2fx1KlTZceOHVJdXS1lZWWybNky6dWrl27N4sWLRSmlq7y8PN0ak8kkmZmZcvToUamtrZXly5dL165ddWssFotkZWWJzWYTm80mWVlZEhoaqlsTEREhOTk5UltbK0ePHpUFCxZIQEBAcx4SAABuy9zpUpm7N08XhOaMuYsg5GLNCkNJSUmycOFCGTBggAwbNkzatm0rubm50r59e926NWvWiNVq1ermm2/W3T9//nwZNWqUjB8/XhITEyU4OFhWrlwp/v4/jbN06VKJjo6W1NRUSU1NlejoaMnOzv5pcH9/WbVqlQQFBUliYqKMHz9exowZI3Pnzr2Q6wAAgFuJuTVVZm5Yqd0+XlcnU65JlJLPvzRwKu+lLrQ6deqklFJq8ODBWm/x4sVq2bJlTf5MSEiIstvtauzYsVovPDxcNTY2qpSUFCUiqnfv3koppeLi4rQ18fHxSimlevXqpUREpaamqsbGRhUeHq6tGTdunKqvr1dms/m85jebzUopdd7rKYqiKKqly8/PTz32r8Vq7t48rW64f4Lhc7lTufr5u1k7Q2c6fWz1/fff6/pDhw6VsrIyOXDggLzxxhvSuXNn7b6YmBgxmUySm5ur9UpKSqSoqEgGDhwoIiIJCQlis9lkx44d2prt27eLzWbTrSkqKpKSkhJtzdq1a6Vdu3YSExPjdF6TySRms1lXAAC4C0tYF5mzZ6tE9Omt9V4aMV4+evMtA6fyfhcVhubNmyebN2+Wffv2ab01a9bInXfeKcnJyZKWliaxsbHy8ccfi8lkEhERq9UqdrtdbDab7neVlZWJ1WrV1pSXlzv8feXl5bo1ZWVluvttNpvY7XZtzZmmTZsm1dXVWhUXF1/wYwcAwJXiR98mT65frt2urvhOpkQnSvnBQwZO5Rsu+N1kr732mvTv318SExN1/XfffVf78759+2Tnzp1y6NAhueWWW2TZsmVN/j4/Pz9RSmm3f/7ni1nzc7NmzZJ58376Ejuz2UwgAgAYys/PT6Z8sFTCrrxC6y2fvUA+yfqncUP5mAsKQ5mZmTJixAgZMmTIOcNEaWmpHDp0SHr27KndDgwMFIvFotsd6tKli2zdulVbExYW5vC7OnfurO0GlZaWSnx8vO5+i8UiJpPJYcfotIaGBmloaDjvxwkAQEvqcJlV/rRWv1Hwws13yHfffGvQRL6p2cdkr776qowePVqSk5Pl66+/Puf6jh07SkREhPbanvz8fGloaJBhw4Zpa6xWq/Tr108LQ3l5eWKxWCQ2NlZbExcXJxaLRbemX79+uiOxlJQUOX78uOTn5zf3YQEA0KoGjR+jC0LffXtEJkcNIggZ5Lxfbb1w4UJVWVmphgwZosLCwrRq166dEhEVFBSkZs+erQYMGKC6d++ukpKS1JYtW9Q333yjgoODtd+zaNEidfjwYZWcnKyio6PV+vXrVUFBgfL399fWrF69Wu3evVvFx8er+Ph4VVhYqHJycn565be/v9qzZ49at26dio6OVsnJyerw4cMqMzPTsFejUxRFUdS5ys/fX81Y+x/du8UGjhtt+FyeVC3w/H3+i5syYcIPb/lr166d+vDDD1VZWZmy2+3q66+/VosXL1bdunXT/Z7AwECVmZmpKioqVF1dncrJyXFY06FDB5Wdna2qqqpUVVWVys7OVqGhobo1ERERasWKFaqurk5VVFSozMxMZTKZjLyYFEVRFNVkdbq8my4Ezd2bpzqEWw2fy9PK1c/ffj/+wSeZzWaprq6WkJAQqampMXocAIAXS7r7VzJiyqPa7dL/+0rmjL6ryTf9oGmufv7mu8kAAGhB/m3ayMwNKyWog0Xr/eupF2THshXGDQUdwhAAAC2kS4/u8kSO/i3yz9w4QqrKjho0EZy5qA9dBAAAzt3wwARdEDpctF/SIhMIQm6InSEAAFzIv20beSHvIwloF6j1lkzNkF2rcs/yUzASYQgAABcJ73WVTP7327rezKG3SM133zfxE3AHHJMBAOACqY9M1AWhL3cWSFpkAkHIA7AzBADARWgTECAv7/pE13vr8emyZ90GgyZCcxGGAAC4QBF9r5bH/vl3Xe+pwalSZ6syaCJcCI7JAAC4ACOfeEwXhPZ/skXSIhMIQh6InSEAAJohoF2gvPjfjbremw+nyWebtxozEC4aYQgAgPPU49oomfTW67rejIHD5HhNrUETwRUIQwAAnIexT0+X+NG3abd3rc6VJU9kGDgRXIUwBADAWQS2by8vbP9I1/vzfZPk/3bkGzQRXI0wBABAE3olxMnv3lig602LS5aG+nqDJkJLIAwBAODE3XOfl6iUZO123nsfyPvPvGTgRGgphCEAAH7mkhCzPLdF/z1ir941Ub4u3GvQRGhphCEAAH7U9/rBcm/my7reE9cNlUa73aCJ0BoIQwAAiMjEv8yX/zcwXru98a2lsmLOqwZOhNZCGAIA+LTgjh3k6U2rdb1Xxt0j3+4/YNBEaG2EIQCAz7rmpmFy18vPaLdPNjbKtNjr5WRjo4FTobURhgAAPunRt/8q3aP6abfXLnpTcv/8NwMnglEIQwAAnxLSpbNkfJSj6718+6+l7MuDBk0Eo/Gt9QAAnxE36jZdEDpWVS1TohMJQj6OnSEAgNfz8/OTKR8slbArr9B6K+a8KhvfWmrcUHAbhCEAgFfr2DVcZnz4H11v1i13SMXhbw2aCO6GYzIAgNdK/PUduiD03bdHZHLUIIIQdNgZAgB4HT9/f3ly3QcS2qWz1nv/2Zcl791lBk4Fd0UYAgB4lc5XXC5TV/xL13suZZRUlpQaNBHcHcdkAACvkXzf3bogdOTAFzK5/0CCEM6KnSEAgMfzb9tGntuSK4Ht22u9d2Y8KztzVp/lp4AfEIYAAB7tqthr5eG/L9T1Zl5/q9RUfGfQRPA0hCEAgMea8sFSsV7VQ7tdV2mTp4bcZOBE8ESEIQCAx2kTECAv7/pE1zt66Bt58daxBk0ET0YYAgB4lN6DE+SBRfN0vdcfeFS+2PZfgyaCpyMMAQA8xlMf5eg+O0hEZMo1iXKq8aRBE8Eb8NZ6AIDbC2gXKHP35umC0Df7PpO0yASCEC4aO0MAALfWf9j1MmHeC7rea3f/Tg4W7DFoIngbwhAAwG29uHOjBAQG6npTohPl1El2g+A6HJMBANxOYFB7mbs3TxeEvti+84djMYIQXIydIQCAW7luxM3yq+ef1PXmj79Xvtn3mUETwdsRhgAAbmPu3jyH3uSoQaJOnTJgGvgKjskAAIa7JMTsEIT2bdgsaZEJBCG0OHaGAACGSrhjlPzyqXRdb+4v75YjB74waCL4GsIQAMAwHIvBHXBMBgBodcEdOzgEoYLVuRyLwRDsDAEAWtWQu8fLyCl/0PVevv3XUvblQYMmgq8jDAEAWoWfn5/M2bPVoT+5/0BRShkwEfADjskAAC0uvNcvHILQ9v+s+OFYjCAEg7EzBABoUc5eJP3ibePk6NeHDZgGcNSsnaGpU6fKjh07pLq6WsrKymTZsmXSq1cvh3UZGRlSXFwsx44dkw0bNkifPn1095tMJsnMzJSjR49KbW2tLF++XLp27apbY7FYJCsrS2w2m9hsNsnKypLQ0FDdmoiICMnJyZHa2lo5evSoLFiwQAICAprzkAAALaR9aIjTIDQt7nqCENxKs8JQUlKSLFy4UAYMGCDDhg2Ttm3bSm5urrRv315bk56eLo8//rhMmjRJYmNjpbS0VNatWyfBwcHamvnz58uoUaNk/PjxkpiYKMHBwbJy5Urx9/9pnKVLl0p0dLSkpqZKamqqREdHS3Z29k+D+/vLqlWrJCgoSBITE2X8+PEyZswYmTt37sVcDwCACySMHSXPfrrWoZ8WmSAN9ccNmAg4O3Wh1alTJ6WUUoMHD9Z6R44cUenp6dptk8mkKisr1cSJE5WIqJCQEGW329XYsWO1NeHh4aqxsVGlpKQoEVG9e/dWSikVFxenrYmPj1dKKdWrVy8lIio1NVU1Njaq8PBwbc24ceNUfX29MpvN5zW/2WxWSqnzXk9RFEWdu+buzXOoa25OMXwuynvK1c/fF/UC6tPHVt9//72IiPTo0UPCw8MlNzdXW9PQ0CCbNm2SgQMHiohITEyMmEwm3ZqSkhIpKirS1iQkJIjNZpMdO3Zoa7Zv3y42m023pqioSEpKSrQ1a9eulXbt2klMTIzTeU0mk5jNZl0BAFyjqWOxJ2KSpGB1rpOfANzDRYWhefPmyebNm2Xfvn0iImK1WkVEpKysTLeurKxMu89qtYrdbhebzXbWNeXl5Q5/X3l5uW7NmX+PzWYTu92urTnTtGnTpLq6Wqvi4uJmPmIAgDODxo9p8lissaHBgImA83fB7yZ77bXXpH///pKYmOhw35lvk/Tz8zvnWyfPXONs/YWs+blZs2bJvHnztNtms5lABAAXydlu0N8fTZd9GzYbMA3QfBe0M5SZmSkjRoyQ66+/XhcmSktLRUQcdma6dOmi7eKUlpZKYGCgWCyWs64JCwtz+Hs7d+6sW3Pm32OxWMRkMjnsGJ3W0NAgNTU1ugIAXJimjsWmXJNIEIJHaXYYevXVV2X06NGSnJwsX3/9te6+gwcPSklJiQwbNkzrBQQESFJSkmzd+sOHbeXn50tDQ4NujdVqlX79+mlr8vLyxGKxSGxsrLYmLi5OLBaLbk2/fv10gSglJUWOHz8u+fn5zX1YAIBmGHzn2CaPxU41njRgIuDinPerrRcuXKgqKyvVkCFDVFhYmFbt2rXT1qSnp6vKykp1++23q759+6olS5ao4uJiFRwcrK1ZtGiROnz4sEpOTlbR0dFq/fr1qqCgQPn7+2trVq9erXbv3q3i4+NVfHy8KiwsVDk5OT+98tvfX+3Zs0etW7dORUdHq+TkZHX48GGVmZlp2KvRKYqifKGcvVusV0Kc4XNRvlMt8Px9/oubMmHCBN26jIwMdeTIEVVfX682btyo+vbtq7s/MDBQZWZmqoqKClVXV6dycnJUt27ddGs6dOigsrOzVVVVlaqqqlLZ2dkqNDRUtyYiIkKtWLFC1dXVqYqKCpWZmalMJpORF5OiKMprK8gS6jQI+f3sf2QpqjXK1c/ffj/+wSeZzWaprq6WkJAQXj8EAGcx9J475ba0Sbqe/dgxmR5/g0ETwZe5+vmb7yYDAJyVsxdJvzbhQTm4q9CAaQDXIwwBAJwK7thBnt602qE/uf9AvmkeXuWiPnQRAOCdbrh/gkMQspWVS1pkAkEIXoedIQCAjrNjsfnj75Vv9n1mwDRAyyMMAQBERMR8aUeZuXGVQz8tMsGAaYDWwzEZAECG//4BhyBU8sWXBCH4BHaGAMDHOTsWmzPmLin5/EsDpgFaH2EIAHxUSJfOkvFRjkOf3SD4Go7JAMAH3fr4JIcg9PXuvQQh+CR2hgDAxzg7Fpt161ipOPSNAdMAxiMMAYCP6BBulT/lLnPosxsEX8cxGQD4gF9mPOEQhD77NI8gBAg7QwDg1fz8/GTOnq0O/edSRkllSakBEwHuhzAEAF7q0ohuMn31ew59doMAPY7JAMALTZj3gkMQ2rliDUEIcIKdIQDwIn7+/jKncItDf+b1t0pNxXcGTAS4P3aGAMBLhF3Vw2kQSotMIAgBZ8HOEAB4gYcXL5KrrrtG1/vk7X/J8pfmGzMQ4EEIQwDgwfzbtpHZBZ869J9MHC7HqqoNmAjwPByTAYCH+n+DBjgNQmmRCQQhoBnYGQIAD+TsKzXWvbFYPnz1DQOmATwbYQgAPEibgAB5edcnDv0ZCTfK8do6AyYCPB/HZADgISJvHOo0CKVFJhCEgIvAzhAAeABnx2Ir5r4mG/+xxIBpAO9CGAIAN9Y2MFBe2rnRoT8t7nppqD/e+gMBXohjMgBwU9feOtxpEEqLTCAIAS7EzhAAuCFnx2LvP/Oy5L23zIBpAO9GGAIAN2K65BKZteNjh/4TMUnS2NBgwESA9+OYDADcRMIdo5wGobTIBIIQ0ILYGQIAN+DsWGzptKclf+WHBkwD+BbCEAAYqJ05WJ7fus6hn37NYDnZ2GjARIDv4ZgMAAwy9J47nQahtMgEghDQitgZAgADODsWe+vx6bJn3QYDpgF8G2EIAFpRUAeLPPPJGof+lOhEOXXypAETASAMAUAr+WXGE5Lwy9sd+mmRCa0/DAANYQgAWoGzY7E3H06TzzZvNWAaAD9HGAKAFhQa1lmeWp/j0J8cNUjUqVMGTATgTIQhAGghE+a9IP2HXe/Q51gMcC+EIQBoAc6Oxf760B/lf59uM2AaAGdDGAIAF7o0optMX/2eQ39y/4GilDJgIgDnQhgCABd5JPsNuSI60qHPsRjg3ghDAHCR/Pz8ZM4ex3eFvTbhQTm4q9CAiQA0B2EIAC5C2FU9JP2DpQ59doMAz0EYAoALNHXlu9K5e4RDnyAEeBbCEAA0U1PHYvPumCDF//vcgIkAXAzCEAA0Q3ivq2Tyv9926LMbBHguwhAAnKdnPlkjQR0sDn2CEODZCEMAcA5NHYu9eNs4Ofr1YQMmAuBK/s39gcGDB0tOTo4UFxeLUkpGjhypu3/x4sWilNJVXp7+k1hNJpNkZmbK0aNHpba2VpYvXy5du3bVrbFYLJKVlSU2m01sNptkZWVJaGiobk1ERITk5ORIbW2tHD16VBYsWCABAQHNfUgA0KSuV/dyGoTSIhMIQoCXaHYYCgoKksLCQpk0aVKTa9asWSNWq1Wrm2++WXf//PnzZdSoUTJ+/HhJTEyU4OBgWblypfj7/zTO0qVLJTo6WlJTUyU1NVWio6MlOzv7p8H9/WXVqlUSFBQkiYmJMn78eBkzZozMnTu3uQ8JAJyaXbhFHn/3LV2vqvwox2KAF1IXWkopNXLkSF1v8eLFatmyZU3+TEhIiLLb7Wrs2LFaLzw8XDU2NqqUlBQlIqp3795KKaXi4uK0NfHx8UoppXr16qVERKWmpqrGxkYVHh6urRk3bpyqr69XZrP5vOY3m81KKXXe6ymK8o3y8/NTc/fmOZTFGmb4bBRFuf75u9k7Q+dj6NChUlZWJgcOHJA33nhDOnfurN0XExMjJpNJcnNztV5JSYkUFRXJwIEDRUQkISFBbDab7NixQ1uzfft2sdlsujVFRUVSUlKirVm7dq20a9dOYmJiWuJhAfABZzsWs5WWGTARgJbm8hdQr1mzRt577z05dOiQ9OjRQ5599ln5+OOPJSYmRhoaGsRqtYrdbhebzab7ubKyMrFarSIiYrVapby83OF3l5eX69aUlen/w2Sz2cRut2trzmQymSQwMFC7bTabL+ahAvAyzr5pvvh/n8u8OyYYMA2A1uLyMPTuu+9qf963b5/s3LlTDh06JLfccossW7asyZ/z8/PTfaOzs293vpA1Pzdt2jSZOXPm+TwMAD6kqXeLzRx6i9R8970BEwFoTS1yTPZzpaWlcujQIenZs6d2OzAwUCwWi25dly5dtJ2e0tJSCQsLc/hdnTt31q05cwfIYrGIyWRy2DE6bdasWRISEqLVme9gA+B7uvXp3eSxGEEI8A0tHoY6duwoERER2mt78vPzpaGhQYYNG6atsVqt0q9fP9m69Yf/IOXl5YnFYpHY2FhtTVxcnFgsFt2afv366QJRSkqKHD9+XPLz853O0tDQIDU1NboC4Lvm7s2TP/5rsa5X+uVB3i0G+KBmveI6KChIRUVFqaioKKWUUo899piKiopSERERKigoSM2ePVsNGDBAde/eXSUlJaktW7aob775RgUHB2u/Y9GiRerw4cMqOTlZRUdHq/Xr16uCggLl7++vrVm9erXavXu3io+PV/Hx8aqwsFDl5OT89Mpvf3+1Z88etW7dOhUdHa2Sk5PV4cOHVWZmpmGvRqcoynPK2bvFzJd2NHwuiqLOXS3w/N28H0hKSlLOLF68WLVr1059+OGHqqysTNntdvX111+rxYsXq27duul+R2BgoMrMzFQVFRWqrq5O5eTkOKzp0KGDys7OVlVVVaqqqkplZ2er0NBQ3ZqIiAi1YsUKVVdXpyoqKlRmZqYymUxGXkyKoty8Lo/s4zQIGT0XRVHnX65+/vb78Q8+yWw2S3V1tYSEhHBkBvgAZ+8WK/niS5kz+i4DpgFwoVz9/M13kwHwCc6C0FODU6XOVmXANADcSYu/gBoAjNTjmv5Og1BaZAJBCICIsDMEwIs5C0H/999d8ud7f2/ANADcFWEIgFdyFoT+NChF6qt5fSAAPY7JAHiVX8TFNHksRhAC4Aw7QwC8hrMQtPejTfKPx6YaMA0AT0EYAuDxmvpusRkDh8nxmloDJgLgSTgmA+DReicOaPK7xQhCAM4HO0MAPJazY7H/Ll8l//zTcwZMA8BTEYYAeJwmj8USbpTjtXUGTATAk3FMBsCj9EtOavpYjCAE4AKwMwTAYzg7Ftvyz3/Lf56fY8A0ALwFYQiA2+NYDEBL4pgMgFu79pYUjsUAtCh2hgC4LWfHYhv+/rasfGWhAdMA8FaEIQBux8/fX+YUbnHocywGoCVwTAbArQy443anQYhjMQAthZ0hAG7D2bFY7ut/l7UL/2rANAB8BWEIgOH827aR2QWfOvQ5FgPQGjgmA2Cooffc6TQIcSwGoLWwMwTAMM6OxVYveF0+evMtA6YB4KsIQwBaXZuAAHl51ycO/ekDbhB73TEDJgLgyzgmA9CqUh+Z6DQIpUUmEIQAGIKdIQCtxtmx2AcvzZfNb//LgGkA4AeEIQAtznRJO5m1Y4NDf1pcsjTU1xswEQD8hDAEoEWNnjFZBo0f49BPi0wwYBoAcEQYAtBinB2LvffMS7LtvQ9afxgAaAJhCIDLBQa1lxe2feTQnxo7VE4ctxswEQA0jTAEwKV+PStDYm5NdehzLAbAXRGGALiMs2OxJdNmyq6Vaw2YBgDOD2EIwEW7JMQsz23Jdeg/EZMkjQ0NBkwEAOePMATgotz32hzpkzTIoc+xGABPQRgCcMGcHYst/sNUKfp4kwHTAMCFIQwBaLagDhZ55pM1Dv30a4fIyRMnDJgIAC4cYQhAszz69l+le1Q/hz7HYgA8FWEIwHlzdiz2xu8ekwNbtxswDQC4BmEIwDmZO10qMzesdOhPuSZRTjWeNGAiAHAdwhCAs5q68l3p3D3Coc+xGABvQRgC0CRnx2KvTXhQDu4qNGAaAGgZhCEADizWMHly3QcO/SnRiXLqJMdiALwLYQiAzrOfrpX2oSEOfY7FAHgrwhAAjbNjsfm/uk++KdpvwDQA0DoIQwDk0m5dZfqa9x36k6MGiTp1yoCJAKD1EIYAH+dsN0iEYzEAvoMwBPgwZ0Fo3tgJUvzZ5wZMAwDGIAwBPijsqh6S/sFSh/7k/gNFKWXARABgHMIQ4GM4FgMAPcIQ4EOcBaHZo+6U0v/7yoBpAMA9EIYAH9CtT2/5478WO/Q5FgMAEf/m/sDgwYMlJydHiouLRSklI0eOdFiTkZEhxcXFcuzYMdmwYYP06dNHd7/JZJLMzEw5evSo1NbWyvLly6Vr1666NRaLRbKyssRms4nNZpOsrCwJDQ3VrYmIiJCcnBypra2Vo0ePyoIFCyQgIKC5DwnwanP35jkEIfuxY5IWmUAQAgC5gDAUFBQkhYWFMmnSJKf3p6eny+OPPy6TJk2S2NhYKS0tlXXr1klwcLC2Zv78+TJq1CgZP368JCYmSnBwsKxcuVL8/X8aZ+nSpRIdHS2pqamSmpoq0dHRkp2d/dPg/v6yatUqCQoKksTERBk/fryMGTNG5s6d29yHBHglPz8/p8dis265Q6bH32DARADgvtSFllJKjRw5Utc7cuSISk9P126bTCZVWVmpJk6cqEREhYSEKLvdrsaOHautCQ8PV42NjSolJUWJiOrdu7dSSqm4uDhtTXx8vFJKqV69eikRUampqaqxsVGFh4dra8aNG6fq6+uV2Ww+r/nNZrNSSp33eorylOpxTX81d2+eQxk9F0VRlCvK1c/fzd4ZOpsePXpIeHi45Obmar2GhgbZtGmTDBw4UEREYmJixGQy6daUlJRIUVGRtiYhIUFsNpvs2LFDW7N9+3ax2Wy6NUVFRVJSUqKtWbt2rbRr105iYmJc+bAAjzJ3b55MyvqLrvd9cQnvFgOAJrj0BdRWq1VERMrKynT9srIy6d69u7bGbreLzWZzWHP6561Wq5SXlzv8/vLyct2aM/8em80mdrtdW3Mmk8kkgYGB2m2z2dyMRwe4Nz9/f5lTuMWh/1zKKKksKTVgIgDwDC7dGTrtzBdl+vn5nfOFmmeucbb+Qtb83LRp06S6ulqr4uLis84EeIpeCXFOg1BaZAJBCADOwaVhqLT0h//onrkz06VLF20Xp7S0VAIDA8VisZx1TVhYmMPv79y5s27NmX+PxWIRk8nksGN02qxZsyQkJESrM9/BBniiuXvz5HdvLND1ij/7nGMxADhPLg1DBw8elJKSEhk2bJjWCwgIkKSkJNm6dauIiOTn50tDQ4NujdVqlX79+mlr8vLyxGKxSGxsrLYmLi5OLBaLbk2/fv10gSglJUWOHz8u+fn5TudraGiQmpoaXQGeyr9tG6fvFnv6hhEyb+wEAyYCAM/VrFdcBwUFqaioKBUVFaWUUuqxxx5TUVFRKiIiQomISk9PV5WVler2229Xffv2VUuWLFHFxcUqODhY+x2LFi1Shw8fVsnJySo6OlqtX79eFRQUKH9/f23N6tWr1e7du1V8fLyKj49XhYWFKicn56dXfvv7qz179qh169ap6OholZycrA4fPqwyMzMNezU6RbVWRd44lHeLURTls9UCz9/N+4GkpCTlzOLFi7U1GRkZ6siRI6q+vl5t3LhR9e3bV/c7AgMDVWZmpqqoqFB1dXUqJydHdevWTbemQ4cOKjs7W1VVVamqqiqVnZ2tQkNDdWsiIiLUihUrVF1dnaqoqFCZmZnKZDIZeTEpqsXLWQh6ePEiw+eiKIpqrXL187ffj3/wSWazWaqrqyUkJIQjM7i9NgEB8vKuTxz6M4feIjXffW/ARABgDFc/f7fIu8kAuFbMbTc5DUJpkQkEIQC4SHxRK+DmnL1Ies+6DfLW49MNmAYAvA9hCHBTfn5+MmfPVof+U0NukrpKW+sPBABeimMywA11j+rnNAilRSYQhADAxdgZAtyMs2OxnTlr5J0ZzxgwDQB4P8IQ4EacBaEnE4fLsapqA6YBAN/AMRngBjp2u8xpEEqLTCAIAUALY2cIMFjGxyskpHMnXW/32o8ke/KfDJoIAHwLYQgwEMdiAGA8jskAAwR1sHAsBgBugjAEtLJfPf+UPPPJGl3v4K5CSYtMMGgiAPBtHJMBrcjZblDG0Jul9rtKA6YBAIgQhoBW0S44SJ7PW+/QZzcIAIzHMRnQwm6b/IhDEDq8dz9BCADcBDtDQAtydiz27LDbxVZaZsA0AABnCENAC2jTtq28XLDZoc9uEAC4H47JABeLTr3RIQgdKiwiCAGAm2JnCHAhPkQRADwPYQhwgbaBgfLSzo0OfXaDAMD9cUwGXKTRMyY7BKEPXppPEAIAD8HOEHARnB2LTY+/QezHjhkwDQDgQrAzBFyAdsFBTX63GEEIADwLO0NAMyXf9xu55bGHdb2/TZoi+zd9atBEAICLQRgCzpN/mzYye7dj4OG1QQDg2TgmA85Dj2ujHILQ4j88QRACAC/AzhBwDr+Z85xED79B13viuqHSaLcbNBEAwJUIQ0ATnB2LbXt/ubz39IsGTQQAaAmEIcCJ3oMT5IFF83S9vz78uPxvs+M7yAAAno0wBJzh1j/+Xq6/9y5dj2MxAPBehCHgRwHtAuXF/27U9coPHpKXRow3ZiAAQKsgDAEi0jP+OnnwzVd1vTcfTpPPNm81aCIAQGshDMHnPfDnV6R34gBdb3LUIFGnThk0EQCgNRGG4LP827aR2QX6d4vl/vlvsnbRmwZNBAAwAmEIPuny/n3lD0v0oeeFm34p331bbNBEAACjEIbgc+56+Rm55qZhuh6fJA0AvoswBJ/h5+8vcwq36Hob/7FUVsx9tYmfAAD4AsIQfELX3r3k8ffe0vVeGjFeyg8eMmgiAIC7IAzB692RMVUG/HKkrsexGADgNMIQvJaz7xbb/u8ceXfmLIMmAgC4I8IQvJK151Uy5T9v63rzxk6Q4s8+N2giAIC7IgzB64xI/4Mk/Ub/FRpTrkmUU40nDZoIAODOCEPwGs4+RHHP+o3y1h+nGTQRAMATEIbgFTp1j5BpK9/V9Rb99mH5cmeBQRMBADwFYQgeL+XBe2X47x/Q9abFXS8N9ccNmggA4EkIQ/BYzo7FDu3ZJ5l33m/QRAAAT0QYgke6tFtXmb7mfV3vH49Nlb0fbTJoIgCApyIMweM4OxZ7anCq1NmqDJoIAODJCEPwGG3atpWXCzbrehXffCuzbr7DoIkAAN6AMASPEHZVD0n/YKmulzX5T1K49iODJgIAeAt/V//CjIwMUUrpqqSkxGFNcXGxHDt2TDZs2CB9+vTR3W8ymSQzM1OOHj0qtbW1snz5cunatatujcVikaysLLHZbGKz2SQrK0tCQ0Nd/XDgBkZNT3MIQn8aNJwgBABwCZeHIRGRoqIisVqtWkVGRmr3paeny+OPPy6TJk2S2NhYKS0tlXXr1klwcLC2Zv78+TJq1CgZP368JCYmSnBwsKxcuVL8/X8ad+nSpRIdHS2pqamSmpoq0dHRkp2d3RIPBwZpExAgc/fmSeKvfqn1vt69V9IiE6S+utrAyQAA3ka5sjIyMlRBQUGT9x85ckSlp6drt00mk6qsrFQTJ05UIqJCQkKU3W5XY8eO1daEh4erxsZGlZKSokRE9e7dWymlVFxcnLYmPj5eKaVUr169zntWs9mslFLKbDa79BpQF19XREWquXvzdNVzQKzhc1EURVHGl6ufv1tkZ6hnz55SXFwsX331lbzzzjvSo0cPERHp0aOHhIeHS25urra2oaFBNm3aJAMHDhQRkZiYGDGZTLo1JSUlUlRUpK1JSEgQm80mO3bs0NZs375dbDabtsYZk8kkZrNZV3A/9y2cI4+8/YauNy3uevli238NmggA4M1cHoa2b98ud999twwfPlweeOABsVqtsnXrVunYsaNYrVYRESkrK9P9TFlZmXaf1WoVu90uNpvtrGvKy8sd/u7y8nJtjTPTpk2T6upqrYqLiy/mocLFAtu3l7l786TPkEFa77/LV0taZAKfJg0AaDEufzfZhx9+qP25qKhI8vLy5Msvv5QJEybItm3bREREKaX7GT8/P4femc5c42z9uX7PrFmzZN68edpts9lMIHIT/ZKHyG8XvKTrzf3l3XLkwBcGTQQA8BUt/tb6Y8eOyd69e6Vnz57ywQcfiMgPOzulpaXami5dumi7RaWlpRIYGCgWi0W3O9SlSxfZunWrtiYsLMzh7+rcubPDrtPPNTQ0SENDgwseFVxpyrIlYv3Flbpe+jWD5WRjo0ETAQB8SYu8ZujnTCaTXH311VJSUiIHDx6UkpISGTZsmHZ/QECAJCUlaUEnPz9fGhoadGusVqv069dPW5OXlycWi0ViY2O1NXFxcWKxWLQ1cH+XhJhl7t48XRBa95fFkhaZQBACALQql77Ce/bs2WrIkCHqiiuuUHFxcSonJ0dVVVWpyy+/XImISk9PV5WVler2229Xffv2VUuWLFHFxcUqODhY+x2LFi1Shw8fVsnJySo6OlqtX79eFRQUKH9/f23N6tWr1e7du1V8fLyKj49XhYWFKicnx9BXo1PnX1HDb3B4t9il3boaPhdFURTl/tUCz9+uHfCdd95RxcXFym63q2+//Va9//776uqrr9atycjIUEeOHFH19fVq48aNqm/fvrr7AwMDVWZmpqqoqFB1dXUqJydHdevWTbemQ4cOKjs7W1VVVamqqiqVnZ2tQkNDjb6Y1HnU9DX/dghC/m3aGD4XRVEU5Rnl6udvvx//4JPMZrNUV1dLSEiI1NTUGD2O17skJESe27JW11sx51XZ+NbSJn4CAABHrn7+5rvJ0CquvXW43Dlrpq73zI0jpKrsqDEDAQDwI8IQWtzMjavEfGlH7fbJxkZ54toh5/w4BQAAWgNhCC0myBIqz2z+UNd7/9mXJe/dZQZNBACAI8IQWkT86Ntk7NPTdb2MpJul9vtKgyYCAMA5whBcbtaODWK6pJ12+1hVtTyZONzAiQAAaBphCC4T3LGDPL1pta73zoxnZWfO6iZ+AgAA4xGG4BIDx42WMX+aouv9aVCK1FfzkQUAAPdGGMJFm7s3T3f7++ISeT51tEHTAADQPIQhXDBzp0tl5oaVut5baTNkT+7HBk0EAEDzEYZwQZLu/pWMmPKorjc9/gaxHztm0EQAAFwYwhCa7cxjsSOf/5/MHfMbg6YBAODiEIZw3ixhXeTJ9ct1vTd/P1k++2SLQRMBAHDxCEM4LzdOvEdueuR3ut4T1w2VRrvdoIkAAHANwhDO6cxjsYMFe+S1u3/XxGoAADwLYQhN6tjtMpmx5t+63l8mPiqf5/3XoIkAAHA9whCcuumR38mNE+/R9dKvHSInT5wwZiAAAFoIYQgOzjwWO7Blm7zx4B8NmgYAgJZFGIKmS4/u8kTOP3W9hfc8JF/l7zZmIAAAWgFhCCIiMvKJx2TIXeN0vSnXJMqpxpMGTQQAQOsgDPk4P39/mVOo/5ygPes3ylt/nGbQRAAAtC7CkA8L73WVTP7327regjvvl8N79hk0EQAArY8w5KPGzpwm8WNG6HpTohPl1EmOxQAAvoUw5GP827aR2QWf6nr5Kz+UpdOeNmgiAACMRRjyIRH9+shj7/xN13tl3D3y7f4DBk0EAIDxCEM+YOiEX8ttkx9x6HMsBgAAYcirtQ0MlJd2bnTo573/gbz/9EutPxAAAG6IMOSleiXEye/eWODQnzPmLin5/EsDJgIAwD0RhryQf5s2ToMQx2IAADjyN3oAuNalEd1k9m79u8U2L3lX0iITCEIAADjBzpAXueWxhyT5vrt1vZdv/7WUfXnQoIkAAHB/hCEv4OfnJ3P2bHXoT44aJOrUKQMmAgDAc3BM5uGCO3ZwCEKfLn1P0iITCEIAAJwHdoY8WMIdo+SXT6Xrei+NGC/lBw8ZNBEAAJ6HMOSBOBYDAMB1OCbzMOZLOzoEod0frudYDACAC8TOkAcZcvd4GTnlD7revLETpPizzw2aCAAAz0cY8gD+bdo4fHaQCB+iCACAK3BM5uY6dY9wCEKFuR/zIYoAALgIO0NubPSMyTJo/Bhdj2MxAABcizDkhtoEBMjLuz5x6E+5JlFONbIbBACAK3FM5mauir3WIQhtynrnh2MxghAAAC7HzpAbefbTtdI+NETXe274KKk8UmrQRAAAeD/CkBsIDessT63P0fVOnTolU6IGGTQRAAC+gzBksBFTHpWku3+l6/314cflf5vzDJoIAADfQhgySFNfqfFETJI0NjQYMBEAAL6JF1AbwNrzKocgdPpF0gQhAABaFztDrezezJel7/WDdb1nbhwhVWVHDZoIAADfRhhqJU0di6VFJhgwDQAAOI1jslbwi7gYhyC0ZGoGQQgAADfg8WHooYcekq+++krq6+tl586dkpiYaPRIOi/u3CgP/e01Xe+JmCTZtSrXoIkAAMDPeXQYGjt2rMyfP1+ef/55ueaaa2Tz5s2yZs0aiYiIMHo0Cb60g8zdmycBgYFar/zgIV4kDQCAm/ETEWX0EBdq27ZtsmvXLnn44Ye13v79++WDDz6Q6dOnn/PnzWazVFdXS0hIiNTU1LhsLmcfovjKuHvk2/0HXPZ3AADgq1z9/O2xL6AOCAiQmJgYefHFF3X93NxcGThwoNOfMZlMEviznRqz2dwis41Mf0x3m9cGAQDgvjz2mKxTp07Stm1bKSsr0/XLysrEarU6/Zlp06ZJdXW1VsXFxS0y29JpT8vJxkbZvfYjghAAAG7OY3eGTlNKf8rn5+fn0Dtt1qxZMm/ePO222WxukUDU2NAg6dcMPvdCAABgOI8NQxUVFdLY2OiwC9SlSxeH3aLTGhoapIEXLwMAgJ/x2GOyEydOSH5+vgwbNkzXHzZsmGzd6vjhhgAAAM547M6QiMi8efMkOztbdu7cKXl5eTJx4kS5/PLL5fXXXzd6NAAA4CE8Ogy9++67cumll8pTTz0l4eHhUlRUJDfffLMcPnzY6NEAAICH8OjPGbpYLfU5QwAAoOW4+vnbY18zBAAA4AqEIQAA4NMIQwAAwKcRhgAAgE8jDAEAAJ9GGAIAAD6NMAQAAHwaYQgAAPg0j/4Ealcxm81GjwAAAM6Tq5+3fToMnb6YxcXFBk8CAACay2w2u+QTqH366zhERC677LIW/SoOs9ksxcXF0rVrV77y4zxxzZqPa9Z8XLMLw3VrPq5Z853PNTObzXLkyBGX/H0+vTMkIi67kOdSU1PDvwTNxDVrPq5Z83HNLgzXrfm4Zs13tmvmymvJC6gBAIBPIwwBAACfRhhqYXa7XWbOnCl2u93oUTwG16z5uGbNxzW7MFy35uOaNV9rXzOffwE1AADwbewMAQAAn0YYAgAAPo0wBAAAfBphCAAA+DTCUAt76KGH5KuvvpL6+nrZuXOnJCYmGj1Sqxg8eLDk5ORIcXGxKKVk5MiRDmsyMjKkuLhYjh07Jhs2bJA+ffro7jeZTJKZmSlHjx6V2tpaWb58uXTt2lW3xmKxSFZWlthsNrHZbJKVlSWhoaEt+thaytSpU2XHjh1SXV0tZWVlsmzZMunVq5fDOq7bTx588EEpLCyUqqoqqaqqkq1bt0pqaqpuDdfr7KZOnSpKKXnllVd0fa7bTzIyMkQppauSkhKHNVwvvcsuu0yys7OloqJC6urqpKCgQK699lrdGne6bopqmRo7dqyy2+3qvvvuU71791avvPKKqqmpUREREYbP1tKVmpqqnn32WTVq1CillFIjR47U3Z+enq6qqqrUqFGjVN++fdU777yjiouLVXBwsLZm0aJF6ptvvlE33HCDio6OVh999JEqKChQ/v7+2prVq1erPXv2qAEDBqgBAwaoPXv2qJycHMMf/4XUmjVr1IQJE1SfPn1U//791YoVK9TXX3+t2rdvz3Vrom699VZ10003qZ49e6qePXuq5557TtntdtWnTx+u13nUddddp7766iu1e/du9corr/DPWROVkZGh9u7dq8LCwrTq1KkT1+ssZbFY1MGDB9Xf//53FRsbq7p3766Sk5PVlVde6a7XzfiL5q21bds2tWjRIl1v//796oUXXjB8ttYsZ2HoyJEjKj09XbttMplUZWWlmjhxohIRFRISoux2uxo7dqy2Jjw8XDU2NqqUlBQlIqp3795KKaXi4uK0NfHx8UoppXr16mX4477Y6tSpk1JKqcGDB3PdmlHfffeduvfee7le56igoCB14MABdcMNN6gNGzbowhDXTV8ZGRmqoKCgyfu5Xo41a9Ys9cknn5x1jTtdN47JWkhAQIDExMRIbm6urp+bmysDBw40aCr30KNHDwkPD9ddm4aGBtm0aZN2bWJiYsRkMunWlJSUSFFRkbYmISFBbDab7NixQ1uzfft2sdlsXnGNT2/zfv/99yLCdTsXf39/GTdunAQFBUleXh7X6xwWLlwoq1atko8++kjX57o517NnTykuLpavvvpK3nnnHenRo4eIcL2aMmLECNm5c6e8++67UlZWJrt27ZL7779fu9/drhthqIV06tRJ2rZtK2VlZbp+WVmZWK1Wg6ZyD6cf/9mujdVqFbvdLjab7axrysvLHX5/eXm5V1zjefPmyebNm2Xfvn0iwnVrSr9+/aSmpkbsdru8/vrrMmrUKPnss8+4Xmcxbtw4ufbaa2XatGkO93HdHG3fvl3uvvtuGT58uDzwwANitVpl69at0rFjR65XE6688kp56KGH5IsvvpDhw4fL66+/LpmZmfKb3/xGRNzvnzOf/9b6lqaU0t328/Nz6PmqC7k2Z65xtt4brvFrr70m/fv3d/qCe66b3oEDByQ6OlosFouMGTNG3nrrLUlKStLu53rpdevWTRYsWCApKSln/aoDrttPPvzwQ+3PRUVFkpeXJ19++aVMmDBBtm3bJiJcrzP5+/vLzp07ZcaMGSIisnv3bunbt6889NBDkp2dra1zl+vGzlALqaiokMbGRodk2qVLF4ck7GtKS0tFRM56bUpLSyUwMFAsFstZ14SFhTn8/s6dO3v0Nc7MzJQRI0bI9ddfL8XFxVqf6+bciRMn5Msvv5T8/HyZPn26FBYWyh/+8AeuVxNiYmIkLCxM8vPz5cSJE3LixAkZOnSoPProo3LixAntMXHdmnbs2DHZu3ev9OzZk3/OmlBSUiL79+/X9T777DO5/PLLRcQ9/3tm+AutvLW2bdumFi5cqOvt27ePF1DLDy+cmzJlinY7ICDA6Qvn7rjjDm2N1Wp1+sK52NhYbU1cXJzHvuBQRNSrr76qvv32W/WLX/zC6f1ct3PX+vXr1eLFi7leTVRwcLDq27evrnbs2KGysrJU3759uW7nUSaTSX3zzTfqySef5Ho1UUuWLHF4AfW8efPUli1btNtudt2Mv2jeWqffWv/b3/5W9e7dW82bN0/V1NSoyy+/3PDZWrqCgoJUVFSUioqKUkop9dhjj6moqCjtYwXS09NVZWWluv3221Xfvn3VkiVLnL6l8vDhwyo5OVlFR0er9evXO31L5e7du1V8fLyKj49XhYWFHvtW1IULF6rKyko1ZMgQ3Vt427Vrp63huunr+eefV4mJiap79+6qX79+6rnnnlONjY3qxhtv5Ho1o858NxnXTV+zZ89WQ4YMUVdccYWKi4tTOTk5qqqqSvtvOdfLsa677jrV0NCgpk2bpq666ir1q1/9StXW1qpf//rX7vrPmfEXzZvroYceUgcPHlTHjx9XO3fu1L1N2psrKSlJOXP6/9hFfni76pEjR1R9fb3auHGj9n+lpyswMFBlZmaqiooKVVdXp3JyclS3bt10azp06KCys7NVVVWVqqqqUtnZ2So0NNTwx38h1ZQJEybo1nHdfqo333xT+/errKxMrVu3TgtCXK/zrzPDENdNX6c//8Zut6tvv/1Wvf/+++rqq6/mep2jbrnlFrVnzx5VX1+v9u/fr+6//36HNe5y3fx+/AMAAIBP4gXUAADApxGGAACATyMMAQAAn0YYAgAAPo0wBAAAfBphCAAA+DTCEAAA8GmEIQAA4NMIQwAAwKcRhgAAgE8jDAEAAJ9GGAIAAD7t/wP66j0qJBTQgAAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(sig1, sig2)" - ] - }, - { - "cell_type": "code", - "execution_count": 208, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(4.179300299429111, 4.491380465109155)" - ] - }, - "execution_count": 208, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "(sig2/sig1).mean(), (ref2/ref1).mean()\n" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "tl = []\n", - "\n", - "for x in range(20):\n", - " tl += [x * 500000]\n", - " \n", - "_z = tl[-1] + 1500\n", - "for x in range(4):\n", - " tl += (np.arange(12) * 120 + x * 1500 + _z).tolist()\n", - "\n", - "_z = tl[-1] + 1500\n", - "for x in range(200):\n", - " tl += [x * 1500 + _z]\n", - "\n", - "\n", - "_z = tl[-1] + 1500\n", - "for _ in range(8):\n", - " _z = tl[-1] + 1500\n", - " for x in range(20):\n", - " tl += [x * 1200 + _z]\n", - "\n", - "_z = tl[-1] + 1500\n", - "for x in range(50):\n", - " tl += [x * 1500 * 4 + _z]\n", - "\n", - "\n", - "_z = tl[-1] + 1500\n", - "inv = 1000\n", - "for x in range(40):\n", - " tl += [x * 1500 * 4 + x * inv + _z]\n", - " inv += 5000" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(tl[:], plot_arr[:, 0] / plot_arr[:, 1], '-')\n", - "#plt.xscale('log')" - ] - }, - { - "cell_type": "code", - "execution_count": 143, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 143, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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/h49eqh79+7Kysry9VmtlTmqvLxcvXv31s6dO6udz+12y+12B1N+vTHCAgCAuYIaYSkvL9fGjRuVkpLi15+SkqK1a9fW+rrx48frlVde0YQJE7R8+XK/57Zv366LL75Y/fr187X3339fOTk56tevn/bu3RtMiY2iag5Lx6Suio6LNbkaAABan6CvcWRmZmrx4sXKzc3VunXrNGnSJCUlJemll16SJGVkZCghIUETJ06UVBlWXnvtNd13331av3694uLiJEklJSUqKipSWVmZtmzZ4vc7jhw5IknV+s0Se26SJGngDddpQOoovfP0HG1YmlXHqwAAQEMJeg7LkiVLdP/99+upp57Sl19+qREjRig1NVV79uyRJHXu3FlJSUm+4++++245HA4tWLBABw4c8LXnn3++4d5FI4qOi1Xvywb7HlttNo2d9ggjLQAANCGLKtc3N3tOp1NFRUWKiopScXFxg523x6ABmvzy/Gr9C26frB25XzTY7wEAoDUK9PubewnVoWDPXhler1+f1+NRwd4fTaoIAIDWh8BSh0JXvr5ameN77PV49M7Tc1ToyjexKgAAWhcCSwB++OJrSdJ36z/TM9eMZsItAABNjMASgAp3uSSp5OgxRlYAADABgSUAFeWVG9TZHQ6TKwEAoHUisATAU145wmIPI7AAAGAGAksAqi4J2QgsAACYgsASAN8Ii53AAgCAGQgsAagaYbGHhZlcCQAArROBJQAVJ0ZYbI6gb70EAAAaAIElAB5GWAAAMBWBJQC+Zc1MugUAwBQElgBUlFdIkmzswwIAgCkILAGocLNxHAAAZiKwBMDjm3RLYAEAwAwElgCcXNZMYAEAwAwElgBUBRarzSarzWZyNQAAtD4ElgBUXRKS2IsFAAAzEFgCULWsWWIvFgAAzEBgCYC3wuP7mREWAACaHoElQOVlZZIku4MRFgAAmhqBJUCsFAIAwDwElgCxFwsAAOYhsASo6o7NjLAAAND0CCwBYoQFAADzEFgCdHIOC5NuAQBoagSWAHEDRAAAzENgCZCnvEISl4QAADADgSVAvhEWJt0CANDkCCwBqhph4ZIQAABNj8ASoKr7CTHCAgBA0yOwBKhqlZCNVUIAADQ5AkuAqvZhsXPzQwAAmhyBJUC+fVi4+SEAAE2OwBIg3063zGEBAKDJEVgC5LuXEKuEAABocgSWALEP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XXSR0tPTCSwnjBs3Tr/61a80YcIEbdmyRf369dO8efO0b98+vfbaa2aXF7L4+x0Yu92ut956S1arVZMnTza7nFoRWGpRUFCgioqKaqMpnTp1qpbaW7sXXnhBN954o0aMGKG8vDyzywkpycnJiouL08aNG319drtdI0aM0D333KPw8HB5vV4TKzTf/v37tXXrVr++bdu26ZZbbjGpotDzxz/+UbNnz9bbb78tSdq8ebO6deumqVOnElhqcODAAUmVIy1VP0v8/a6J3W7XkiVL1L17d1155ZUhO7oisUqoVuXl5dq4caNSUlL8+lNSUrR27VqTqgo9f/rTnzRmzBhdeeWV+uGHH8wuJ+SsWrVKF198sfr16+drn332mV5//XX169ev1YcVSVqzZo169+7t19erVy/t3r3bpIpCT9u2bav9b8Xj8chq5U94TXbt2qX9+/f7/f12OBwaOXIkf79PURVWevbsqauvvrpZzD80feZvqLa0tDSjrKzMuP32240LLrjAyMzMNIqLi42kpCTTawuFNn/+fOPw4cPGiBEjjLi4OF+LiIgwvbZQbqwS8m8DBw403G63MXXqVKNHjx7GbbfdZhw9etSYMGGC6bWFSlu0aJGxd+9eIzU11ejWrZtx8803GwcPHjRmz55tem1mtcjISKNv375G3759DcMwjPvvv9/o27evb4XLww8/bBw+fNi4+eabjYsuush4/fXXjby8PKNdu3am1x4Kn5HNZjOWLVtm7Nmzx7jkkkv8/oY7HA7Ta6+lmV5ASLf09HRj165dRmlpqZGbm8uS3VNabSZOnGh6baHcCCzV2y9+8Qvj66+/NkpKSoytW7cad955p+k1hVJr166dMXfuXOOHH34wjh8/bnz//ffGjBkzQvmLpdHbyJEja/z7s2jRIt8x06ZNM/bt22eUlJQY//73v42LLrrI9LpD5TPq1q1brX/DR44caXrtNTXLiR8AAABCFhdAAQBAyCOwAACAkEdgAQAAIY/AAgAAQh6BBQAAhDwCCwAACHkEFgAAEPIILAAAIOQRWAAAQMgjsAAAgJBHYAEAACGPwAIAAELe/wfWrmhRPaQcawAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot((np.array(tl[-50:]) - tl[-50])/1e6, plot_arr[-50:, 0] / plot_arr[-50:, 1], '.-')\n", - "#plt.xscale('log')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[ 4942][V][u_adpd6100.cpp:584] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 6 bytes\n", - "[ 4943][V][u_adpd6100.cpp:631] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 8 bytes\n", - "[ 4953][I][PAM.cpp:37] detector_preset_1(): [PAM] Preset 1 set\n", - "[ 4958][V][do_c.cpp:83] detector_preset(): [DO_C] ADPD detector preset with current: 50\n", - "[ 4965][V][do_c.cpp:43] arr_set(): [DO_C] Set line 0 to type 1 with 100 x 10Hz samples, actinic:0, sub:1\n", - "[ 4975][V][PAM.cpp:74] run_preprocess(): [PAM] Run type:1, number:100, freq: 10, actinic: 0, subfactor 1\n", - "[ 4984][V][PAM.cpp:112] run_arr(): [PAM] Sample: 100, ref: 100, amb: 100, dark: 100\n", - "[ 4991][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[ 4998][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[ 5004][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[ 5011][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[ 5017][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 400 bytes\n", - "[ 5024][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[ 5030][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 400 bytes\n", - "[ 5036][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[ 5042][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 400 bytes\n", - "[ 5048][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[ 5054][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 400 bytes\n", - "[ 5061][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "[ 5066][V][PAM.cpp:120] run_arr(): [PAM] Memory allocation completed\n", - "[ 13584][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[ 13585][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[ 13586][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[ 13593][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[ 13598][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[ 13605][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "[ 13611][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "[ 13618][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "\n" - ] - } - ], - "source": [ - "print(outputs)" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "6\n", - "[ 35920][V][u_adpd6100.cpp:584] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 6 bytes\n", - "\n", - "[ 35921][V][u_adpd6100.cpp:631] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 8 bytes\n", - "\n", - "[ 35931][I][PAM.cpp:37] detector_preset_1(): [PAM] Preset 1 set\n", - "\n", - "[ 35936][V][do_c.cpp:83] detector_preset(): [DO_C] ADPD detector preset with current: 110\n", - "\n", - "[ 35943][V][do_c.cpp:43] arr_set(): [DO_C] Set line 0 to type 1 with 50 x 50Hz samples, actinic:50, sub:1\n", - "\n", - "[ 35953][V][PAM.cpp:74] run_preprocess(): [PAM] Run type:1, number:50, freq: 50, actinic: 50, subfactor 1\n", - "\n", - "[ 35962][V][PAM.cpp:112] run_arr(): [PAM] Sample: 50, ref: 50, amb: 50, dark: 50\n", - "\n", - "[ 35969][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "\n", - "[ 35976][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "\n", - "[ 35982][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "\n", - "[ 35989][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "\n", - "[ 35995][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 200 bytes\n", - "\n", - "[ 36002][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "\n", - "[ 36007][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 200 bytes\n", - "\n", - "[ 36014][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "\n", - "[ 36020][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 200 bytes\n", - "\n", - "[ 36026][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "\n", - "[ 36032][V][data_utils.cpp:32] init(): [DATA_UTIL] Allocation 200 bytes\n", - "\n", - "[ 36038][V][data_utils.cpp:61] clear(): [DATA_UTIL] ARR reset to 0\n", - "\n", - "[ 36044][V][PAM.cpp:120] run_arr(): [PAM] Memory allocation completed\n", - "\n", - "0, 765, 16455, 16444\n", - "0, 717, 16451, 16440\n", - "0, 714, 16452, 16436\n", - "0, 713, 16451, 16437\n", - "[ 0 765 16455 16444 0 717 16451 16440 0 714\n", - " 16452 16436 0 713 16451 16437 134475]\n", - "0, 715, 16451, 16436\n", - "0, 716, 16451, 16436\n", - "0, 718, 16451, 16437\n", - "0, 714, 16452, 16436\n", - "[ 0 715 16451 16436 0 716 16451 16436 0 718\n", - " 16451 16437 0 714 16452 16436 134413]\n", - "0, 715, 16452, 16438\n", - "0, 716, 16451, 16436\n", - "0, 713, 16451, 16436\n", - "0, 715, 16450, 16437\n", - "[ 0 715 16452 16438 0 716 16451 16436 0 713\n", - " 16451 16436 0 715 16450 16437 134410]\n", - "0, 714, 16453, 16437\n", - "0, 715, 16452, 16437\n", - "0, 714, 16450, 16437\n", - "0, 716, 16451, 16438\n", - "[ 0 714 16453 16437 0 715 16452 16437 0 714\n", - " 16450 16437 0 716 16451 16438 134414]\n", - "0, 716, 16451, 16437\n", - "0, 714, 16451, 16437\n", - "0, 711, 16451, 16438\n", - "0, 716, 16450, 16436\n", - "[ 0 716 16451 16437 0 714 16451 16437 0 711\n", - " 16451 16438 0 716 16450 16436 134408]\n", - "0, 714, 16450, 16438\n", - "0, 714, 16451, 16438\n", - "0, 714, 16451, 16437\n", - "0, 713, 16450, 16438\n", - "[ 0 714 16450 16438 0 714 16451 16438 0 714\n", - " 16451 16437 0 713 16450 16438 134408]\n", - "0, 715, 16450, 16437\n", - "0, 713, 16451, 16436\n", - "0, 716, 16451, 16436\n", - "0, 717, 16452, 16438\n", - "[ 0 715 16450 16437 0 713 16451 16436 0 716\n", - " 16451 16436 0 717 16452 16438 134412]\n", - "0, 715, 16452, 16437\n", - "0, 715, 16452, 16437\n", - "0, 716, 16451, 16439\n", - "0, 713, 16450, 16438\n", - "[ 0 715 16452 16437 0 715 16452 16437 0 716\n", - " 16451 16439 0 713 16450 16438 134415]\n", - "0, 713, 16452, 16437\n", - "0, 715, 16453, 16436\n", - "0, 715, 16452, 16437\n", - "0, 715, 16451, 16438\n", - "[ 0 713 16452 16437 0 715 16453 16436 0 715\n", - " 16452 16437 0 715 16451 16438 134414]\n", - "0, 713, 16451, 16437\n", - "0, 714, 16453, 16438\n", - "0, 715, 16450, 16440\n", - "0, 715, 16451, 16437\n", - "[ 0 713 16451 16437 0 714 16453 16438 0 715\n", - " 16450 16440 0 715 16451 16437 134414]\n", - "0, 712, 16451, 16436\n", - "0, 715, 16451, 16438\n", - "0, 715, 16451, 16436\n", - "0, 713, 16451, 16438\n", - "[ 0 712 16451 16436 0 715 16451 16438 0 715\n", - " 16451 16436 0 713 16451 16438 134407]\n", - "0, 713, 16453, 16436\n", - "0, 716, 16450, 16438\n", - "0, 716, 16452, 16437\n", - "0, 715, 16451, 16437\n", - "[ 0 713 16453 16436 0 716 16450 16438 0 716\n", - " 16452 16437 0 715 16451 16437 134414]\n", - "[ 36895][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "\n", - "[ 36896][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "\n", - "[ 36897][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "\n", - "[ 36904][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "\n", - "[ 36909][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "\n", - "[ 36916][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "\n", - "[ 36922][V][data_utils.cpp:15] ~dataclass(): [DATA_UTIL] DATACLASS destroied\n", - "\n", - "[ 36929][V][data_utils.cpp:22] clean(): [DATA_UTIL] Memory freed\n", - "\n", - "\n" - ] - } - ], - "source": [ - "\n", - "def line_parse(l:str)->str:\n", - " l = l.strip()\n", - " if len(l) < 1:return \"\"\n", - " if l[0] == '[':\n", - " print(l)\n", - " return \"\"\n", - " else:\n", - " return l.strip()\n", - "\n", - "with serial.Serial(\"COM4\", 115200, timeout = 5) as ser:\n", - "\n", - "\n", - " ser.write(cmd_str.encode())\n", - " data_ready = False\n", - " checked = False\n", - " ts = time.time()\n", - " while time.time() - ts < 1:\n", - " if ser.in_waiting > 0:\n", - " l = line_parse(ser.readline().decode())\n", - " if l == \"Wake!\":\n", - " ser.write(\"Ready\".encode())\n", - " data_ready = True\n", - " elif data_ready and l.isdigit():\n", - " ts = time.time()\n", - " data_len = int(l)\n", - " ser.write(\"GO\".encode())\n", - " a1 = ser.read(4 * (data_len + 1))\n", - " _a = np.frombuffer(a1, dtype=\">i4\")\n", - " if (_a[:-1].sum() == _a[-1]):\n", - " checked = True\n", - " elif l == \"DONE\":\n", - " ser.write(\"Check\".encode())\n", - " data_ready = False\n", - " print(_a)\n", - "\n", - " elif l == \"Finish\":\n", - " print(l)\n", - " break\n", - " else:\n", - " print(l)\n", - "\n", - " " - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "6932649" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "_a[:-1].sum()" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363, 56363,\n", - " 56363, 56363, 56363, 56363, 56363, 56363, 7503])" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "_a" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'200'" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "l" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/.vscode/examples/traces_loads.ipynb b/.vscode/examples/traces_loads.ipynb deleted file mode 100644 index 9218081..0000000 --- a/.vscode/examples/traces_loads.ipynb +++ /dev/null @@ -1,463 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import serial\n", - "import time\n", - "import matplotlib.pyplot as plt\n", - "from IPython import display\n", - "plt.style.use(\"dark_background\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def parse_data(l:str):\n", - " if len(l) < 10: return \"\", []\n", - " if l[:5] != \"Data:\": return \"\", []\n", - " s = l.split('\\t')\n", - "\n", - " if len(s) < 2: return \"\", []\n", - " _t = s[0].split(',')\n", - " if len(_t) < 2: return \"\", []\n", - " _type = _t[0][5:]\n", - " _size = int(_t[1][7:])\n", - " arr = np.fromstring(s[1], sep = ',', dtype=np.uint32)\n", - " if _type == \"ENV\":\n", - " env_type = np.bitwise_and(arr, 0xF000) >> 12\n", - " env_read = np.bitwise_and(arr, 0x0FFF)\n", - " env_time = np.bitwise_and(arr, 0xFFFF0000) >> 10\n", - " leaftemp_t = env_time[env_type == 4]\n", - " leaftemp = env_read[env_type == 4] / 10\n", - " timestemp_t = env_time[env_type < 4]\n", - " timestemp = env_type[env_type < 4]\n", - " return \"ENV\", [timestemp_t, timestemp, leaftemp_t, leaftemp]\n", - " elif _type in [\"Fluo\", \"Fluoref\", \"SUN\", \"leaf\", \"730\", \"730ref\"]:\n", - " return _type, [arr]\n", - " else:\n", - " print(_type)\n", - "\n", - " return \"\", []\n", - "\n", - "def gen_cmd_arr_line(mode:int, ir:int, num:int, freq:int, actinic:int, sub:int)->list:\n", - " return [mode, ir, num//256, num%256, freq//256, freq%256, actinic, sub]\n", - "\n", - "def ambit_run_arr(cmd_str, port = \"COM31\"):\n", - " timeout = 300\n", - " s = ''\n", - " _l = 0\n", - " result = {\"env_t0\": time.time()}\n", - "\n", - " with serial.Serial(\"COM31\", baudrate=115200) as ser:\n", - " t0 = time.time()\n", - " ser.write(cmd_str.encode())\n", - " while time.time() - t0 < timeout:\n", - " try:\n", - " if ser.in_waiting > 1:\n", - " c = ser.read()\n", - " if (c > bytes([0])) and (c < bytes([128])):\n", - " s += c.decode()\n", - " _l += 1\n", - " if (c == b\"\\n\") or (c == b\"\\r\"):\n", - " l = s\n", - " s = ''\n", - " _l = 0\n", - " if l[:-1] == \"BAD COMMAND\":\n", - " timeout = 1\n", - " continue\n", - " if l[:-1] == \"Data sent\":\n", - " timeout = .1\n", - " continue\n", - " #print(l)\n", - " d_type, data = parse_data(l)\n", - " if d_type == \"ENV\":\n", - " result.update({\"env_stamp_t\": data[0], \"env_stamp\": data[1], \"env_leaf_t\": data[2], \"env_leaf\": data[3]})\n", - " elif d_type != \"\":\n", - " result.update({d_type: data[0][:-1]})\n", - " else:\n", - " time.sleep(.1)\n", - " except KeyboardInterrupt:\n", - " break\n", - " return result\n", - "\n", - "def calc_arr_param(cmd: list, persist:bool):\n", - " cmd_arr = np.reshape(cmd, (-1, 8))\n", - " arr_length = cmd_arr.shape[0]\n", - " cmd_str = \"arrun,\" + str(arr_length) + \",\" + str(persist) + \",\"+ str(cmd)[1:-1] + \",\\n\"\n", - " cmd_str = cmd_str.replace(\" \", \"\")\n", - "\n", - " num_pts = cmd_arr[:, 2] * 256 + cmd_arr[:, 3]\n", - " mea_feq = 1 / (cmd_arr[:, 4] * 256 + cmd_arr[:, 5])\n", - " _t, mea_tml = 0, []\n", - " for _n, _f in zip(num_pts, mea_feq):\n", - " mea_tml += (np.arange(_n) * _f + _t).tolist()\n", - " _t = mea_tml[-1]\n", - " mea_tml = np.array(mea_tml) * 0.854\n", - " return cmd_str, mea_tml \n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "class ambit_run():\n", - " def __init__(self, persist:bool = False) -> None:\n", - " self.cmd_list = []\n", - " self.led_persist = int(persist)\n", - " \n", - " def add(self, mode:int = 2, ir:int = 0, num:int = 100, freq:int = 100, actinic:int = 0, sub:int = 1):\n", - " self.cmd_list += [mode, ir, num//256, num%256, freq//256, freq%256, actinic, sub]\n", - " return\n", - " def run(self):\n", - " cmd_str, self.mea_tml = calc_arr_param(self.cmd_list, self.led_persist)\n", - " self.result = {\"env_tl\": self.mea_tml, \"env_t0\": time.time()}\n", - " self.result.update(ambit_run_arr(cmd_str))\n", - " return self.result\n", - "\n", - " def plot(self):\n", - " result = self.result\n", - " mea_tml = self.mea_tml\n", - " plot_list = [_k for _k in result.keys() if _k[:4] != \"env_\"]\n", - " fig, axes = plt.subplots(1, len(plot_list) + 1, sharex = True, figsize = (20, 6))\n", - " for n, _p in enumerate(plot_list):\n", - " axes[n + 1].plot(mea_tml[1:], result[_p][1:])\n", - " axes[n + 1].set_title(_p)\n", - " axes[0].plot(mea_tml[1:], (result[\"Fluo\"]/ result[\"Fluoref\"])[1:])\n", - " # axes[0].plot(result['env_stamp_t'][:-1]/854, result['env_stamp'][:-1]* np.mean((result[\"Fluo\"]/ result[\"Fluoref\"])[1:]) + .1)\n", - " ax = axes[0].twinx()\n", - " ax.plot(result['env_leaf_t']/1000, result['env_leaf'], 'r.-')\n", - " \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "MPF1 = ambit_run(True)\n", - "MPF1.add(num = 10, freq = 5, actinic=0)\n", - "MPF1.add(num = 40, freq = 100, actinic=250) # 0.4s max\n", - "\n", - "MPF1.add(num = 10, freq = 100, actinic=200) # 0.1s \n", - "MPF1.add(num = 10, freq = 100, actinic=120) # 0.1s \n", - "MPF1.add(num = 10, freq = 100, actinic=100) # 0.1s \n", - "MPF1.add(num = 10, freq = 100, actinic=80) # 0.1s \n", - "MPF1.add(num = 10, freq = 100, actinic=60) # 0.1s \n", - "\n", - "MPF1.add(num = 40, freq = 100, actinic=240)\n", - "MPF1.add(num = 25, freq = 100, actinic=0)\n", - "MPF1.add(num = 50, freq = 10, actinic=0)\n", - "\n", - "MPF1.run()\n", - "MPF1.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ret = MPF1.result\n", - "plt.figure()\n", - "plt.plot(ret['Fluo'][5:],ret['Fluoref'][5:])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "MPF2 = ambit_run(True)\n", - "MPF2.add(num = 25, freq = 100, actinic=250)\n", - "MPF2.add(num = 25, freq = 100, actinic=200)\n", - "MPF2.add(num = 25, freq = 100, actinic=150)\n", - "MPF2.add(num = 25, freq = 100, actinic=100)\n", - "MPF2.add(num = 25, freq = 100, actinic=250)\n", - "MPF2.add(num = 25, freq = 100, actinic=50)\n", - "# ret = MPF2.run()\n", - "# MPF2.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "MPF3 = ambit_run(True)\n", - "MPF3.add(num = 25, freq = 100, actinic=250)\n", - "MPF3.add(num = 25, freq = 100, actinic=200)\n", - "MPF3.add(num = 25, freq = 100, actinic=150)\n", - "MPF3.add(num = 25, freq = 100, actinic=100)\n", - "MPF3.add(num = 25, freq = 100, actinic=250)\n", - "MPF3.add(num = 25, freq = 100, actinic=0)\n", - "# ret = MPF3.run()\n", - "# MPF3.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "RGF1 = ambit_run(True)\n", - "RGF1.add(num = 25, freq = 100, actinic=250)\n", - "RGF1.add(num = 25, freq = 100, actinic=0)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ACT1 = ambit_run(True)\n", - "ACT1.add(num=100, freq = 20, actinic=50)\n", - "\n", - "AMB1 = ambit_run()\n", - "AMB1.add(num=100, freq = 20, actinic=0)\n", - "\n", - "AMB2 = ambit_run()\n", - "AMB2.add(num=100, freq = 10, actinic=0)\n", - "\n", - "AMB3 = ambit_run()\n", - "AMB3.add(num=100, freq = 5, actinic=0)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "runlist = [MPF1] + [ACT1, MPF2] * 4 + [AMB1, MPF3] * 2 + [AMB2, MPF3] * 3 + [AMB3, RGF1] * 4\n", - "fig, axes = plt.subplots(1, 5, sharex = True, figsize = (20, 6))\n", - "ax = axes[0].twinx()\n", - "results = []\n", - "for n in runlist:\n", - " ret = n.run()\n", - " results.append(ret)\n", - " _tl = ret['env_tl']\n", - " _TL = _tl + ret['env_t0']\n", - " plot_list = [_k for _k in ret.keys() if _k[:4] != \"env_\"]\n", - " for n, _p in enumerate(plot_list):\n", - " axes[n + 1].plot(_TL[1:], ret[_p][1:])\n", - " axes[n + 1].set_title(_p)\n", - " axes[0].plot(_TL[1:], (ret[\"Fluo\"]/ ret[\"Fluoref\"])[1:])\n", - " # axes[0].plot(result['env_stamp_t'][:-1]/854, result['env_stamp'][:-1]* np.mean((result[\"Fluo\"]/ result[\"Fluoref\"])[1:]) + .1)\n", - " ax.plot(ret['env_leaf_t']/1000 + ret['env_t0'], ret['env_leaf'], 'r.')\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig, axes = plt.subplots(1, 5, sharex = True, figsize = (16, 6))\n", - "ax = axes[0].twinx()\n", - "for ret in results:\n", - " _tl = ret['env_tl']\n", - " _TL = _tl + ret['env_t0']\n", - " plot_list = [_k for _k in ret.keys() if _k[:4] != \"env_\"]\n", - " for n, _p in enumerate(plot_list):\n", - " axes[n + 1].plot(_TL[1:], ret[_p][1:])\n", - " axes[n + 1].set_title(_p)\n", - " axes[0].plot(_TL[1:], (ret[\"Fluo\"]/ ret[\"Fluoref\"])[1:])\n", - " \n", - " ax.plot(ret['env_leaf_t']/1000 + ret['env_t0'], ret['env_leaf'], 'r.')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig, axes = plt.subplots(1, 1, sharex = True, figsize = (16, 6), facecolor = [0, 0, 0, 0])\n", - "ax = axes.twinx()\n", - "for ret in results:\n", - " _tl = ret['env_tl']\n", - " _TL = _tl + ret['env_t0']\n", - " axes.plot(_TL[1:], (ret[\"Fluo\"]/ ret[\"Fluoref\"])[1:])\n", - " \n", - " ax.plot(ret['env_leaf_t']/1000 + ret['env_t0'], ret['env_leaf'], 'r.')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "58ef9ac2162448afbde1ea528d540e3e", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAB9AAAAJYCAYAAAA6431rAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3gUVdsG8Hs3yaYXWiq9hCJICV2kCaEJiPgKCogUKRYUkCafBRFQKQIKIogRUXzBVyCAlFhABIICgjQpYoiREggQ0jdtvj+SnWyZbclsye79u65zZXfmzJkzm81kd555zlEAEEBEREREREREREREREREROTmlI7uABERERERERERERERERERkTNgAJ2IiIiIiIiIiIiIiIiIiAgMoBMREREREREREREREREREQFgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQFgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQFgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQFgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQFgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQFgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQFgAJ2IiIiIKpnRo0dDEATJsnjxYgBAUlIS4uLiHNxTIiLLuNJ5zcvLCx9//DGuX7+OwsJCnDx50tFdIiIX0r59e2zduhXJycnIy8vDzZs3ceTIESxZskSsk5SUhJ07d0puHxMTA0EQMHr0aHHZm2++CUEQkJqaioCAAINtTLVHRGRvms+NderUsdk+WrVqhQMHDiA9PR2CIODll1+22b6IiJyVp6M7QERERERUHs8++ywuXLigs+z69esO6g0RUcW5wnlt8uTJmDRpEl588UWcOHECWVlZju4SEbmI/v37Y8eOHThw4ABmzpyJGzduICIiAm3btsXw4cPx6quvVqj90NBQzJw5E2+88YZMPSYiqpw+++wz+Pv7Y/jw4bh37x6uXr3q6C4REdkdA+hEREREVCmdPXsWJ06ccHQ3iIhk44znNV9fX+Tm5lpcv3nz5sjJycGqVats2CsickczZ85EUlIS+vTpg6KiInH55s2bMXPmzAq3v2fPHkydOhWrVq1CampqhdsjIqqsmjdvjnXr1mHv3r2O7goRkcNwCHciIiIicnnGhrnr1q0bBEFAt27ddJaPGTMGp06dQm5uLu7cuYOtW7eiSZMm9uwyEZFJtjivxcXFITMzE82bN8e+ffuQkZGBH3/8EUDJ0Oxz587Fn3/+iby8PNy6dQufffYZqlevLm4vCAKee+45+Pn5iUPQaw+TTERUEdWqVUNaWppO8FxDEIQKt/9///d/8PT0xFtvvVXhtoiI7OmRRx7BDz/8gPv37yM7OxuHDh1Cz549deo0aNAAn332GS5duoTs7Gz8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YN2hQ4cEQRCEnTt3Ovz4WVhYWICyz4116tQRlz388MPCzp07hbS0NEGtVgspKSnCzp07dc55wcHBwrp164SbN28KWVlZwsGDB4WHHnpI2L9/v7B//36dfQiCIHz44YcOP1YWFhYWRxZF6QMiIiIiIiIiIiIiIiIiIiK3xkl8iIiIiIiIiIiIiIiIiIiIwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQEAPB3dASKyvdatWyM1NdXR3SAiJxcWFoaTJ086uhuyi4yMRGZmpqO7QUROLjAwENevX3d0N2TF8x8RWYrnQCJyV654/gN4DiQiy7jqOZBIDgygE7m41q1b4/fff3d0N4iokmjTpo1LBdEjIyNx7do1R3eDiCqJqKgol7l4wPMfEVmL50AicleudP4DeA4kIuu42jmQSC4MoBNZaPLkyZgxYwYiIiJw7tw5vPLKKzh06JDR+l27dsWyZcvwwAMP4Pr163j//ffxySefiOvHjx+PZ555Bs2bNwcAnDhxAq+99hqOHTtWof3q02Set2nThlnoRGRUWFgYfv/9d5c7T2juuI+KiuLd90RkVGBgIK5du+ZS5wme/4jIUjwHEpG7csXzH8BzIBFZxlXPgURyYQCdyAJPPvkkli9fjueffx6HDx/GxIkTsWfPHjRr1gwpKSkG9evWrYvdu3dj3bp1GDlyJB566CGsXr0at2/fxtatWwEA3bt3x9dff40jR44gLy8PM2fOREJCghhwL89+TUlNTeWdZETktjIzM/mFgIjcEs9/ROTOeA4kInfGcyAREVHFCCwsLKbL0aNHhdWrV+ssO3/+vLBw4ULJ+u+++65w/vx5nWUff/yxcOTIEaP7UCqVwv3794VRo0aVe79SJTIyUhAEQYiMjHT468jCwuK8xVXPFYGBgYIgCEJgYKDD+8LCwuK8xRXPFa54TCwsLLYprni+cMVjYmFhkb+46rnCVY+LhYVF3sJzBQuL6aIEEZnk5eWFmJgYJCQk6CxPSEhA586dJbfp1KmTQf19+/ahbdu28PSUHvjBz88PXl5euHv3brn3CwAqlQqBgYFiCQgIMHuMRERERERERERERERERAQwgE5kRvXq1eHp6WkwL3BqairCw8MltwkPD5es7+XlherVq0tu8+677+LatWv44Ycfyr1fAJgzZw4yMjLEcvHiRbPHSEREREREREREREREREQMoBNZTBAEnecKhcJgmbn6UssBYMaMGXjqqafw+OOPQ61WV2i/ixYtQlBQkFgaN25stC4RERERERERERERERERlZEeS5qIRGlpaSgsLDTI+g4NDTXIDte4efOmZP2CggLcuXNHZ/n06dPx2muvoVevXjhz5kyF9gsA+fn5yM/PF58HBgaaPkAiIiIiIiIiIiIiIiIiAsAMdCKzCgoKcOLECfTu3Vtnee/evXHkyBHJbRITEw3qx8bG4vjx4ygsLBSXvfrqq3j99dfRt29fnDhxosL7JSIiIiIiIiIiIiIiIqLyYwCdyALLli3D+PHjMWbMGDRp0gTLli1D7dq1sWbNGgDAwoULsWHDBrH+mjVrUKdOHSxduhRNmjTBmDFjMG7cOCxZskSsM2PGDLzzzjsYO3Ysrl69irCwMISFhcHf39/i/RIRERGRfDp37owdO3bg2rVrEAQBgwcPNqjz5ptv4tq1a8jJycH+/fvRrFkznfUqlQorV67E7du3kZWVhfj4eERFRenUCQkJwRdffIH09HSkp6fjiy++QHBwsE6dWrVqYceOHcjKysLt27exYsUKeHl5yX/QRERERERERERkQGBhYTFfJk+eLCQlJQl5eXnC8ePHhYcfflhcFxcXJ+zfv1+nfteuXYUTJ04IeXl5wt9//y1MnDhRZ31SUpIg5c0337R4v5aUyMhIQRAEITIy0uGvIQsLi/MWVz1XBAYGCoIgCIGBgQ7vCwsLi/MWzbni8ccfF+bPny8MGTJEEARBGDx4sE69mTNnCvfv3xeGDBkiPPDAA8LXX38tXLt2TQgICBDrrF69WkhJSREeeeQRoVWrVsKPP/4onDx5UlAqlWKd3bt3C6dPnxY6duwodOzYUTh9+rSwY8cOcb1SqRROnz4t/Pjjj0KrVq2ERx55RPj333+FlStXWn1MPP+xsLCYK654vnDFY2JhYZG/uOq5wlWPi4WFRd7CcwULi9ni8A6wsLDYsLhqUIyFhUXe4qrnCn4ZYGFhsaRInSukAujXr18XZs6cKT5XqVTCvXv3hAkTJggAhKCgIEGtVgtPPvmkWCciIkIoLCwUYmNjBQBCkyZNBEEQhPbt24t1OnToIAiCIERHRwsAhL59+wqFhYVCRESEWGfYsGFCbm6uxecznv9YWFgsLa54vnDFY2JhYZG/uOq5wlWPi4WFRd7CcwULi+nCIdyJiIiIiIjMqFevHiIiIpCQkCAuy8/Px88//4zOnTsDAGJiYqBSqXTq3LhxA2fPnhXrdOrUCenp6fjtt9/EOr/++ivS09N16pw9exY3btwQ6+zbtw8+Pj6IiYmR7J9KpUJgYKBOISIiIiIiIiIi6zGATkREREREZEZ4eDgAIDU1VWd5amqquC48PBxqtRrp6ekm69y6dcug/Vu3bunU0d9Peno61Gq1WEffnDlzkJGRIZZr165Zf5BEREREbioyMhIbN25EWloasrOzcfLkSbRp00ZcHxcXB0EQdEpiYqK4vk6dOgbrNeWJJ54w2J9KpcLJkychCAJatmyps65WrVrYsWMHsrKycPv2baxYsQJeXl62O3giIiIywAA6ERERERGRhQRB0HmuUCgMlunTryNVvzx1tC1atAhBQUFiiYqKMtknIiIiIioREhKCw4cPo6CgAP369UOzZs0wffp0g5si9+zZg/DwcLH0799fXJeSkqKzLjw8HG+88QaysrKwZ88eg32+//77uH79usFypVKJ7777Dv7+/ujSpQuGDx+OoUOHYunSpbIfNxERERnn6egOEBERERERObubN28CKMkO1zwGgNDQUDFb/ObNm/D29kZISIjOBdfQ0FAcOXJErBMWFmbQfo0aNXTa6dChg876kJAQqFQqg8x0jfz8fOTn55f/AImIiIjc1CuvvIKUlBSMHTtWXJacnGxQT61WG/0sVlxcbLBuyJAh2Lx5M7Kzs3WW9+3bF7GxsRg6dKhOEB4AYmNj0axZM9SqVUuczmf69On4/PPPMXfuXGRmZpbrGImIiMg6zEAnIiIiq82ePRu//fYbMjIykJqaim3btiE6Olqnjrkh7qSMHj1acsg7b29vWx4OEZFZSUlJuHHjBnr37i0u8/LyQrdu3cTg+IkTJ5Cfn69TJzw8HM2bNxfrJCYmIiQkBO3atRPrtG/fHiEhITp1mjdvrjNce2xsLPLy8nDixAmbHicRERGRu+nfvz+OHz+OLVu2IDU1Fb///jvGjx9vUK979+5ITU3FxYsXsXbtWtSoUcNom23atEHr1q2xfv16neWhoaFYt24dRo0ahZycHIPtOnXq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Pn36oE+fPtiwYQO+//57nD17FgCQlJSE06dPIyEhAV27dsWQIUPwzjvvYMOGDS4NjjgHOlHdNXLkSCQnJyMxMRFZWVk4evQoZsyYYdJu0KBByMrKQmpqKtavX4/IyEhhXWRkJPr27Yvr169j3759yMzMxP/+9z/ccccdQpucnBycPn0ajzzyCIKDgyGTyfD4448jMzNTuECoX79+OHnypBCeA8CuXbsQGBiIHj16mO2/UqlEWFiY0Y38Q/UQ7qxAJyJyN346ICKLGKAbawRAAaACQKaH+2IOA3QiIiIi/xWknwM9nwE6Od/EiRMxYcIEnDx5EkuWLMGcOXOwefNmYf13332HJ554AgsWLMCJEycwY8YMjBs3Dvv27RPazJ07F9999x0SExOxb98+FBcXY/To0UbDF0+cOBEnTpxAUlISkpKS8Oeff2Ly5MnC+srKSowaNQqlpaXYt28fEhMT8d133+H555936fFLpRzCnaiuatGiBWbNmoVz585h2LBhWLduHeLj443+Nu3cuRMTJ07EkCFDMG/ePPTq1Qu//PILlEolAKBVq1YAgFdeeQUbNmzA8OHDcfToUezevRtt2rQRthMXF4du3bqhoKAApaWlmDt3LoYPH45bt24B0I3cUbPKPS8vD2q12mR6C71FixYhPz9fuKWnpzv19SHPqR7CnRXoRETuxiHcicgiBujG9MF0JoByT3ZEhK0BerOqrwzQiYiIiHyDXKlEQLCu+qg475aHe0P+6McffxSGXhezadMmbNq0SXS9Wq3G7NmzMXv2bNE2N2/eNAqlzLly5QpGjx5tucNOJmUFOlGdJZVKkZycjMWLFwMAUlJScNttt2HWrFlISEgAACQmJgrtT506heTkZKSlpWHUqFHYsmULpFLd35APPvgAH3/8sbCdf/7zn5g2bRpefPFFAMB7772H69ev484770RJSQlmzJiB7du3o1evXsK0FVqt1qSPEonE7HIAWLp0KVauXCk8DgsLY4juJ9T6CvRABuhERO7GTwdEZBEDdGPePP85wAp0IiIiIn8VFK4bjrWivBylhUUe7g2R/5FIeYqMqK7KzMzE6dOnjZb99ddfaNasmcgzdM9JS0tD27ZtAUAYct3SdoYMGYJ77rkHDz30EPbv349jx47hqaeeQklJCR599FFhuzUrzVUqFZRKpej86xqNBgUFBUY3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6+uLxYsXY9asWfa9ECIzMpOKssb1JdyJiNxdvjxHeBxcpZILe0JE5H4Uubmu7gIRkdNptVrR8wJNgYt6QkRUfjADnYjcTmOYDk4+ABq5oC+e6J+lv0CenoEj6/8q0XFunDiN7NQ0aTpFbuPKlSvIysoSlilTphS7z/fff49HHnkEr776qrCuTZs2GDduHF577TW7zt+1a1dMnToV77zzDtq0aYMBAwbg2WefxSeffGLvSyEycfvCRdyJuYJrR06gQM0JQoio7DixeQfSExKFKkBEROXJj2+ORV5WNlZ+aPu0Y0REnmTNtC+Qm5UFALh9IQbpd5hiRERUGrRcuHDx3KVmzZparVarrVmzpsv7YutSC9CqAa3WYFEVrnd13zxlkclkLu8DF/da9GNFkyZNtCEhIcLi5+dndb+FCxdqb9++ra1Xr55o/bhx47QajUarUqmERavVatVqtTY2Ntbi8f7991/tN998I1o3ePBgbU5OjkO/tyEhIVqtVqsNCQlx+TXmwoWL+y6eOFZ44mviwoWLcxZPHC888TWV94WfYbk4Y/HUscJTX1d5XzgOcpF64VjBhYv1hSXcicjtJAB4E7qy7T4A1ADeAsu3S8m49BORnlwuR3Z2tk1tv/vuOwwYMABPPvkkbt26JdoWFRWFf/75R7Ru165diIqKwooVKyweMzAwEAUF4lJkGo0GMpkMMpmMv7tEREREROUQPwcQUXnHcZCIqHQxgE5Ebmk5dHOeNwJwHQyeE7mbRYsWYdCgQejfvz+ys7NRo0YNAMD9+/eRn5+P9PR0pKeni/ZRqVRISkrC1atXhXUrV65EQkICPv74YwDAli1b8MEHH+D06dM4evQoGjVqhJkzZ2Lz5s0mgXUiIiIiIiIiIiIiIqkxgE5EbisBDJwTuat33nkHALB//37R+tdeew0rV660+Th16tQRBca/+OILaLVafPHFF6hVqxZSUlKwZcsWTJ3K+Q6JiIiIiIiIiIiIyPkYQCciIiK7yWQyu/epX7++ybqnnnpK9Fyj0WDGjBmYMWOGw30jIiIiIiIiIiIiInKUl6s7QERERERERERERERERERE5A4YQCciIiIiIiIiIiIiIiIiIgID6ERERERERERERERERERERAAYQCciIiIiIiIiIiIiIiIiIgLAADoREREREREREREREREREREABtCJiIiIiIiIiIiIiIiIiIgAMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiIiFzA29sbM2fOxM2bN5Gbm4sbN25g2rRpkMlkonafffYZEhISkJubi71796J58+Yu6jERERERERERERERlQcMoBMREVGpmzx5Mt5++2289957ePDBBzFp0iRMnDgRY8aMEdpMmjQJH3zwAd577z20b98eSUlJ+PvvvxEcHOzCnhMRERERERERERGRJ2MAnYiIiEpd586dsWnTJmzfvh1xcXHYsGEDoqOj0a5dO6HN+++/jy+//BIbN27ExYsXMXz4cAQGBmLQoEEu7DkREREREREREREReTIG0ImIiKjUHThwAN27d0fjxo0BAI888gi6dOmC7du3AwDq16+PiIgIREdHC/solUrs378fjz76qMXj+vn5ISQkRLQQEREREREREREREdnKx9UdICIiovJn1qxZqFixIi5fvgyNRgNvb29MnToVf/zxBwAgPDwcAJCcnCzaLzk5GXXr1rV43ClTpmD69OlO6zcREREREREREREReTYG0InKiRo1ari6C0Tkxkp7jBg4cCCGDBmCQYMG4eLFi2jVqhXmz5+Pu3fvYtWqVUI7rVYr2k8mk5msMxQZGYm5c+cKz0NCQpCQkMBMdCKyypPHCE9+bUQkDU8eJzz5tRFRyXn6GOHpr4+ISoZjBJF1DKATeTh9UOzUqVMu7gkRlQU1atTA3bt3nX6e2bNn4+uvv8aaNWsAABcuXEDdunUxZcoUrFq1CklJSQB0mej6xwBQvXp1k6x0Q0qlEkqlUniu/zCQkJDgjJdBRB4mJCQE2dnZru6GJDj+EZG9OAYSUXnlSeMfwDGQiOzjaWMgkVQYQCfycKdPn0abNm2sBpwMBQcH48qVK2jatCnkcrmTe+e5eB2lwesoDVuvY40aNXD69OlS6VNgYCAKCgpE6zQaDby8vAAAsbGxSExMRI8ePXDmzBkAgK+vL7p27YrJkyfbfJ67d++iVq1aNn8Q0Ges27MPmeJ1lAavozRsvY4hISGlcgNRabF3/AP4OycVXseS4zWUhj3XsbyPgfydkwavozR4HaVRXt8DAhwDXYXXURq8jtIoz2MgkVQYQCcqB+wJiOnvUk1MTOSblBLgdZQGr6M0bL2OpfmGecuWLZg6dSpu376NixcvonXr1vjggw+wfPlyoc38+fPx8ccf49q1a7h27Ro+/vhj5ObmYvXq1Xady5HXlZ2dzd85CfA6SoPXURrFXUdPvMaOjuv8nZMGr2PJ8RpKw5br6InXme8BXYfXURq8jtIoj+8BAY6BrsTrKA1eR2mU1zGQSAoMoBMREVGpGzNmDGbOnInFixejevXquHv3Ln788UfMmDFDaPPNN9+gQoUKWLx4MSpVqoSjR4+iZ8+erEZARERERERERERERE7DADoRERGVOrlcjvHjx2P8+PFW233++ef4/PPPS6lXRERERERERERERFTeebm6A0TkXhQKBaZPnw6FQuHqrpRpvI7S4HWUBq+j7XitpMHrKA1eR2nwOtqO10oavI4lx2soDV5H2/FaSYPXURq8jtLgdbQdr5U0eB2lwesoDV5HopKTAdC6uhNERERERERERERERERERESuxgx0IiIiIiIiIiIiIiIiIiIiMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiMjB49Gjdv3kReXh5OnDiBLl26uLpLbuujjz7CsWPHkJWVheTkZGzcuBFNmjQxaffZZ58hISEBubm52Lt3L5o3b+6C3pYdH330EbRaLebNmydaz+tYvJo1ayIqKgqpqanIycnB6dOn0aZNG1EbXkfLOP7Zh2Ogc3AMdBzHwJLhGGgfjoHS4/hXMhwDS4ZjoO04/jkHx0DHcfwrGY5/9uEY6BwcAx3HMZDIubRcuHDhAkD78ssvaxUKhXbkyJHaZs2aaefNm6fNzs7W1q5d2+V9c8dlx44d2uHDh2ubN2+ufeSRR7RbtmzR3rp1SxsYGCi0mTRpkvb+/fvaAQMGaB966CHt77//rk1ISNAGBwe7vP/uuLRr10578+ZN7ZkzZ7Tz5s3jdbRjCQsL08bGxmqXL1+ubd++vbZu3brabt26aRs0aMDraMPC8c/+hWOg9AvHQMcXjoElWzgG2r9wDJR24fhXsoVjYMkWjoH2LRz/pF84Bjq+cPwr2cLxz/6FY6D0C8dAxxeOgVy4OH1xeQe4cOHiJsuRI0e0ixcvFq2LiYnRfvXVVy7vW1lYqlatqtVqtdrHH39cWHf37l3tpEmThOd+fn7ajIwM7Ztvvuny/rrbEhQUpL1y5Yq2e/fu2r1794reNPM6Fr9ERkZq//33X6tteB0tLxz/Sr5wDCzZwjGwZAvHwJItHANLvnAMdHzh+FfyhWNgyRaOgSVbOP6VbOEYWLKF41/JFo5/JV84BpZs4RhYsoVjIBcuzl1Ywp2IAAC+vr5o27YtoqOjReujo6Px6KOPuqhXZUvFihUBAOnp6QCA+vXrIyIiQnRNlUol9u/fz2tqxqJFi7Bt2zbs3r1btJ7X0Tb9+vXDiRMnsHbtWiQnJ+PUqVMYNWqUsJ3X0TKOf9LgGFgyHANLhmOg4zgGSoNjoOM4/pUcx0DHcQwsOY5/JcMxsGQ4/jmO4580OAaWDMfAkuEYSORcDKATEQCgatWq8PHxQXJysmh9cnIywsPDXdSrsmXu3Ln477//cPHiRQAQrhuvafEGDhyINm3aYMqUKSbbeB1t06BBA4wePRrXrl3DM888gyVLlmDhwoUYOnQoAF5Hazj+SYNjoOM4BpYcx0DHcQyUBsdAx3D8kwbHQMdxDCw5jn+O4xhYchz/HMfxTxocAx3HMbDkOAYSOZePqztARO5Fq9WKnstkMpN1ZOr777/HI488gi5duphs4zW17oEHHsCCBQvQs2dPKBQKi+14Ha3z8vLCiRMnMHXqVADAmTNn8NBDD2H06NGIiooS2vE6WsZr4ziOgY7jGCgNjoElx2vjOI6BjuH4Jx2OgSXHa+MYjn+O4xgoDY5/Jcdr4ziOgY7jGCgNjoFEzsUMdCICAKSmpkKtVpvcfVa9enWTu9RIbOHChejXrx+eeuopJCQkCOuTkpIAgNe0GG3btkWNGjVw8uRJqFQqqFQqPPnkkxg7dixUKpVwrXgdrUtMTERMTIxo3aVLl1CnTh0A/H20huNfyXAMLBmOgdLgGOg4joElwzHQcRz/pMMx0HEcAx3H8a9kOAZKg+Of4zj+lQzHwJLhGCgNjoFEzsUAOhEBAFQqFU6ePIkePXqI1vfo0QOHDh1yUa/c33fffYf//e9/6NatG27duiXaFhsbi8TERNE19fX1RdeuXXlNDezevRstWrRAq1athOX48eP47bff0KpVK9y8eZPX0QYHDx5E06ZNReuaNGmCuLg4APx9tIbjn+M4BpYcx0BpcAx0HMdAx3EMLBmOf9LhGOg4joGO4fhXchwDpcHxz3Ec/xzHMbDkOAZKg2MgkfNpuXDhwgWA9uWXX9YqFArtiBEjtM2aNdPOnTtXm52dra1Tp47L++aOy6JFi7QZGRnaJ554QlujRg1hCQgIENpMmjRJm5GRoX3++ee1Dz30kPa3337TJiQkaIODg13ef3de9u7dq503bx6vox1Lu3bttEqlUjtlyhRtw4YNta+++qpWLpdrBw0axOtow8Lxz/6FY6DzFo6B9i8cA0u2cAy0f+EY6JyF459jC8fAki0cA+1bOP45b+EYaP/C8a9kC8c/+xeOgc5bOAbav3AM5MLF6YvLO8CFCxc3WkaPHq2NjY3V5ufna0+cOKF9/PHHXd4nd10sGT58uKjdZ599pr179642Ly9Pu2/fPu1DDz3k8r67+2L8ppnX0balb9++2nPnzmnz8vK0MTEx2lGjRpm04XW0vHD8s2/hGOi8hWOgYwvHwJItHAPtWzgGOmfh+Of4wjGwZAvHQNsXjn/OWzgGOrZw/CvZwvHPvoVjoPMWjoGOLRwDuXBx3iIrfEBERERERERERERERERERFSucQ50IiIiIiIiIiIiIiIiIiIiMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiIiIiIiIiIiIiIiIiICAAD6ERERERERERERERERERERAAYQCciIiIiIiIiIiIiIiIiIgLAADoREREREREREREREREREREABtCJiIiIiIiIiIiIiIiIiIgAMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiIiIiIiIiIiIiIiIiICAAD6ERERERERERERERERERERAAYQCciIiIiIiIiIiIiIiIiIgLAADoREREREREREREREREREREABtCJiIiIiIiIiIiIiIiIiIgAMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiIiIiIiIiIiIiIiIiICAAD6ERERERERERERERERERERAAYQCciIiIiIiIiIiIiIiIiIgLAADoREREREREREREREREREREABtCJiIiIiIiIiIiIiIiIiIgAMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiIiIiIiIiIiIiIiIiICAAD6ERERERERERERERERERERAAYQCciIiIiIiIiIiIiIiIiIgLAADoREREREREREREREREREREABtCJiIiIiIiIiIiIiIiIiIgAMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiIiIiIiIiIiIiIiIiICAAD6ERERERERERERERERERERAAYQCciIiIiIiIiIiIiIiIiIgLAADoREREREREREREREREREREABtCJiIiIiIiIiIiIiIiIiIgAMIBORERERERERERERET0//bsQAAAAABA0P7Ui5RGAFAJdAAAAAAAAACoBDoAAAAAAAAAVDXMGo4aXNsxgwAAAABJRU5ErkJggg==", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "TEMP1 = ambit_run()\n", - "TEMP1.add(num=50, freq = 5, actinic=0)\n", - "TEMP1.add(num=100, freq = 5, actinic=250)\n", - "TEMP1.add(num=200, freq = 5, actinic=0)\n", - "ret_M0 = TEMP1.run()\n", - "TEMP1.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "norm = lambda x: (x - x[-1]) / (x.max() - x[-1])\n", - "norm = lambda x:x - x[:4].mean()" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\hjcbj\\AppData\\Local\\Temp\\ipykernel_4868\\2471166036.py:1: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`). Consider using `matplotlib.pyplot.close()`.\n", - " plt.figure()\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "977f06e1bcc2475487cd817bfe46ae8f", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "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Bsa9duvWLdRqNfny5cPLyytNY84qUtL333zzDadPn2bOnDkAeHh4EBISwqlTp/jf//4nd3xEqpERwHQWFRXFxYsXadKkSbzXmzRpwpkzZxLdx83NLUH7pk2b4u7ujk6nS7NYs5qU9D3EjPz98ccf9OjRg/3796d1mFlScvs+MDCQ0qVLU758+bhtyZIl3L59m/Lly3Pu3Ln0Cj3TS8nP/enTp3FycsLCwiLutWLFihEdHc2TJ0/SNN6sJCV9b25ujl6vj/da7CMQsXd+hEgtBp+Jkt222LIA/fv3V1xdXZV58+YpQUFBirOzswIoM2bMUFatWhXXPrYMzNy5cxVXV1elf//+UgYmnfq+W7duSmRkpDJ06FDFwcEhbrO2tjb4tWS2Lbl9/99NZgGnX99bWFgo3t7eyubNm5USJUooderUUe7cuaMsW7bM4NeS2bbk9n3fvn2VyMhIZciQIUqhQoWUmjVrKufPn1fOnj1r8GuRLcttBg8gW25Dhw5VHjx4oISHhyvu7u5KnTp14t5buXKlcvTo0Xjt69atq1y8eFEJDw9X7t+/rwwePNjg15BZt+T0/dGjR5XErFy50uDXkRm35P7c/3uTBDB9+7548eLKoUOHlJCQEMXb21uZM2eOYmpqavDryIxbcvt+2LBhyvXr15WQkBDFx8dHWbNmjeLk5GTw65Ata22qt/8ihBBCCCGyCXkGUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim5EEUAghhBAim/k/9thSwd+e4mMAAAAASUVORK5CYII=", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.figure()\n", - "plt.plot(ret_EPT['env_leaf_t']/60000, norm(ret_EPT['env_leaf']), label = 'empty')\n", - "plt.plot(ret_C1['env_leaf_t']/60000, norm(ret_C1['env_leaf']), label = 'c1')\n", - "plt.plot(ret_C2['env_leaf_t']/60000, norm(ret_C2['env_leaf']), label = 'c1')\n", - "\n", - "plt.plot(ret_M0['env_leaf_t']/60000, norm(ret_M0['env_leaf']), label = 'm0')\n", - "plt.plot(ret_M1['env_leaf_t']/60000, norm(ret_M1['env_leaf']), label = 'm1')\n", - "\n", - "\n", - "plt.plot(ret_OLD['env_leaf_t']/60000, norm(ret_OLD['env_leaf']), label = 'old')\n", - "\n", - "\n", - "\n", - "plt.legend()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/.vscode/examples/traces_loads_qProtocols.ipynb b/.vscode/examples/traces_loads_qProtocols.ipynb deleted file mode 100644 index 2adc495..0000000 --- a/.vscode/examples/traces_loads_qProtocols.ipynb +++ /dev/null @@ -1,550 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import serial\n", - "import time\n", - "import matplotlib.pyplot as plt\n", - "from IPython import display\n", - "plt.style.use(\"dark_background\")" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [], - "source": [ - "def parse_data(l:str):\n", - " if len(l) < 10: return \"\", []\n", - " if l[:5] != \"Data:\": return \"\", []\n", - " s = l.split('\\t')\n", - "\n", - " if len(s) < 2: return \"\", []\n", - " _t = s[0].split(',')\n", - " if len(_t) < 2: return \"\", []\n", - " _type = _t[0][5:]\n", - " _size = int(_t[1][7:])\n", - " arr = np.fromstring(s[1], sep = ',', dtype=np.uint32)\n", - " if _type == \"ENV\":\n", - " env_type = np.bitwise_and(arr, 0xF000) >> 12\n", - " env_read = np.bitwise_and(arr, 0x0FFF)\n", - " env_time = np.bitwise_and(arr, 0xFFFF0000) >> 10\n", - " leaftemp_t = env_time[env_type == 4]\n", - " leaftemp = env_read[env_type == 4] / 10\n", - " timestemp_t = env_time[env_type < 4]\n", - " timestemp = env_type[env_type < 4]\n", - " return \"ENV\", [timestemp_t, timestemp, leaftemp_t, leaftemp]\n", - " elif _type in [\"Fluo\", \"Fluoref\", \"SUN\", \"leaf\", \"730\", \"730ref\"]:\n", - " return _type, [arr]\n", - " else:\n", - " print(_type)\n", - "\n", - " return \"\", []\n", - "\n", - "def gen_cmd_arr_line(mode:int, ir:int, num:int, freq:int, actinic:int, sub:int)->list:\n", - " return [mode, ir, num//256, num%256, freq//256, freq%256, actinic, sub]\n", - "\n", - "def ambit_run_arr(cmd_str, port = \"COM31\"):\n", - " timeout = 60\n", - " s = ''\n", - " _l = 0\n", - " result = {\"env_t0\": time.time()}\n", - "\n", - " with serial.Serial(port, baudrate=115200) as ser:\n", - " t0 = time.time()\n", - " ser.write(cmd_str.encode())\n", - " while time.time() - t0 < timeout:\n", - " try:\n", - " if ser.in_waiting > 1:\n", - " c = ser.read()\n", - " if (c > bytes([0])) and (c < bytes([128])):\n", - " s += c.decode()\n", - " _l += 1\n", - " if (c == b\"\\n\") or (c == b\"\\r\"):\n", - " l = s\n", - " s = ''\n", - " _l = 0\n", - " if l[:-1] == \"BAD COMMAND\":\n", - " timeout = 1\n", - " continue\n", - " if l[:-1] == \"Data sent\":\n", - " timeout = .1\n", - " continue\n", - " #print(l)\n", - " d_type, data = parse_data(l)\n", - " if d_type == \"ENV\":\n", - " result.update({\"env_stamp_t\": data[0], \"env_stamp\": data[1], \"env_leaf_t\": data[2], \"env_leaf\": data[3]})\n", - " elif d_type != \"\":\n", - " result.update({d_type: data[0][:-1]})\n", - " else:\n", - " time.sleep(.1)\n", - " except KeyboardInterrupt:\n", - " break\n", - " return result\n", - "\n", - "def calc_arr_param(cmd: list, persist:bool):\n", - " cmd_arr = np.reshape(cmd, (-1, 8))\n", - " arr_length = cmd_arr.shape[0]\n", - " cmd_str = \"arrun,\" + str(arr_length) + \",\" + str(persist) + \",\"+ str(cmd)[1:-1] + \",\\n\"\n", - " cmd_str = cmd_str.replace(\" \", \"\")\n", - "\n", - " num_pts = cmd_arr[:, 2] * 256 + cmd_arr[:, 3]\n", - " mea_feq = 1 / (cmd_arr[:, 4] * 256 + cmd_arr[:, 5])\n", - " _t, mea_tml = 0, []\n", - " for _n, _f in zip(num_pts, mea_feq):\n", - " mea_tml += (np.arange(_n) * _f + _t).tolist()\n", - " _t = mea_tml[-1]\n", - " mea_tml = np.array(mea_tml) * 0.854\n", - " return cmd_str, mea_tml \n" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [], - "source": [ - "class ambit_run():\n", - " def __init__(self, persist:bool = False) -> None:\n", - " self.cmd_list = []\n", - " self.led_persist = int(persist)\n", - " \n", - " def add(self, mode:int = 2, ir:int = 0, num:int = 100, freq:int = 100, actinic:int = 0, sub:int = 1):\n", - " self.cmd_list += [mode, ir, num//256, num%256, freq//256, freq%256, actinic, sub]\n", - " return\n", - " def run(self):\n", - " cmd_str, self.mea_tml = calc_arr_param(self.cmd_list, self.led_persist)\n", - " self.result = {\"env_tl\": self.mea_tml, \"env_t0\": time.time()}\n", - " self.result.update(ambit_run_arr(cmd_str))\n", - " return self.result\n", - "\n", - " def plot(self):\n", - " result = self.result\n", - " mea_tml = self.mea_tml\n", - " plot_list = [_k for _k in result.keys() if _k[:4] != \"env_\"]\n", - " fig, axes = plt.subplots(1, len(plot_list) + 1, sharex = True, figsize = (20, 6))\n", - " for n, _p in enumerate(plot_list):\n", - " axes[n + 1].plot(mea_tml[1:], result[_p][1:])\n", - " axes[n + 1].set_title(_p)\n", - " axes[0].plot(mea_tml[1:], (result[\"Fluo\"]/ result[\"Fluoref\"])[1:])\n", - " # axes[0].plot(result['env_stamp_t'][:-1]/854, result['env_stamp'][:-1]* np.mean((result[\"Fluo\"]/ result[\"Fluoref\"])[1:]) + .1)\n", - " ax = axes[0].twinx()\n", - " ax.plot(result['env_leaf_t']/1000, result['env_leaf'], 'r.-')\n", - " \n" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "cmd: set_currents\n", - "\n", - "Currents set to 110, 0, 80\n", - "\n" - ] - } - ], - "source": [ - "def set_led(port = \"COM31\"):\n", - " cmd_str = \"set_currents,110,0,80,\\n\"\n", - " #cmd_str = \"set_gains,1,4,4,1,5,5,\\n\"\n", - "\n", - " s = \"\"\n", - " with serial.Serial(port, baudrate=115200) as ser:\n", - " t0 = time.time()\n", - " ser.write(cmd_str.encode())\n", - " while time.time() - t0 < 1:\n", - " if ser.in_waiting > 0:\n", - " c = ser.read()\n", - " if (c > bytes([0])) and (c < bytes([128])):\n", - " s += c.decode()\n", - " if (c == b\"\\n\") or (c == b\"\\r\"):\n", - " l = s\n", - " s = ''\n", - " print(l)\n", - "\n", - "\n", - "set_led()" - ] - }, - { - "cell_type": "code", - "execution_count": 78, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "d02bd63e4cbd4c8799d429a5f3ddd235", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAB9AAAAJYCAYAAAA6431rAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3gU5doG8HtnN5teKCGhIyC9SQ2IgCD9CCoqKioqHBR7O9JERY6ioqIoiqJyAI+cDxVEpCpioUuVDkqHJBCSkF42me+P3ZmdmS2ZTTZbkvvHNRfJ7szsu8lOdnae93keAwARRERERERERERERERERERE1Zzg7wEQEREREREREREREREREREFAgbQiYiIiIiIiIiIiIiIiIiIwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQFgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQFgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQFgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQFgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQFgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgAMoBMREREREREREREREREREQFgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIKAiMHTsWoig6XWbPng0AOHXqFBYuXOjnkRIFl6p0bIWEhODjjz/GxYsXYbFYsHfvXn8PiahSdO/eHcuXL8eZM2dQUFCAlJQUbN26FW+//ba8zqlTp7Bq1Sqn23fp0gWiKGLs2LHybS+//DJEUURqaiqioqIctnG3P6LqTnovbdy4caU9RqdOnfDLL78gMzMToijiqaeeqrTHIgpGCxcudHlOK4oievToAQB44oknsG3bNly+fBkFBQU4c+YMli5dijZt2jjd7+OPP44jR46goKAAJ0+exEsvvQSTyeTLp0bkd8F+fPFzIhGVF9/xiYiIKGg88MADOHr0qOq2ixcv+mk0RFVHVTi2Jk6ciEceeQSPP/44du/ejZycHH8Picjrhg0bhu+//x6//PILXnjhBSQnJ6Nu3bro2rUr7rrrLjz//PMV2n+dOnXwwgsv4KWXXvLSiInIG7744gtERkbirrvuQkZGBk6fPu3vIREFlJkzZ2L+/PkOt69atQqFhYX4448/AAC1atXC2rVrsX//fmRkZKBp06aYPHkyduzYgS5duuD48ePytlOnTsXMmTPxxhtvYMOGDejWrRv+/e9/o379+nj44Yd99tyI/C3Yjy9+TiSiihC5cOHChQsXLlwCeRk7dqwoiqLYpUsXl+ucOnVKXLhwod/HyoVLMC2BfGyFh4d7tP6nn34q5ubm+v1nyoVLZS6//PKLeOLECdFoNDrcZzAY5K9PnTolrlq1yuk+unTpIoqiKI4dO1a+7eWXXxZFURTXrFkjZmdniwkJCapt3O2PC5fqvkjvpY0b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FhQUiKdPnxZffvll0WQy+f15cuHij8Ufxxc/J3LhwsWfi8H2BRERERERERERERERERERUbXGRmZERERERERERERERERERERgAJ2IiIiIiIiIiIiIiIiIiAgAA+hEREREREREREREREREREQAGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIAAAmfw+AiCrfddddh9TUVH8Pg8hnEhISsHfvXn8Pw0G9evWQnZ3t72EQ+Ux0dDQuXrzo72E44LFI1U0gHos8Dqk64rFI5H+BeBwCPBap+uGxSBQYAvVYJAoEDKATVXHXXXcd9uzZ4+9hEPlc586dAyqIXq9ePVy4cMHfwyDyufr16wfUhzEei1RdBdKxyOOQqjMei0T+F0jHIcBjkaovHotEgSHQjkWiQMEAOlEVJ2Wed+7cmVnoVC0kJCRgz549Afd6l2Yw169fn7OZqVqIjo7GhQsXAu71zmORqptAPBZ5HFJ1xGORyP8C8TgEeCxS9cNjkSgwBOqxSBQoGEAnqiZSU1M5k4woAGRnZ/PElCgA8Fgk8j8eh0SBgcciUWDgsUgUGHgsEhERAAj+HgAREREREREREREREREREVEgYACdiIiIiIiIiIiIiIiIiIgIDKATEREREREREREREREREREBYACdiIiIiIiIiIiIiIiIiIgIAAPoREREREREREREREREREREABhAJyIiIiIiCnj16tXDkiVLkJaWhtzcXOzduxedO3dWrdOqVSusXLkSmZmZyMrKwrZt29CgQQP5/vnz5+Ovv/5CXl4eLl26hO+++w4tW7ZU7SMuLg6LFy9GZmYmMjMzsXjxYsTGxqrWadiwIb7//nvk5OTg8uXLeP/99xESElJ5T56IiIiIiIiIyIcYQCciIiIiIgpgcXFx2LJlC4qLizF06FC0adMGzz33HDIzM+V1mjZtis2bN+Po0aPo168fOnbsiJkzZ6KgoEBeZ/fu3XjwwQfRunVrDB48GAaDARs2bIAg2D8WfvXVV+jUqROGDBmCIUOGoFOnTliyZIl8vyAIWL16NSIjI9G7d2/cddddGDVqFN555x2f/CyIiIiIiIiIiHxB5MKFS9Vd6tWrJ4qiKNarV8/vY+HCxRdLoL7mo6OjRVEUxejoaL+PhQsXXyyB+poP1HFx4eJu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GjTIzir38sc//lFz585Vfn6+HTyXpDvvvNO1XORV2llZWUm5cnvzu6tUfeiwyrdsa/dzAQAyw461G/TavEfk9/n04uw5OrxnX7pXCei0srOztW7dOt17773asGGDHnvsMS1atEgzZsywl3nxxRf1+uuva+PGjXrttdc0ZcoUndb7VA3MKdSOtRvsMsR//OMf9dRTT2nDhg366U9/qq1bt7pKwy9YsMDOSH/mmWd0++2367bbbnN9Hn3ggQe0Zs0affTRR5o3b55++ctf6uc///kJv694nyVnzZql6upqe5SXc4lwZ3Fk334d3LlbW1e9l+5VQQwn8r0uFXNx78YtqjlyVFtWrpYkbV65WnVVx7Xn000xH7N19Xv2MtWHj2j/ts/tfWzH2vVqqK7RZ++vtZ/Psm3V+5Kkz99fFxUYPLx3n+vzyefvrY15UffWMu/9efO7q6KXjdj31772hg7v3adN76yM+f6c9n66WTVHjtrr7nT84CFVfLZdW95d7bp9a1nL9vlkk44fallmZXPFoO0frFNDdY12bfgkZjA8cp23OLZhrPfutPuTjaqrOu553+G9+3RwV7jtyGO3/0QN1TV66sd3ey5/opb87mHVVR3Xy/89N+Yy21Z/YL/3Q7v36si+/Ul57fZK91z0UnusSns3brZ/tvajWD9L0if/fNv+19rOR/dXqHLHrpSsI5Jra9l7OlpeoUO79iS0/L7NW5uP4WXufWHzu6uaf+/bw0lAO9ZuUEN1jT5vOT477f74U9UePda+lQfgYhgMxsk7BgwYYIwxZsCAAR32mllZWWl/320dAyUTkIxxDH/L7eleN0Ziw9rnhw0bZoqLi+2Rn59vJJmioiJTUVFhfvKTn7get3DhQtPU1GSeeuop1+033nijaWxsjHqduro6s2bNmpjr8e6775oFCxbYr2+tV11dncnKyjJ5eXnG7/eb6667zvW4BQsWmLfffjsp26Izz0VG5x/FxcXGGGOKi4vTvi6dYb0YjFSNTNznM3GdGF1v7Nq1yyxatMh12+2332727dsX93Hbtm0zd911l5FkhgwZYowxZtq0aa5l/vKXv5g//elPrtuc+731uXDMmDExX2f8+PHGGGP69evnef+ll15qjDGmpKTEdfuGDRvM/fff7/mY/Px81+djaz2Yi51j8Nm+/SMVf3/a8r0uVXMxch9JZJ9x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", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "ename": "ValueError", - "evalue": "5 is not a valid MouseButton", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", - "File \u001b[1;32mc:\\Users\\hjcbj\\anaconda3\\Lib\\site-packages\\ipympl\\backend_nbagg.py:279\u001b[0m, in \u001b[0;36mCanvas._handle_message\u001b[1;34m(self, object, content, buffers)\u001b[0m\n\u001b[0;32m 276\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmanager\u001b[38;5;241m.\u001b[39mhandle_json(content)\n\u001b[0;32m 278\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m--> 279\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmanager\u001b[38;5;241m.\u001b[39mhandle_json(content)\n", - "File \u001b[1;32mc:\\Users\\hjcbj\\anaconda3\\Lib\\site-packages\\matplotlib\\backends\\backend_webagg_core.py:464\u001b[0m, in \u001b[0;36mFigureManagerWebAgg.handle_json\u001b[1;34m(self, content)\u001b[0m\n\u001b[0;32m 463\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mhandle_json\u001b[39m(\u001b[38;5;28mself\u001b[39m, content):\n\u001b[1;32m--> 464\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcanvas\u001b[38;5;241m.\u001b[39mhandle_event(content)\n", - "File \u001b[1;32mc:\\Users\\hjcbj\\anaconda3\\Lib\\site-packages\\matplotlib\\backends\\backend_webagg_core.py:264\u001b[0m, in \u001b[0;36mFigureCanvasWebAggCore.handle_event\u001b[1;34m(self, event)\u001b[0m\n\u001b[0;32m 261\u001b[0m e_type \u001b[38;5;241m=\u001b[39m event[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtype\u001b[39m\u001b[38;5;124m'\u001b[39m]\n\u001b[0;32m 262\u001b[0m handler \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mhandle_\u001b[39m\u001b[38;5;132;01m{\u001b[39;00me_type\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m,\n\u001b[0;32m 263\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhandle_unknown_event)\n\u001b[1;32m--> 264\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m handler(event)\n", - "File \u001b[1;32mc:\\Users\\hjcbj\\anaconda3\\Lib\\site-packages\\matplotlib\\backends\\backend_webagg_core.py:294\u001b[0m, in \u001b[0;36mFigureCanvasWebAggCore._handle_mouse\u001b[1;34m(self, event)\u001b[0m\n\u001b[0;32m 291\u001b[0m guiEvent \u001b[38;5;241m=\u001b[39m event\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mguiEvent\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m 292\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m e_type \u001b[38;5;129;01min\u001b[39;00m [\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mbutton_press\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mbutton_release\u001b[39m\u001b[38;5;124m'\u001b[39m]:\n\u001b[0;32m 293\u001b[0m MouseEvent(e_type \u001b[38;5;241m+\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m_event\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;28mself\u001b[39m, x, y, button,\n\u001b[1;32m--> 294\u001b[0m modifiers\u001b[38;5;241m=\u001b[39mmodifiers, guiEvent\u001b[38;5;241m=\u001b[39mguiEvent)\u001b[38;5;241m.\u001b[39m_process()\n\u001b[0;32m 295\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m e_type \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mdblclick\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[0;32m 296\u001b[0m MouseEvent(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mbutton_press_event\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;28mself\u001b[39m, x, y, button, dblclick\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[0;32m 297\u001b[0m modifiers\u001b[38;5;241m=\u001b[39mmodifiers, guiEvent\u001b[38;5;241m=\u001b[39mguiEvent)\u001b[38;5;241m.\u001b[39m_process()\n", - "File \u001b[1;32mc:\\Users\\hjcbj\\anaconda3\\Lib\\site-packages\\matplotlib\\backend_bases.py:1271\u001b[0m, in \u001b[0;36mEvent._process\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 1269\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_process\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[0;32m 1270\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Process this event on ``self.canvas``, then unset ``guiEvent``.\"\"\"\u001b[39;00m\n\u001b[1;32m-> 1271\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcanvas\u001b[38;5;241m.\u001b[39mcallbacks\u001b[38;5;241m.\u001b[39mprocess(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname, \u001b[38;5;28mself\u001b[39m)\n\u001b[0;32m 1272\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_guiEvent_deleted \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n", - "File \u001b[1;32mc:\\Users\\hjcbj\\anaconda3\\Lib\\site-packages\\matplotlib\\cbook.py:303\u001b[0m, in \u001b[0;36mCallbackRegistry.process\u001b[1;34m(self, s, *args, **kwargs)\u001b[0m\n\u001b[0;32m 301\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[0;32m 302\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mexception_handler \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m--> 303\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mexception_handler(exc)\n\u001b[0;32m 304\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 305\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m\n", - "File \u001b[1;32mc:\\Users\\hjcbj\\anaconda3\\Lib\\site-packages\\matplotlib\\cbook.py:87\u001b[0m, in \u001b[0;36m_exception_printer\u001b[1;34m(exc)\u001b[0m\n\u001b[0;32m 85\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_exception_printer\u001b[39m(exc):\n\u001b[0;32m 86\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m _get_running_interactive_framework() \u001b[38;5;129;01min\u001b[39;00m [\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mheadless\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m]:\n\u001b[1;32m---> 87\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exc\n\u001b[0;32m 88\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 89\u001b[0m traceback\u001b[38;5;241m.\u001b[39mprint_exc()\n", - "File \u001b[1;32mc:\\Users\\hjcbj\\anaconda3\\Lib\\site-packages\\matplotlib\\cbook.py:298\u001b[0m, in \u001b[0;36mCallbackRegistry.process\u001b[1;34m(self, s, *args, **kwargs)\u001b[0m\n\u001b[0;32m 296\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m func \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 297\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 298\u001b[0m func(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 299\u001b[0m \u001b[38;5;66;03m# this does not capture KeyboardInterrupt, SystemExit,\u001b[39;00m\n\u001b[0;32m 300\u001b[0m \u001b[38;5;66;03m# and GeneratorExit\u001b[39;00m\n\u001b[0;32m 301\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n", - "File \u001b[1;32mc:\\Users\\hjcbj\\anaconda3\\Lib\\site-packages\\matplotlib\\backend_bases.py:2567\u001b[0m, in \u001b[0;36mbutton_press_handler\u001b[1;34m(event, canvas, toolbar)\u001b[0m\n\u001b[0;32m 2565\u001b[0m toolbar \u001b[38;5;241m=\u001b[39m canvas\u001b[38;5;241m.\u001b[39mtoolbar\n\u001b[0;32m 2566\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m toolbar \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m-> 2567\u001b[0m button_name \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mstr\u001b[39m(MouseButton(event\u001b[38;5;241m.\u001b[39mbutton))\n\u001b[0;32m 2568\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m button_name \u001b[38;5;129;01min\u001b[39;00m rcParams[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mkeymap.back\u001b[39m\u001b[38;5;124m'\u001b[39m]:\n\u001b[0;32m 2569\u001b[0m toolbar\u001b[38;5;241m.\u001b[39mback()\n", - "File \u001b[1;32mc:\\Users\\hjcbj\\anaconda3\\Lib\\enum.py:712\u001b[0m, in \u001b[0;36mEnumType.__call__\u001b[1;34m(cls, value, names, module, qualname, type, start, boundary)\u001b[0m\n\u001b[0;32m 687\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 688\u001b[0m \u001b[38;5;124;03mEither returns an existing member, or creates a new enum class.\u001b[39;00m\n\u001b[0;32m 689\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 709\u001b[0m \u001b[38;5;124;03m`type`, if set, will be mixed in as the first base class.\u001b[39;00m\n\u001b[0;32m 710\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 711\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m names \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m: \u001b[38;5;66;03m# simple value lookup\u001b[39;00m\n\u001b[1;32m--> 712\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mcls\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;21m__new__\u001b[39m(\u001b[38;5;28mcls\u001b[39m, value)\n\u001b[0;32m 713\u001b[0m \u001b[38;5;66;03m# otherwise, functional API: we're creating a new Enum type\u001b[39;00m\n\u001b[0;32m 714\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mcls\u001b[39m\u001b[38;5;241m.\u001b[39m_create_(\n\u001b[0;32m 715\u001b[0m value,\n\u001b[0;32m 716\u001b[0m names,\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 721\u001b[0m boundary\u001b[38;5;241m=\u001b[39mboundary,\n\u001b[0;32m 722\u001b[0m )\n", - "File \u001b[1;32mc:\\Users\\hjcbj\\anaconda3\\Lib\\enum.py:1128\u001b[0m, in \u001b[0;36mEnum.__new__\u001b[1;34m(cls, value)\u001b[0m\n\u001b[0;32m 1126\u001b[0m ve_exc \u001b[38;5;241m=\u001b[39m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m%r\u001b[39;00m\u001b[38;5;124m is not a valid \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m%\u001b[39m (value, \u001b[38;5;28mcls\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__qualname__\u001b[39m))\n\u001b[0;32m 1127\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m result \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m exc \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m-> 1128\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m ve_exc\n\u001b[0;32m 1129\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m exc \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 1130\u001b[0m exc \u001b[38;5;241m=\u001b[39m \u001b[38;5;167;01mTypeError\u001b[39;00m(\n\u001b[0;32m 1131\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124merror in \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m._missing_: returned \u001b[39m\u001b[38;5;132;01m%r\u001b[39;00m\u001b[38;5;124m instead of None or a valid member\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m 1132\u001b[0m \u001b[38;5;241m%\u001b[39m (\u001b[38;5;28mcls\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m, result)\n\u001b[0;32m 1133\u001b[0m )\n", - "\u001b[1;31mValueError\u001b[0m: 5 is not a valid MouseButton" - ] - } - ], - "source": [ - "IR1 = ambit_run(False)\n", - "IR1.add(mode = 1, ir = 0, num = 20, freq = 2, actinic = 0, sub = 1)\n", - "IR1.add(mode = 1, ir = 0, num = 20, freq = 2, actinic = 5, sub = 1)\n", - "IR1.add(mode = 1, ir = 1, num = 20, freq = 2, actinic = 5, sub = 1)\n", - "IR1.add(mode = 1, ir = 0, num = 20, freq = 2, actinic = 5, sub = 1)\n", - "IR1.add(mode = 1, ir = 0, num = 20, freq = 2, actinic = 0, sub = 1)\n", - "IR1.run()\n", - "IR1.plot()\n" - ] - }, - { - "cell_type": "code", - "execution_count": 77, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "b360e1d6e7b644b692e2c8d5e9c88fab", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAB9AAAAJYCAYAAAA6431rAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdeXwTdf4/8FeS3he9KOU+BUTuS2BRlF0QdEXxvhVBFBcvdEVgPVhUdD1hxYtFRPy5u/hdFRE5vPBAUDkFAUHlEtpCoaUtvdv5/ZHMZD6TmWSSps0keT0fj0CbzPFJmslM5j3v99sGQAIREREREREREREREREREVGUs4d6AERERERERERERERERERERFbAADoREREREREREREREREREREYQCciIiIiIiIiIiIiIiIiIgLAADoREREREREREREREREREREABtCJiIiIiIiIiIiIiIiIiIgAMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiIiIiIiIiIiIiIiIiICAAD6ERERERERERERERERERERAAYQCciIiIiIiIiIiIiIiIiIgLAADoREREREREREREREREREREABtCJiIiIiIiIiIiIiIiIiIgAMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiIiIiIiIiIiIiIiIiICAAD6ERERERERERERERERERERAAYQCciIiIiIiIiIiIiIiIiIgLAADoREREREREREREREREREREABtCJiIiIiIiIiIiIiIiIiIgAMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiIiIiIiIiIiIiIiIiICAAD6ERERERERERERERERERERAAYQCciIiIiIiIiIiIiIiIiIgLAADoREREREREREREREREREREABtCJiIiIiIiIiIiIiIiIiIgAMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiIiIiIiIiIiIiIiIiICAAD6ERERERERERERERERERERAAYQCciIiIiIiIiIiIiIiIiIgLAADoREREREREREREREREREREABtCJiIiIiIiIiIiIiIiIiIgAMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiIiIiIiIiIiIiIiIiICAAD6ERERERERERERERERERERAAYQCciIiIiIiIiIiIiIiIiIgLAADoREREREREREREREREREREABtCJiIiIiIiIiIiIiIiIiIgAMIBOREREREREREREREREREQEgAF0IiIiIiIiIiIiIiIiIiIiAAygExERERERERERERERERERAWAAnYiIiIiIiIiIiIiIiIiICAAD6ERERERERERERERERERERAAYQCciIiIiIiIiIiIiIiIiIgLAADoRERGFgZtvvhmSJOnennnmGQDA/v37sXjx4hCPlCi8RNK2FRsbi1deeQVHjx5FbW0ttm7dGuohETWKwYMH47333sPBgwdRWVmJ/Px8fPvtt3j22WeVafbv348VK1bozj9gwABIkoSbb75Zue/RRx+FJEkoKChASkqKxzzelkcU7eR9afv27RttHX379sW6detQXFwMSZJwzz33NNq6iMLR4sWLDY9pJUnC2WefDQC46667sGHDBhw/fhyVlZU4ePAg/v3vf6NHjx66y506dSp2796NyspK/Pbbb3jkkUcQExPTlE+NKOTCffvi90QiChT3+ERERBQ2brnlFuzZs0e47+jRoyEaDVHkiIRta8qUKbjjjjswdepUbN68GWVlZaEeElHQXXjhhfjwww+xbt06PPjgg8jLy0PLli0xcOBAXHPNNXjggQcatPycnBw8+OCDeOSRR4I0YiIKhjfeeAPJycm45pprUFRUhAMHDoR6SESWMmfOHLz66qse969YsQJVVVX44YcfAABZWVlYtWoVtm/fjqKiInTq1AkPPfQQvvvuOwwYMAB79+5V5p05cybmzJmDp556CmvXrsWgQYPw+OOPo3Xr1rj99tub7LkRhVq4b1/8nkhEDSHxxhtvvPHGG2+8Wfl28803S5IkSQMGDDCcZv/+/dLixYtDPlbeeAunm5W3rcTERL+mf/3116XTp0+H/DXljbfGvK1bt07at2+f5HA4PB6z2WzKz/v375dWrFihu4wBAwZIkiRJN998s3Lfo48+KkmSJH388cdSaWmp1KJFC2Eeb8vjjbdov8n70vbt2zfaOqqrq6UFCxaE/Lnyxls43c4991xJkiTp73//u9fpunfvLkmSJM2ePVu5LzMzUyovL5deffVVYdoZM2ZIdXV10plnnhny58cbb6G8hXL74vdE3njjraluLOFOREREEcmonOaIESMgSRJGjBgh3D9hwgRs27YNFRUVOHHiBN577z107969KYdMFBYaY9tavHgxSktL0bNnT6xZswYlJSX47LPPADhL7s2aNUsp73fs2DG88cYbyM7OVuaXJAm33XYbkpKSlFKC6vLURJEiKysLhYWFqKur83hMkqQGL/9vf/sbYmJi8NhjjzV4WUTR7I9//CM+/fRTnDp1CqdPn8Y333yDkSNHCtN07twZb7zxBvbu3YvTp0/j999/x4cffoiePXsq08j73NjYWNx5553KPo6IfJs4cSLq6+vxxhtveJ3u+PHjAIDa2lrlvjFjxiAxMdGjjdHixYtht9tx6aWXBn28ROGkqbYvfk8kolBiAJ2IiIjChsPh8LgFw0MPPYQ33ngDP/30Ey677DLcc8896N27NzZs2IAuXboEZR1EVmaFbSsuLg4ffvghPv/8c1xyySV49NFHYbPZsHz5cjz00EN45513cNFFF+Ghhx7CqFGjsG7dOiQkJAAAhgwZgpUrV6K8vBxDhgxRfieKNBs2bMCQIUMwb948DB48OOh9Ig8ePIiXX34ZEydOxBlnnBHUZRNFi+uvvx5r165FSUkJbr75Zlx11VU4efIk1qxZIwTRW7VqhRMnTuChhx7CmDFj8Je//AW1tbX47rvv0LVrVwDAypUrMWTIEADAu+++q+zjiMi7tLQ0XHHFFfjss890Wx7Y7XbExcWhW7du+Ne//oWCggIhmCdfyLJjxw5hvvz8fBw/fly40IUo2jT19sXviUQUSiFPg+eNN95444033njzdpNLY+qRy9hqy0wbldMcMWKEJEmSNGLECAmA1KxZM+n06dPSRx99JEzXpk0bqaKiQnr77bdD/vx5462xblbZthYvXixJkiTdcsstwrRXX321JEmSNH78eOF+uQT1HXfcISyjtLQ05K8pb7w15i0zM1P66quvlO20qqpK+uabb6Tp06dLycnJynSBlnDPysqSMjMzpaKiIundd981tTzeeIv2m3q/mJiYKBUWFkrLly8XprHZbNLWrVuljRs3Gi7HbrdLMTEx0s8//yw999xzwmOSJEn//Oc/Q/5ceeMtXG633367JEmSdPXVV+s+XlFRoexL9+zZI3Xv3l14/LXXXpMqKip0592zZ4+0evXqkD9H3ngL1a0pty9+T+SNN95CeWMGOhEREYWNG2+8EQMHDhRuemVs/TF06FAkJSXhzTffFO7//fff8fnnn+OPf/xjg5ZPFA6ssm3973//E37/85//jKKiIqxYsULIjt+2bRvy8vJw3nnnNWiMROHm5MmTOPfcczFw4EBMnz4dy5cvR9euXfHUU09hx44dyMrKCso6nn76aVxxxRUYPHhwEEZNFD2GDRuGrKwsLFmyRNhv2e12rF69GoMGDUJSUhIAZ/WXGTNm4KeffkJVVRXq6upQU1ODrl274swzzwzxMyEKbxMnTkRhYSHef/993ceHDRuGIUOG4Prrr0dpaSm++OIL9OjRQ5hG8tIuwdtjRJEuFNsXvycSUSgEt94bERERUSPavXs3Nm/eHNRlysGGvLw8j8eOHj2KUaNGBXV9RFZkhW3r9OnTKC0tFe5r0aIFMjIyUFNTo7sOdX87omiyefNmZZuNiYnB008/jWnTpuHBBx/E9OnTUVtba9iKQS77brRdAcCLL76IqVOn4h//+AdPQBL5oUWLFgA8T/SrZWZmory8HM8//zz+8pe/4Omnn8aXX36JoqIi1NfX41//+hcSExObashEEadXr14YNGgQXnzxRVRXV+tOs3XrVgDAd999hw8//BC//PILnnzySaX38okTJ5CYmIjExERUVFQI82ZmZgb9uJkoXIRi++L3RCIKFQbQiYiIKCJVVlYCAOLj44X7tV+kTpw4AQBo2bKlxzJatWqFwsLCRhohUXhqrG1LL9OgsLAQhYWFGDNmjO5YtCdSiKJRbW0tZs+ejWnTpik9IwsKCtC6dWvd6eX7CwoKDJdZWVmJxx57DAsXLsSFF14Y/EETRSh53zZ16lRs3LhRdxp527vhhhvw1ltvYdasWcLj2dnZKC4ubtRxEkWyiRMnAgD+9a9/mZq+rKwMe/bsQdeuXZX75N7MvXr1wvfff6/c36JFCzRv3hw7d+4M4oiJwkcoti9+TySiUGEJdyIiIopIBw4cAAD07t1buH/cuHHC7xs2bEB5eTluuOEG4f7WrVtj5MiR+Oyzzxp1nEThpim3rY8++gjZ2dlwOBxKxq36tnfv3oY9GaIwk5ubq3u/XO756NGjAIBPP/0UPXv21C0DfdVVV6G0tBTfffed13W98cYb2LVrF5566inY7Tx1QGTG+vXrUVRUhB49eujutzZv3qxky0mShKqqKmH+Cy+8EG3atAnF0IkiQlxcHG644QZ89913+Omnn0zNk5WVhV69euGXX35R7lu9ejUqKipwyy23CNPecsstqK+vxwcffBDEUROFByttX/yeSERNgRnoREREFJF++OEH7NmzB88++yxiYmJQVFSE8ePHY/jw4cJ0p06dwpw5czB37lwsWbIE//73v5GVlYVHH30UlZWVmD17doieAZE1NeW29Z///AfXX389Pv74Y8ybNw/ff/89ampq0KZNG5x//vlYvnw5T2BSVFmzZg1+//13rFixAnv27IHdbkffvn1x//33o7S0FPPmzQMAzJs3DzfddBPWrVuHJ598Ejt27EBGRgauvvpqXHnllbjvvvtQVlbmdV319fWYOXOmso39+OOPjf30iMLe6dOncdddd2HJkiXIzMzE//3f/+HYsWNo3rw5+vTpg+bNm+POO+8E4Dz5f8stt2DPnj348ccfMWDAAPz1r3/F4cOHQ/wsiMLXpZdeiqysLDz00EMej6WlpeGTTz7BO++8g3379qGiogJdu3bFPffcg/j4eOHYtKioCI8//jjmzJmDkydPYu3atRg0aBAee+wx/Otf/8Lu3bub8mkRWYKVti9+TySipiLxxhtvvPHGG2+8Wfl28803S5IkSQMGDDCcZv/+/dLixYuF+7p06SKtXr1aKi4ulgoKCqR58+ZJY8eOlSRJkkaMGCFMe+utt0rbtm2TKisrpaKiIun999+XzjzzzJA/d954a8ybVbatxYsXS6Wlpbrrdzgc0rRp06StW7dK5eXlUklJibRr1y7plVdekTp37mxqGbzxFim3K6+8Unr77beln3/+WSopKZGqqqqkAwcOSEuWLJG6d+8uTJuTkyMtWLBAOnDggFRdXS2dOnVK+uqrr6TLL7/cY7mPPvqoJEmSlJWV5fHYN998I0mSJK1YsSLkz5833qx4k/el7du3V+4755xzpBUrVkiFhYVSVVWVdPjwYWnFihXC9tesWTNp4cKFUn5+vlRWViZ99dVX0h/+8Afpiy++kL744gthHZIkSf/85z9D/lx5483qtzVr1kilpaVSSkqKx2NxcXHS66+/Lv30009SSUmJVF1dLR06dEh66623DL/33XXXXdKePXukyspK6cCBA9Kjjz4qxcTEhPx58sZbKG6h2L74PZE33ngL5c3m+oGIiIiIiIiIiIiIiIiIiCiqsZEZERERERERERERERERERERGEAnIiIiIiIiIiIiIiIiIiICwAA6ERERERERERERERERERERAAbQiYiIiIiIiIiIiIiIiIiIADCATkREREREREREREREREREBIABdCIiIiIiIiIiIiIiIiIiIgBATKgHQESNr1+/figoKAj1MIiaTIsWLbB169ZQD8NDq1atUFpaGuphEDWZ1NRUHD16NNTD8MBtkaKNFbdFbocUjbgtEoWeFbdDgNsiRR9ui0TWYNVtkcgKGEAninD9+vXDli1bQj0MoibXv39/SwXRW7VqhSNHjoR6GERNrnXr1pb6MsZtkaKVlbZFbocUzbgtEoWelbZDgNsiRS9ui0TWYLVtkcgqGEAninBy5nn//v2ZhU5RoUWLFtiyZYvl3u/yFcytW7fm1cwUFVJTU3HkyBHLvd+5LVK0seK2yO2QohG3RaLQs+J2CHBbpOjDbZHIGqy6LRJZBQPoRFGioKCAV5IRWUBpaSkPTIksgNsiUehxOySyBm6LRNbAbZHIGrgtEhERANhDPQAiIiIiIiIiIiIiIiIiIiIrYACdiIiIiIiIiIiIiIiIiIgIDKATEREREREREREREREREREBYACdiIiIiIiIiIiIiIiIiIgIAAPoREREREREREREREREREREABhAJyIiIiIiIiIiIiIiIiIiAsAAOhEREREREREREREREREREQAG0ImIiIiIiIiIiIiIiIiIiAAwgE5ERERERERERERERERERASAAXQiIiIiIiIiIiIiIiIiIiIADKATEREREREREREREREREREBYACdiIiIiIiIiIiIiIiIiIgIAAPoREREREREREREREREREREABhAJyKiKPfQQw/h+++/R0lJCQoKCvD++++ja9euwjSPPvoodu/ejbKyMpw8eRKffPIJBg8e7HW5MTExePjhh/HLL7+goqIC69ev151uypQp+O2331BRUYFNmzZh+PDhQXtuREREREREFLhzzjkHH374IY4cOQJJknDJJZd4TPPoo4/iyJEjKC8vxxdffIEePXqEYKREREREFEwMoBMRUVQbMWIEFixYgCFD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jPBXCxIIBELjgnpHAoFQNxhECvSaoCwCXaggbTQV+ne+8x288MILaG9vR1tbGx544AFMmTJFWuYHP/gB3njjDWzZsgUbN27EH/7wBxx66KGJ2/6P//gPvPnmm+js7OQqo2ZlAmqHHXbAwoULsX79emzduhUvv/wyDjrooModYC/QUpxM69iwCX/8r7txy5nnY1t7ex+3ikCoDZj5LSnQCYT6QCzBrJDYjMh1VStyKQNdt+zWJ7VNBLqagW5RoMfZxRsUvLr9tKLeNmagl6NAd7RiSL09MnkPKAp0Q06laV+q7b6XyeC0730bX7/zFhx66udw0OdOMBY+2I7FzXgayW7el2skx8XjLmTe2xXy6uRqYR8uXtywFuOHDMPhYydh+9aR2HPEaOw3anssffdtAMDQoUNxww034PDDD8eOO+6Io446Cr/73e+wfv16/O53v+PbvOGGG3DppZfitNNOw6677oprrrkGe+65J+666y6pHcceeyx22WUX7X0AWLx4MVasWIGFCxfigAMOwLHHHosbb7wRCxYsKIkYTws1P5VBtkKVv6NaFt7aYhCY7arJppVd10NbW63bFa3/vWxGItDjJs7V7HGbhTsrTvFzOUk5qIJny3b3GEn81BbuwnLNQwoRakYFutDebAm//2xq9YGAev5d1x/AuoOCDbXZ8aKiO0INyWtp/8rLWinQDRbucl9Zk2YQqoAoAz3kFu5uJhpbFohkuShFJMgD3+frsddiFAx3hCjeL+w5n+vp5kp1L5vh7/tqBrpQmM76m+HbjSksG6NAjyugYWOPwM9LbZDOhx9E/ar0O0GJhdLGBhm+TK67qLBvZvb0+eL/pEAnEAYKiEAnEAh1A6ZA797amXodItBLR3kW7oICPdtYCvSjjjoKt99+Ow4//HDMmDEDmUwGixcvxpDihBIArFy5Epdccgn23XdfHHnkkXjvvfewePFijBkzxrrds846Cz/5yU9w9dVXY6+99sIll1wCAFLuZGtrK5YsWYJcLocTTzwRU6dOxRVXXIHNmzdX63BLArNwT6u0IRAaCaFiY0cgEPoWeo6vTGLLdtzMcltUmSnEsCFvWyMGbRbuSjskEphloMco0DXy3tOJPTeGpI/eZgrbXlq4xyjH4yzcmxQFukoYqkr9xU507gABAABJREFU/WYcgyP+9VT+XqYpq6jf4jPQvUzG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HlMd6OnUQE860I1nYOb1SY1nj6k10H3aJYgGcqAn/issKUZR0lUuiBQXJWaRwnfiWBSVlsoI9wZjMIFJSecyKcQ50even5nXejxroBvtUWs0Ow70thHQ26IvRmvrECkqSgxMiSdLIVjiXpc81qki3JPzNno50IWAXqs70AWsga5jdYDBeeZn3JChA71bRW8AwKZVazrEwAJC2hIK6IQ0QSZfwA455BBMmTJFe+3dd9/FpZdeikgkglhy9GHnzp2xbNkyFBQUYO7cubj11lsxd+5cucxJJ52Ed999Fy+++CKOPPJIrF69Go8++ij+/nf/KNCioiItBr5z586B9tOJcM9vAb0fgNOV3wsAPJac2pIwgCcAvAuA1WuC0blzZ5SXl8vfo9EoGgKM0B07diw++eQTfPfdd9rrJ5xwAv7zn/+grKwMlZWVGDlyJDZu3Oi7nhdeeAHbb789Pv30U4RCIRQmR6KPGzcOANCzZ09EIhHXIJp62CiLFDLCnRBCcpxB++yFQfvsBQB4e/zjmPrUcwCAjStXtWWzCMkJbMPp1pGJNTRi9YJFiEXpBGuKOGugkw6I399169atQ0VFhecy5nMb9e/WdBB9So3aFthaDXTHxajWQN/7p0dh758e5byXqgY6Mneg6xHu9fI19XVP17JxC2morXOJxkLYjcfibgd6BhHu5sABVUC3jBro+jz1xmsBHOjJHTzrz3/AWX/+Q8p2iv+vfPYJZxu1qR3of/zwbWU9+voCOdCVYyMHQBntUZeJ1rWtA70t+mJDXR06deua6Dui3rysha679r2wUznQjQh3lwO9DaPyc5GOMDjPdR1J04Eej+nnxMbVjG8npLkwwp2QJsjkC1hFRYXn/IWFhbJm8oIFC3DRRRfhpJNOwtlnn436+npMmzYNO++8s1xmxx13xOjRo7F48WIce+yxePzxxzF+/Hicf/75vu0dM2YMqqqq5LRw4cJA+yki3PPdgb4LcvfCGAGwc5NzEcHChQu1vjBmzJgml3n44Yex99574+yzz3a9N3XqVOy777449NBD8c477+DFF1/E9ttv77GWBEceeSRuueUW/PrXv8b++++Pc845BwBwww03aPOZD5aLShN/vFVv3NxkewkhhLQ+q+YvxLbNWwAkYvLeHv843p/wdNs2ipAcY+G0majasBFrFi1u66a0ONUbNmLs6Rdi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beLEqaWnXDzvc7FNnDi15NSS53xz/q5jX+SUbxP7IidOuTHxnOfEqcmpzRvAiVO7mFJ9AXvqqafsqVOnavMfccQR9pw5c+z6+np7yZIl9q9+9Svt/bFjx9rLli2z6+vr7XXr1tnvvPOOq846APuEE06wv/76a7uurs6eN2+efdlll6XVbgronPJtytVznl9KOeXblKvnfK62ixOnlppy8ZzPxTZx4tTSUy6e97nYJk6cWnLK1XM+V9vFiVNLTbl6zudquzhxaqmJ5zwnTk1Obd4ATpw4teCUq2IiJ04tNZnn/O233+5yl1dWVmrLDB061H799dftLVu22FVVVfb06dPtAQMGyPeLiors8ePH2+vXr7dramrs119/3e7Xr19a7eKXUk75NuXqOZ+r7eLEqaWmXDznc7FNnDi19JSL530utokTp5accvWcz9V2ceLUUlOunvO52i5OnFpq4jnPiVPqKQxCCCGkg/Ptt9+ioqJCTnvttZd8b8cdd8Snn36KBQsW4KijjsI+++yDP//5z6ivr5fzPPjggzjllFNw1llnYfjw4ejcuTPefPNNhMO8jRJCCCGEEEIIIYQQQgghHYlIWzeAEEIIaWlisRjWrVvn+d5dd92Ft99+GzfeeKN8benSpfLnLl264NJLL8X555+P999/HwBw3nnnYeXKlfjpT3+KKVOmtGzjCSGEEEIIIYQQQgghhBDSatA6RwghpMOzyy67YPXq1ViyZAmef/55DB48GAAQCoVwwgknYNGiRXjnnXewbt06zJgxAyeffLJc9oADDkBRUZEmlFdWVuLbb7/FoYce6rvNoqIilJeXaxMhhBBCCCGEEEIIIYQQQnIbCuiEEEI6NDNnzsQFF1yAY489FpdffjkqKirw2WefoXv37ujVqxfKy8tx00034Z133sGoUaPw6quv4pVXXsERRxwBAKioqEA0GsWWLVu09a5btw4VFRW+2x0zZgyqqqrktHr16pbcTUIIIYQQQgghhBBCCCGEZAFGuBNCCOnQvPPOO/Lnb7/9FtOnT8cPP/yACy+8EP/5z38AAK+//joefPBBAMBXX32FQw89FFdccQU+/vhj3/WGQiHYtu37/j333IOxY8fK38vLyymiE0IIIYQQQgghhBBCCCE5Dh3ohBBC8ora2lp888032GWXXbBhwwY0NjZi3rx52jzz58/HwIED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A50QQgghhBBCCCGEEEIIIYQQQggBBXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAAV0QgghhBBCCCGEEEIIIYQQQgghBAAFdEIIIYQQQgghhBBCCCGEEEIIIQQABXRCCCGEEEIIIYQQQgghhBBCCCEEAPD/MG7+sYbifRsAAAAASUVORK5CYII=", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "IR1 = ambit_run(False)\n", - "IR1.add(mode = 1, ir = 0, num = 20, freq = 2, actinic = 0, sub = 1)\n", - "IR1.add(mode = 1, ir = 0, num = 20, freq = 2, actinic = 5, sub = 1)\n", - "IR1.add(mode = 1, ir = 0, num = 20, freq = 2, actinic = 5, sub = 1)\n", - "IR1.add(mode = 1, ir = 0, num = 20, freq = 2, actinic = 5, sub = 1)\n", - "IR1.add(mode = 1, ir = 0, num = 20, freq = 2, actinic = 0, sub = 1)\n", - "IR1.run()\n", - "IR1.plot()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "MPF1 = ambit_run(True)\n", - "MPF1.add(num = 50, freq = 50, actinic=0)\n", - "MPF1.add(num = 50, freq = 50, actinic=10)\n", - "MPF1.add(num = 50, freq = 50, actinic=0)\n", - "\n", - "MPF1.add(num = 50, freq = 10, actinic=0)\n", - "\n", - "\n", - "MPF1.add(num = 50, freq = 50, actinic=0)\n", - "MPF1.add(num = 50, freq = 50, actinic=10)\n", - "MPF1.add(num = 50, freq = 50, actinic=0)\n", - "\n", - "MPF1.run()\n", - "MPF1.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure()\n", - "plt.plot(MPF1.result['Fluo']/MPF1.result['Fluoref'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "MPF1 = ambit_run(True)\n", - "MPF1.add(num = 10, freq = 5, actinic=0)\n", - "MPF1.add(num = 40, freq = 100, actinic=250) # 0.4s max\n", - "\n", - "MPF1.add(num = 10, freq = 100, actinic=200) # 0.1s \n", - "MPF1.add(num = 10, freq = 100, actinic=120) # 0.1s \n", - "MPF1.add(num = 10, freq = 100, actinic=100) # 0.1s \n", - "MPF1.add(num = 10, freq = 100, actinic=80) # 0.1s \n", - "MPF1.add(num = 10, freq = 100, actinic=60) # 0.1s \n", - "\n", - "MPF1.add(num = 40, freq = 100, actinic=240)\n", - "MPF1.add(num = 25, freq = 100, actinic=0)\n", - "MPF1.add(num = 50, freq = 10, actinic=0)\n", - "\n", - "MPF1.run()\n", - "MPF1.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "actinic_setting = 10" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "MPF2 = ambit_run(True)\n", - "MPF2.add(num = 25, freq = 100, actinic=250)\n", - "MPF2.add(num = 25, freq = 100, actinic=200)\n", - "MPF2.add(num = 25, freq = 100, actinic=150)\n", - "MPF2.add(num = 25, freq = 100, actinic=100)\n", - "MPF2.add(num = 25, freq = 100, actinic=250)\n", - "MPF2.add(num = 25, freq = 100, actinic=actinic_setting)\n", - "# ret = MPF2.run()\n", - "# MPF2.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "MPF3 = ambit_run(True)\n", - "MPF3.add(num = 25, freq = 100, actinic=250)\n", - "MPF3.add(num = 25, freq = 100, actinic=200)\n", - "MPF3.add(num = 25, freq = 100, actinic=150)\n", - "MPF3.add(num = 25, freq = 100, actinic=100)\n", - "MPF3.add(num = 25, freq = 100, actinic=250)\n", - "MPF3.add(num = 25, freq = 100, actinic=0)\n", - "# ret = MPF3.run()\n", - "# MPF3.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "RGF1 = ambit_run(True)\n", - "RGF1.add(num = 50, freq = 100, actinic=250)\n", - "RGF1.add(num = 50, freq = 100, actinic=0)\n", - "\n", - "RGF2 = ambit_run(True)\n", - "RGF2.add(num = 50, freq = 100, actinic=250)\n", - "RGF2.add(num = 50, freq = 100, actinic=actinic_setting)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ACT1 = ambit_run(True)\n", - "ACT1.add(num=100, freq = 20, actinic=actinic_setting)\n", - "\n", - "AMB1 = ambit_run()\n", - "AMB1.add(num = 100, freq = 20, actinic=0)\n", - "AMB1.add(num = 50, freq = 100, actinic=250)\n", - "AMB1.add(num = 50, freq = 100, actinic=0)\n", - "AMB1.add(num = 100, freq = 20, actinic=0)\n", - "\n", - "AMB2 = ambit_run()\n", - "AMB2.add(num=100, freq = 10, actinic=0)\n", - "AMB2.add(num = 50, freq = 100, actinic=250)\n", - "AMB2.add(num = 50, freq = 100, actinic=0)\n", - "AMB2.add(num=100, freq = 10, actinic=0)\n", - "\n", - "AMB3 = ambit_run()\n", - "AMB3.add(num=100, freq = 5, actinic=0)\n", - "AMB3.add(num = 50, freq = 100, actinic=250)\n", - "AMB3.add(num = 50, freq = 100, actinic=0)\n", - "AMB3.add(num=100, freq = 5, actinic=0)\n", - "\n", - "AMB4 = ambit_run()\n", - "AMB4.add(num=60, freq = 1, actinic=0)\n", - "AMB4.add(num = 50, freq = 100, actinic=250)\n", - "AMB4.add(num = 50, freq = 100, actinic=0)\n", - "AMB4.add(num=60, freq = 1, actinic=0)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "runlist = [MPF1] + [ACT1, RGF2] * 4 + [AMB1] * 2 + [AMB2] * 2 + [AMB3] * 2 + [AMB4] * 2\n", - "fig, axes = plt.subplots(1, 5, sharex = True, figsize = (20, 6))\n", - "ax = axes[0].twinx()\n", - "results = []\n", - "for n in runlist:\n", - " ret = n.run()\n", - " results.append(ret)\n", - " _tl = ret['env_tl']\n", - " _TL = _tl + ret['env_t0']\n", - " plot_list = [_k for _k in ret.keys() if _k[:4] != \"env_\"]\n", - " for n, _p in enumerate(plot_list):\n", - " axes[n + 1].plot(_TL[1:], ret[_p][1:])\n", - " axes[n + 1].set_title(_p)\n", - " axes[0].plot(_TL[1:], (ret[\"Fluo\"]/ ret[\"Fluoref\"])[1:])\n", - " # axes[0].plot(result['env_stamp_t'][:-1]/854, result['env_stamp'][:-1]* np.mean((result[\"Fluo\"]/ result[\"Fluoref\"])[1:]) + .1)\n", - " ax.plot(ret['env_leaf_t']/1000 + ret['env_t0'], ret['env_leaf'], 'r.')\n", - " display.display(fig)\n", - " display.clear_output(True)\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig, axes = plt.subplots(1, 1, sharex = True, figsize = (16, 6), facecolor = [0, 0, 0, 0])\n", - "ax = axes.twinx()\n", - "for ret in results:\n", - " _tl = ret['env_tl']\n", - " _TL = _tl + ret['env_t0']\n", - " axes.plot(_TL[1:], (ret[\"Fluo\"]/ ret[\"Fluoref\"])[1:])\n", - " \n", - " ax.plot(ret['env_leaf_t']/1000 + ret['env_t0'], ret['env_leaf'], 'r.')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/.vscode/examples/uploader_v2.ipynb b/.vscode/examples/uploader_v2.ipynb deleted file mode 100644 index 88f7211..0000000 --- a/.vscode/examples/uploader_v2.ipynb +++ /dev/null @@ -1,278 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "0cf1e568", - "metadata": {}, - "outputs": [], - "source": [ - "import serial\n", - "import serial.tools.list_ports\n", - "import subprocess, os\n", - "import sys\n", - "import time\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "5033d621", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['COM34']" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "def serial_ports():\n", - " ports = serial.tools.list_ports.comports()\n", - " Flasherport = []\n", - " for port, desc, hwid in sorted(ports):\n", - " if hwid[:4] == \"USB \":\n", - " devices = hwid[4:].split(\" \")\n", - " if len(devices) > 1 and len(devices[0]) > 9:\n", - " if devices[0][-9:] == \"1A86:55D4\":\n", - " Flasherport.append(port)\n", - "\n", - " return Flasherport\n", - "\n", - "serial_ports()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "24669334", - "metadata": {}, - "outputs": [], - "source": [ - "while True:\n", - " flash_new_ambit = False\n", - "\n", - " with serial.Serial('COM34', 115200) as ser:\n", - " t0 = time.time()\n", - " s = \"\"\n", - " counter = 0\n", - " while time.time() - t0 < 5:\n", - " while (ser.in_waiting > 0) and (counter < 300):\n", - " c = ser.read()\n", - " if c > bytes([9]) and c < bytes([127]):\n", - " counter += 1\n", - " s += c.decode()\n", - "\n", - " if (counter > 20):\n", - " if \"invalid header\" in s:\n", - " flash_new_ambit = True\n", - " break\n", - " else:\n", - " if c.isascii():\n", - " print(c.decode(), end=\"\")\n", - " if (counter >= 49): break\n", - " if flash_new_ambit: break\n", - " time.sleep(.25)\n", - " print(\".\", end=\"\")" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "id": "31f542eb", - "metadata": {}, - "outputs": [], - "source": [ - "def ser_get_lines(ser: serial.Serial, n:int = -1, no_resp_timeout:float = 0.5, max_timeout:float = 50, display:bool = False, ter:str = \"\") -> list[str]:\n", - " start_t0:float = time.perf_counter()\n", - " last_char_t:float = time.perf_counter()\n", - " a_line:str = \"\"\n", - " ret_list:list[str] = []\n", - " counter:int = 0\n", - " if n == -1: n = 10000\n", - "\n", - " while True:\n", - " if time.perf_counter() - start_t0 > max_timeout: break\n", - " if time.perf_counter() - last_char_t > no_resp_timeout: break\n", - " if len(ret_list) >= n: break\n", - "\n", - " while (ser.in_waiting > 0):\n", - " c:bytes = ser.read()\n", - "\n", - " if c.isascii():\n", - " _char:str = c.decode()\n", - " if _char == '\\n' or _char == '\\r':\n", - " if counter > 0:\n", - " ret_list += [a_line]\n", - " if (display): print(a_line)\n", - " if len(ter) > 0 and (a_line == ter): return ret_list\n", - " a_line = \"\"\n", - " counter = 0\n", - "\n", - " else:\n", - " a_line += _char\n", - " counter += 1\n", - " last_char_t = time.perf_counter()\n", - " if time.perf_counter() - start_t0 > max_timeout: break\n", - " if counter > 100000: break\n", - " time.sleep(.05)\n", - "\n", - "\n", - " if len(a_line)>0:\n", - " ret_list += [a_line]\n", - "\n", - " return ret_list" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "id": "cf4a0273", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ESP-ROM:esp32c3-api1-20210207\n", - "Build:Feb 7 2021\n", - "rst:0xc (RTC_SW_CPU_RST),boot:0xd (SPI_FAST_FLASH_BOOT)\n", - "Saved PC:0x403819c4\n", - "SPIWP:0xee\n", - "mode:DIO, clock div:1\n", - "load:0x3fcd5820,len:0x1144\n", - "load:0x403cc710,len:0xad8\n", - "load:0x403ce710,len:0x2f80\n", - "entry 0x403cc710\n", - "BOOT\n", - "256223\n", - "ADPD Found, chip version: 2\n", - "Metadata: lon:1.000000\tlat:1.000000\talt:1.000000\ttime:0\tacc:1.000000\tvacc:1.000000\tinfo1:New_Ambit\tx:0.000000\ty:0.000000\tz:0.000000\n", - "Calibration: Name:AmbitV002\tActinic:0.100000\tSpec:1.000000\tEmit:1.000000\tSun:1.000000\tTemp_offset:0.000000\tTemp_slope:1.000000\n", - "Calibration: Act_50:5\tAct_100:4\tAct_150:3\tAct_200:2\tAct_250:1\n", - "Calibration: ADPD: 0\t0\t0\t0\t0\t0\n", - "MLX: 5832480\t78523296\t0\t6400\t4907706\t5832480\t64774732\t-51539608\t-35734128\t16384\t0\t10752\t10752\t72641246\t\n", - "FW: MAC:f80b44a89110\tSize:539216\tDate:Mar 26 2025\n", - "FW: 0.0.3\n" - ] - } - ], - "source": [ - "with serial.Serial('COM34', 115200) as ser:\n", - " ser.write(b\"reboot\\r\\n\")\n", - " ser_get_lines(ser, display=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "459d9b0c", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ESP-ROM:esp32c3-api1-20210207\n", - "Build:Feb 7 2021\n", - "rst:0x1 (POWERON),boot:0xd (SPI_FAST_FLASH_BOOT)\n", - "SPIWP:0xee\n", - "mode:DIO, clock div:1\n", - "load:0x3fcd5820,len:0x1144\n", - "load:0x403cc710,len:0xad8\n", - "load:0x403ce710,len:0x2f80\n", - "entry 0x403cc710\n", - "ESP-ROM:esp32c3-api1-20210207\n", - "Build:Feb 7 2021\n", - "rst:0x1 (POWERON),boot:0xd (SPI_FAST_FLASH_BOOT)\n", - "SPIWP:0xee\n", - "mode:DIO, clock div:1\n", - "load:0x3fcd5820,len:0x1144\n", - "load:0x403cc710,len:0xad8\n", - "load:0x403ce710,len:0x2f80\n", - "entry 0x403cc710\n", - "ESP-ROM:esp32c3-api1-20210207\n", - "Build:Feb 7 2021\n", - "rst:0x1 (POWERON),boot:0xd (SPI_FAST_FLASH_BOOT)\n", - "SPIWP:0xee\n", - "mode:DIO, clock div:1\n", - "load:0x3fcd5820,len:0x1144\n", - "load:0x403cc710,len:0xad8\n", - "load:0x403ce710,len:0x2f80\n", - "entry 0x403cc710\n", - "BOOT\n", - "256218\n", - "ADPD Found, chip version: 2\n", - "Metadata: lon:1.000000\tlat:1.000000\talt:1.000000\ttime:0\tacc:1.000000\tvacc:1.000000\tinfo1:New_Ambit\tx:0.000000\ty:0.000000\tz:0.000000\n", - "Calibration: Name:AmbitV002\tActinic:0.100000\tSpec:1.000000\tEmit:1.000000\tSun:1.000000\tTemp_offset:0.000000\tTemp_slope:1.000000\n", - "Calibration: Act_50:5\tAct_100:4\tAct_150:3\tAct_200:2\tAct_250:1\n", - "Calibration: ADPD: 0\t0\t0\t0\t0\t0\n", - "MLX: 5827307\t78665872\t0\t6400\t4916617\t5827307\t64589252\t-51539608\t-35734128\t16384\t0\t10752\t10752\t72596907\t\n", - "FW: MAC:f84044a89110\tSize:539216\tDate:Mar 26 2025\n", - "FW: 0.0.3\n" - ] - } - ], - "source": [ - "# 0: Conn/NA 1: New NoFW 2: has FW 3: ADPD exist -2: No ADPD\n", - "Ambit_status:int = -1\n", - "with serial.Serial('COM34', 115200) as ser:\n", - " t0:int = time.perf_counter()\n", - " loop_contiune:bool = True\n", - " Ambit_status = -1\n", - " \n", - " while time.perf_counter() - t0 < 50:\n", - " if not loop_contiune: break\n", - " _lines:list[str] = ser_get_lines(ser, display=True, ter = \"invalid header: 0xffffffff\")\n", - " if len(_lines) == 0:\n", - " time.sleep(.05)\n", - " continue\n", - "\n", - " for _l in _lines:\n", - " if \"invalid header: 0xffffffff\" in _l:\n", - " print(\"NEW ambit Found\")\n", - " loop_contiune = False\n", - " Ambit_status = 1\n", - "\n", - " if (\"esp\" in _l) or (\"load\" in _l) or (\"BOOT\" in _l):\n", - " Ambit_status = 2\n", - "\n", - "\n", - " if (\"ADPD Found\" in _l):\n", - " Ambit_status = 3\n", - " else:\n", - " if (\"BOOT\" in _l) and (\"Metadata\" in _l):\n", - " print(\"ADPD Not Found!\")\n", - " Ambit_status = -2\n", - "\n", - " " - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index bebb363..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "files.associations": { - "bitset": "cpp", - "string_view": "cpp", - "*.tcc": "cpp", - "random": "cpp" - }, - "python-envs.defaultPackageManager": "ms-python.python:pip", - "python-envs.workspaceSearchPaths": [ - ".venv", - "*/.venv", - "D:\\Python_env\\conn_env\\.venv" - ] -} \ No newline at end of file diff --git a/260605_amBIT_DEMO.ipynb b/260605_amBIT_DEMO.ipynb new file mode 100644 index 0000000..8594eb6 --- /dev/null +++ b/260605_amBIT_DEMO.ipynb @@ -0,0 +1,508 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import serial\n", + "import time\n", + "import matplotlib.pyplot as plt\n", + "from IPython import display\n", + "from datetime import datetime\n", + "import os \n", + "import re\n", + "import serial.tools.list_ports\n", + "\n", + "\n", + "plt.style.use(\"dark_background\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Function to find the connected devices on available serial ports\n", + "def findDevice(question=\"hello\", answer=\"Ready\", flush=True, timeout=3):\n", + " \"\"\"\n", + " Find the amBIT on an available serial port by sending `question` and looking\n", + " for `answer` in the reply. Returns the port name (str), or None if not found.\n", + " \"\"\"\n", + " if not question.endswith(\"\\n\"):\n", + " question += \"\\n\" # firmware is line-based\n", + " for p in serial.tools.list_ports.comports(): # only real ports, not COM1..COM256\n", + " port = p.device\n", + " try:\n", + " with serial.Serial(port, baudrate=115200, timeout=timeout) as ser:\n", + " ser.setRTS(False) # keep the ESP out of bootloader mode\n", + " ser.flush()\n", + " time.sleep(0.7) # let the device finish (re)booting\n", + " if flush:\n", + " ser.reset_input_buffer() # drop the boot-log / stale bytes\n", + " ser.write(question.encode())\n", + " time.sleep(0.5)\n", + "\n", + " reply = \"\"\n", + " t0 = time.monotonic()\n", + " while time.monotonic() - t0 < timeout:\n", + " line = ser.readline().decode(\"utf-8\", errors=\"replace\").strip()\n", + " if not line:\n", + " break\n", + " reply += line + \"\\n\"\n", + " if answer and answer in line:\n", + " break\n", + "\n", + " print(f\"[{port}] {p.description}: {reply.strip()!r}\")\n", + " if answer and answer in reply:\n", + " print(f\"Found device at: {port}\")\n", + " return port\n", + " except (OSError, serial.SerialException):\n", + " pass\n", + " print(\"No matching device found.\")\n", + " return None\n", + "\n", + " \n", + "def ensure_path_exists(path: str) -> None:\n", + " \"\"\"\n", + " Ensures that the given directory path exists.\n", + " If it does not exist, this function will create it (including any intermediate directories).\n", + "\n", + " :param path: The directory path to check (and potentially create).\n", + " \"\"\"\n", + " if not os.path.exists(path):\n", + " print(f\"Creating path: {path}\")\n", + " os.makedirs(path)\n", + " else:\n", + " print(f\"{path} exist\")\n", + "\n", + "def gen_cmd_arr_line(num: int, freq: int, actinic: int) -> list:\n", + " \"\"\"\n", + " Generate a single command-array entry of 8 parameters.\n", + " \n", + " :param num: Total number of measurement points (1-2000).\n", + " :param freq: Frequency measurement points (1-200).\n", + " :param actinic: Actinic light setting (0-255).\n", + " :return: List of 8 integer values representing the command.\n", + " \"\"\"\n", + " return [\n", + " 2, # Fixed flag or identifier\n", + " 0, # Another fixed flag or placeholder\n", + " num // 256, # High byte of 'num'\n", + " num % 256, # Low byte of 'num'\n", + " freq // 256, # High byte of 'freq'\n", + " freq % 256, # Low byte of 'freq'\n", + " actinic, # Actinic light parameter\n", + " 1 # Fixed flag or terminator\n", + " ]\n", + "\n", + "def calc_arr_param(cmd: list, persist: bool = False):\n", + " \"\"\"\n", + " Reshape a flat command list into a 2D array, construct a command string,\n", + " and calculate measurement timelines and actinic-array values.\n", + "\n", + " :param cmd: A 1D list of length 8*N (multiple commands concatenated).\n", + " :param persist: A flag indicating whether the command persists after completion.\n", + " :return: Tuple (cmd_str, mea_tml, mea_act)\n", + " - cmd_str: Formatted command string (e.g., for sending over serial).\n", + " - mea_tml: NumPy array of timestamps (scaled by 0.854).\n", + " - mea_act: List of actinic values (one per timestamp).\n", + " \"\"\"\n", + " # Reshape 'cmd' into an N x 8 array\n", + " cmd_arr = np.reshape(cmd, (-1, 8))\n", + " arr_length = cmd_arr.shape[0]\n", + "\n", + " # Create a comma-separated command string:\n", + " # e.g., \"arrun1,N,persist,,\\n\"\n", + " cmd_str = f\"arrun1,{arr_length},{persist},{str(cmd)[1:-1]},\\n\"\n", + " cmd_str = cmd_str.replace(\" \", \"\") # Remove spaces for a cleaner command\n", + "\n", + " # Parse the reshaped array into usable parameters\n", + " num_pts = cmd_arr[:, 2] * 256 + cmd_arr[:, 3] # Number of points (high/low bytes)\n", + " act_arr = cmd_arr[:, 6] # Actinic setting\n", + " mea_feq = 1 / (cmd_arr[:, 4] * 256 + cmd_arr[:, 5]) # Measurement frequency (1 / freq)\n", + "\n", + " # Build up the measurement timeline and actinic values\n", + " _t = 0\n", + " mea_tml = []\n", + " mea_act = []\n", + "\n", + " # For each command segment, create a timeline and corresponding actinic values\n", + " for _n, _f, _a in zip(num_pts, mea_feq, act_arr):\n", + " segment_times = (np.arange(_n) * _f) + _t\n", + " mea_tml.extend(segment_times) # Accumulate times\n", + " mea_act.extend([_a] * _n) # Repeat actinic value _n times\n", + " _t = segment_times[-1] # Update the offset for the next segment\n", + "\n", + " # Scale the entire timeline by 0.854\n", + " mea_tml = np.array(mea_tml) * 0.854\n", + "\n", + " return cmd_str, mea_tml, mea_act\n", + "\n", + "def send_read_command(port, string, required_string=None, baudrate=115200, timeout=10):\n", + " \"\"\"\n", + " Opens the serial port, sends a “hello” and then your command, and collects replies.\n", + " Stops reading when either:\n", + " • no more data arrives (an empty readline, i.e. per-read timeout fired), or\n", + " • we see `required_string` in the response, if given, or\n", + " • the overall `timeout` has elapsed.\n", + " Returns the list of lines read so far.\n", + " \"\"\"\n", + " lines = []\n", + " start = time.monotonic()\n", + " with serial.Serial(port, baudrate=baudrate, timeout=timeout) as ser:\n", + " # hand-shake / mode set\n", + " ser.setRTS(False)\n", + " ser.flush()\n", + " time.sleep(0.7)\n", + " ser.write(b\"hello\\n\")\n", + " time.sleep(0.5)\n", + " ser.write(f\"{string}\\n\".encode())\n", + " last_recv = time.monotonic()\n", + "\n", + " while True:\n", + " # Check idle timeout\n", + " now = time.monotonic()\n", + " if now - last_recv >= timeout:\n", + " print(f\"No (additional) responses within user-set timeout: {timeout}s\")\n", + " return lines\n", + " \n", + " try:\n", + " line = ser.readline().decode('utf-8', errors='replace').strip()\n", + " # print(line)\n", + " \n", + " last_recv = time.monotonic() # Reset idle timer since we got data\n", + " except Exception as e:\n", + " print(f\"Error reading line: {e!s}\")\n", + " break\n", + "\n", + " # if nothing arrived within per-read timeout, readline() returns b'' → line == ''\n", + " if not line:\n", + " print(f\"No (additional) responses within user-set timeout: {timeout}s\")\n", + " return lines\n", + " \n", + " lines.append(line)\n", + "\n", + " # if we were waiting for a specific marker, stop when we see it\n", + " if required_string and required_string in line:\n", + " break\n", + " return lines\n", + "\n", + "def parse_command_blocks(lines, cmd_prefix=\"cmd:\"):\n", + " \"\"\"\n", + " Parse a list of lines and group them by commands that match `cmd_prefix`.\n", + "\n", + " The function identifies lines containing commands (e.g., 'cmd: something')\n", + " and collects all subsequent lines into that command's \"block\" until the\n", + " next command (or end of list).\n", + "\n", + " :param lines: List of strings to parse.\n", + " :param cmd_prefix: The prefix that indicates a new command. Defaults to 'cmd:'.\n", + " :return: A dictionary mapping { command_name: [list_of_lines_until_next_command] }\n", + " If the same command appears multiple times, each occurrence will\n", + " overwrite previous data unless modified to handle duplicates.\n", + " \"\"\"\n", + " command_dict = {}\n", + " current_cmd = None\n", + " current_data = []\n", + "\n", + " for line in lines:\n", + " # Try to find a line that includes 'cmd:' (possibly with extra characters).\n", + " # The regex captures whatever follows 'cmd:' up to the first whitespace \n", + " # or end of string. E.g., 'cmd: hello' => group(1) = 'hello'.\n", + " match = re.search(r'cmd:\\s*([^\\s]+)', line)\n", + "\n", + " if match:\n", + " # If we have a \"current_cmd\", store its accumulated data first\n", + " if current_cmd is not None:\n", + " command_dict[current_cmd] = current_data\n", + "\n", + " # Extract the new command from the current line\n", + " current_cmd = match.group(1)\n", + " current_data = [] # Start a fresh list for data following this command\n", + " else:\n", + " # If this line does not declare a new command, and we've already seen a command,\n", + " # then add the line to the current command's block\n", + " if current_cmd is not None:\n", + " current_data.append(line)\n", + "\n", + " # At the end, if there is a command in progress, save its data\n", + " if current_cmd is not None:\n", + " command_dict[current_cmd] = current_data\n", + "\n", + " return command_dict\n", + "\n", + "\n", + "def parse_serial(lines):\n", + " \"\"\"\n", + " Efficiently parses a list of strings and returns a dictionary of lists for T, F, S, R, Sun, L.\n", + " Skips lines that do not match the expected format.\n", + " \"\"\"\n", + " import re\n", + " pattern = re.compile(r\"T:([\\d\\.]+),F:([\\d\\.]+),S:(\\d+),R:(\\d+),Sun:(\\d+),L:(\\d+)\")\n", + " result = {'T': [], 'F': [], 'S': [], 'R': [], 'Sun': [], 'L': []}\n", + " for line in lines:\n", + " m = pattern.search(line)\n", + " if m:\n", + " result['T'].append(float(m.group(1)))\n", + " result['F'].append(float(m.group(2)))\n", + " result['S'].append(int(m.group(3)))\n", + " result['R'].append(int(m.group(4)))\n", + " result['Sun'].append(int(m.group(5)))\n", + " result['L'].append(int(m.group(6)))\n", + " return result\n", + "\n", + "\n", + "def append_timeseries(base_series, *additional_series, additionalnumber=1):\n", + " \"\"\"\n", + " Appends one or more additional time series to an existing base series,\n", + " inserting a fixed gap (additionalnumber) between each appended series.\n", + "\n", + " :param base_series: List of time values (the initial series).\n", + " :param additional_series: One or more lists of time values to append.\n", + " :param additionalnumber: Gap to insert between each series (default 0).\n", + " :return: A new merged list with all timeseries concatenated and offset, including gaps.\n", + " \"\"\"\n", + " merged = list(base_series)\n", + " offset = merged[-1] if merged else 0\n", + " for s in additional_series:\n", + " if len(s) == 0:\n", + " continue\n", + " # Always add the gap before appending a new series (except before the first)\n", + " offset += additionalnumber\n", + " adjusted = [t + offset for t in s]\n", + " merged.extend(adjusted)\n", + " offset = adjusted[-1]\n", + " return merged\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "PORT = findDevice(question=\"hello\", answer=\"Ready\") #<----automatic find ambit\n", + "# PORT = \"COM5\" #<--- manual port selection (overrides the auto-detect above)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "SAVE = False\n", + "mytime = datetime.now().strftime(\"%y%m%d_%H%M_%S\")\n", + "\n", + "cmdA = []\n", + "cmdA += gen_cmd_arr_line(50, 10, 0) # 50 points, 10Hz, no actinic light\n", + "cmdA += gen_cmd_arr_line(8, 10, 255) # 8 points, 10Hz, no actinic light\n", + "cmdA += gen_cmd_arr_line(50, 10, 0) # 50 points, 10Hz, no actinic light\n", + "\n", + "cmd_strA, timelineA, act_arrA = calc_arr_param(cmdA, 0)\n", + "act_arrA = calc_arr_param(cmdA, 0)\n", + "data_serial = send_read_command(PORT, cmd_strA, required_string=\"Done\")\n", + "parsed_serial = parse_serial(data_serial)\n", + "\n", + "df = pd.DataFrame(parsed_serial)\n", + "df.insert(0,\"time (sec)\",timelineA)\n", + "\n", + "\n", + "plt.plot(df[\"time (sec)\"],df[\"F\"])\n", + "plt.xlabel(\"Time (sec)\")\n", + "plt.ylabel(\"Fluo. rel. yield\")\n", + "plt.title(\"Fluorescence Intensity over pulses\")\n", + "\n", + "\n", + "if SAVE:\n", + " df.to_csv(f\"{mytime}_output.csv\",sep=\",\", index=False)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cmdA = []\n", + "cmdA += gen_cmd_arr_line(500, 500, 255) # 400 points, 200Hz, 255 actinic light (on scale of 0-255)\n", + "\n", + "cmd_strA, timelineA, act_arrA = calc_arr_param(cmdA, 0)\n", + "data_serial = send_read_command(PORT, cmd_strA, required_string=\"Done\")\n", + "parsed_serial = parse_serial(data_serial)\n", + "\n", + "df = pd.DataFrame(parsed_serial)\n", + "df.insert(0,\"time (sec)\",timelineA)\n", + "\n", + "\n", + "plt.plot(df[\"time (sec)\"],df[\"F\"])\n", + "plt.xscale(\"log\")\n", + "plt.xlabel(\"Time (sec)\")\n", + "plt.ylabel(\"Intensity\")\n", + "plt.title(\"Fluorescence Intensity over pulses\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def STING(PORT, POINT_LOW = 10, FREQ_LOW = 10, POINT_HIGH = 40):\n", + " cmdA = gen_cmd_arr_line(POINT_LOW, FREQ_LOW, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_HIGH, 1000, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_LOW, FREQ_LOW, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_HIGH, 1000, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_LOW, FREQ_LOW, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_HIGH, 1000, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_LOW, FREQ_LOW, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_HIGH, 1000, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_LOW, FREQ_LOW, 0)\n", + " cmd_strA, timelineA, _ = calc_arr_param(cmdA, 0)\n", + " data_serial = send_read_command(PORT, cmd_strA, required_string=\"Done\")\n", + " parsed_serial = parse_serial(data_serial)\n", + " fluo =np.array(parsed_serial[\"F\"])\n", + " sun = np.array(parsed_serial[\"S\"])\n", + " leaf = np.array(parsed_serial[\"L\"])\n", + " return fluo, sun, leaf\n", + "\n", + "\n", + "results = []\n", + "for freq_low in [10,20,40,80,160]:\n", + " # time.sleep(30)\n", + " fluo, sun, leaf = STING(PORT, FREQ_LOW=freq_low)\n", + " results.append((freq_low,fluo, sun, leaf))\n", + " plt.plot(fluo,label=f\"Frequency low: {freq_low} Hz\")\n", + " plt.legend()\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "results = []\n", + "for freq_low in [10,20,40,80,160]:\n", + " time.sleep(10)\n", + " fluo, sun, leaf, timeline = STING(PORT, FREQ_LOW=freq_low)\n", + " results.append((freq_low,fluo, sun, leaf, timeline))\n", + " plt.plot(fluo, label=f\"Frequency low: {freq_low} Hz\")\n", + " plt.legend()\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "results = []\n", + "for freq_low in [10,20,40,80,160]:\n", + " time.sleep(10)\n", + " fluo, sun, leaf, timeline = STING(PORT, FREQ_LOW=freq_low)\n", + " results.append((freq_low,fluo, sun, leaf, timeline))\n", + " plt.plot(timeline, fluo, label=f\"Frequency low: {freq_low} Hz\")\n", + " plt.legend()\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cmdA = gen_cmd_arr_line(1, 120, 0)\n", + "# cmdA += gen_cmd_arr_line(10, 100, 0)\n", + "cmd_strA, timelineA, _ = calc_arr_param(cmdA, 0)\n", + "data_serial = send_read_command(PORT, cmd_strA, required_string=\"Done\")\n", + "data_serial" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def STING(PORT, POINT_LOW = 10, FREQ_LOW = 10, POINT_HIGH = 40):\n", + " cmdA = gen_cmd_arr_line(POINT_LOW, FREQ_LOW, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_HIGH, 1000, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_LOW, FREQ_LOW, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_HIGH, 1000, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_LOW, FREQ_LOW, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_HIGH, 1000, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_LOW, FREQ_LOW, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_HIGH, 1000, 0)\n", + " cmdA += gen_cmd_arr_line(POINT_LOW, FREQ_LOW, 0)\n", + " cmd_strA, timelineA, _ = calc_arr_param(cmdA, 0)\n", + " data_serial = send_read_command(PORT, cmd_strA, required_string=\"Done\")\n", + " parsed_serial = parse_serial(data_serial)\n", + " fluo =np.array(parsed_serial[\"F\"])\n", + " sun = np.array(parsed_serial[\"S\"])\n", + " leaf = np.array(parsed_serial[\"L\"])\n", + " return fluo, sun, leaf\n", + "\n", + "results = []\n", + "for freq_low in [10,20,40,80,160]:\n", + " time.sleep(10)\n", + " fluo, sun, leaf, timeline = STING(PORT, FREQ_LOW=freq_low)\n", + " results.append((freq_low,fluo, sun, leaf, timeline))\n", + " plt.plot(timeline, fluo, label=f\"Frequency low: {freq_low} Hz\")\n", + " plt.legend()\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f7ef046 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,108 @@ +--- +title: "AGENTS.md — ambit-iot firmware" +date: 2026-08-04 +generated_by: skills-for-architects +--- + +# AGENTS.md — ambit-iot firmware + +ESP32-C3 (Arduino framework via PlatformIO) firmware for the AMBIT leaf-sensor board: +ADPD6100 (fluorescence), AS7341 (spectral/PAR), MLX90632 (leaf temperature). One image +serves every host at runtime over UART0: an Ambyte datalogger (binary FSM over the FFC), +the openJII app and the Calibratron (text console), and the openJII JSON envelope. + +## Commands + +- Build: `pio run` (single env `ambit`) +- Flash: add `-t upload`; serial monitor: 115200 8N1 +- Linux/macOS quirk: `platformio.ini` sets `build_dir = ${sysenv.LOCALAPPDATA}/pio_build/…` + (Windows OneDrive/AV workaround). Off Windows, export something sane first, e.g. + `LOCALAPPDATA=$HOME/.cache pio run`. +- Version is build-injected by `tools/version.py`: `$AMBIT_RELEASE_VERSION` (CI) → + `git describe` (dev) → `0.0.0-dev`. Never hand-edit a version literal; everything + (cmd 33/2, `hello`, JSON identity) reports the same injected value. +- Releases: semantic-release keyed on Conventional-Commit PR titles (squash-merge). + `pr.yml` validates the title, builds, and uploads the artifact; `release.yml` reuses + that artifact bit-identically and publishes the GitHub release (app image, + bootloader, partitions, boot_app0, `manifest.json` with offsets + sha256). The + Calibratron flashes devices from these release assets. +- No automated test suite. The regression gate for anything touching `run_esp.cpp`, + the `data_utils.cpp` FSM, or the `loop()` router is the hardware conformance + procedure in `plans/HW_CONFORMANCE.md` (byte-diff against a real Ambyte). + +## Architecture — one CORE + one CONFIG + thin transport adapters, routed at runtime + +`plans/MERGE_PLAN.md` is the historical design doc for the fork merge (its §10/§11 +build-variant split was superseded by the single image once every host landed on the +same UART0); read it before structural changes. The spine: + +``` +wire bytes → [adapter: decode + undo wire conventions] → normalized args → core op → [sink] +``` + +- **First-byte router** (`ambit-1.ino` `loop()`): >127 → binary FSM branch (every + frozen framing byte is >127); `{`/`[` → openJII JSON envelope (`ojii::poll`); other + printable → legacy text console (`do_command`). `ojii::poll()` must never see binary + traffic (it consumes bytes and drops >127 junk silently) — peek, then route. While + `ojii::busy()` the envelope owns the stream. +- **Sticky host latch** (`HOST_UNKNOWN/BINARY/TEXT`): BINARY/UNKNOWN idle in light + sleep (the Ambyte re-sends its wake until acked, so sleep-lost bytes are safe); + TEXT never sleeps (text/JSON hosts don't retry; UART sleep-wake eats in-flight + bytes) and releases after 2 min idle. The latch also gates the 0x85 idle heartbeat. +- `src/core.{h,cpp}` — transport-agnostic command core. Takes normalized typed args + (gains 0-indexed, real widths), does **no** wire I/O: no Serial/printf/JSON. +- `src/config.h` + PAM globals — single config (`adpd_*_config`, `adpd_mode`) with + "set, then apply on run" semantics. `CONNECTION_TYPE` (PLOTTING / AMBYTE / COMPUTER) + selects the measurement output sink; the JSON envelope path uses the `json_output` + flag through `run_arr_type1` instead. +- Adapters: + - `src/run_esp.cpp` — binary/Ambyte adapter. **Frozen wire contract** (below). + - `src/do_command.h` — text/console verbs (`hash()` dispatch switch). This is the + dialect the openJII platform's ambit driver and the Calibratron actually speak. + - `src/frontend_json.cpp` — openJII envelope handlers (snapshot tree-builders + + streamed `arrun`), registered from `setup()`. +- `lib/openjii_proto/` — device-agnostic openJII serial protocol module. Devices + register snapshot/stream handlers; `busy()`/`reset()` exist for router coexistence. + Bare-line LINE mode is intentionally unreachable in this firmware (no live consumer; + bare lines belong to the text console). +- `src/PAM.cpp` — measurement engine (array runs, MPF, triggers, env logging); + `src/data_utils.cpp` — FSM data framing + checksums; `src/nvs1.*` — calibration, + metadata, and fw-info structs persisted in NVS; `src/src/` — sensor drivers. + +## The frozen binary wire contract (do not break) + +The binary path is a wire contract with shipped Ambyte firmware. The source of truth is +the Ambyte repo (`ambyte-iot`): `components/uart_sensors/uart_sensors.c` and +`components/device_commands/include/ambit_protocol.h`. MERGE_PLAN §2 lists every frozen +element. Rules: + +- Refactor behind the wire freely; never change the bytes. Obscure-but-frozen encodings + stay: 1-indexed gains on the wire (stored −1), base-128 MPF length split + (`cmd[1]<<7 | cmd[2]`), byte-sum-mod-256 data checksum, struct sizes 140/48/248 with + `eof_mark==2025`. +- Wire conventions are decoded **in the adapter, never the core** — the core only ever + sees normalized values. +- The cmd 33/2 fw-info struct is 48 bytes with fixed offsets; `hw_rev` lives in the + first formerly-reserved byte (old readers never looked at it). Do not move fields. +- Any change to the binary path or the router re-runs `plans/HW_CONFORMANCE.md`. +- UART0 doubles as the log console; setup silences all ESP logging, and nothing may + `printf` outside explicit console replies — ASCII in front of a binary frame breaks + the Ambyte session. The boot banner + NVS dump predate the freeze and are tolerated; + do not add more. + +## Version reporting (single source) + +`tools/version.py` injects `AMBIT_FW_VERSION` (string) + `AMBIT_FW_MAJOR/MINOR/BATCH` +(uint8 fields of the cmd 33/2 struct). Reported by: binary cmd 33/2, text `hello` +(`NEW Ready FW:` — "NEW"/"Ready" are matched by the app driver and +Calibratron, the FW token is additive), and the JSON `hello`/envelope identity. + +## Conventions + +- Heavy rationale comments are the house style — a constraint's *why* lives at its + definition (wire freezes, sleep thresholds, build-dir workaround). Keep it that way. +- Conventional Commits for commits and PR titles: `type(scope): description`. + PR titles drive semantic-release (feat → minor, fix/perf → patch). +- License: CERN-OHL-S-2.0 (hardware-adjacent open license — keep headers/attribution). +- Vendored driver libs under `src/src/` (as7341, adpd, mlx90632) are third-party + imports; don't reformat or "clean up" wholesale. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..1fb4cfd --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,7 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +The canonical agent guidance lives in AGENTS.md (shared with Codex and other agents): + +@AGENTS.md diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..114486f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,289 @@ +CERN Open Hardware Licence Version 2 - Strongly Reciprocal + + +Preamble + +CERN has developed this licence to promote collaboration among +hardware designers and to provide a legal tool which supports the +freedom to use, study, modify, share and distribute hardware designs +and products based on those designs. Version 2 of the CERN Open +Hardware Licence comes in three variants: CERN-OHL-P (permissive); and +two reciprocal licences: CERN-OHL-W (weakly reciprocal) and this +licence, CERN-OHL-S (strongly reciprocal). + +The CERN-OHL-S is copyright CERN 2020. Anyone is welcome to use it, in +unmodified form only. + +Use of this Licence does not imply any endorsement by CERN of any +Licensor or their designs nor does it imply any involvement by CERN in +their development. + + +1 Definitions + + 1.1 'Licence' means this CERN-OHL-S. + + 1.2 'Compatible Licence' means + + a) any earlier version of the CERN Open Hardware licence, or + + b) any version of the CERN-OHL-S, or + + c) any licence which permits You to treat the Source to which + it applies as licensed under CERN-OHL-S provided that on + Conveyance of any such Source, or any associated Product You + treat the Source in question as being licensed under + CERN-OHL-S. + + 1.3 'Source' means information such as design materials or digital + code which can be applied to Make or test a Product or to + prepare a Product for use, Conveyance or sale, regardless of its + medium or how it is expressed. It may include Notices. + + 1.4 'Covered Source' means Source that is explicitly made available + under this Licence. + + 1.5 'Product' means any device, component, work or physical object, + whether in finished or intermediate form, arising from the use, + application or processing of Covered Source. + + 1.6 'Make' means to create or configure something, whether by + manufacture, assembly, compiling, loading or applying Covered + Source or another Product or otherwise. + + 1.7 'Available Component' means any part, sub-assembly, library or + code which: + + a) is licensed to You as Complete Source under a Compatible + Licence; or + + b) is available, at the time a Product or the Source containing + it is first Conveyed, to You and any other prospective + licensees + + i) as a physical part with sufficient rights and + information (including any configuration and + programming files and information about its + characteristics and interfaces) to enable it either to + be Made itself, or to be sourced and used to Make the + Product; or + ii) as part of the normal distribution of a tool used to + design or Make the Product. + + 1.8 'Complete Source' means the set of all Source necessary to Make + a Product, in the preferred form for making modifications, + including necessary installation and interfacing information + both for the Product, and for any included Available Components. + If the format is proprietary, it must also be made available in + a format (if the proprietary tool can create it) which is + viewable with a tool available to potential licensees and + licensed under a licence approved by the Free Software + Foundation or the Open Source Initiative. Complete Source need + not include the Source of any Available Component, provided that + You include in the Complete Source sufficient information to + enable a recipient to Make or source and use the Available + Component to Make the Product. + + 1.9 'Source Location' means a location where a Licensor has placed + Covered Source, and which that Licensor reasonably believes will + remain easily accessible for at least three years for anyone to + obtain a digital copy. + + 1.10 'Notice' means copyright, acknowledgement and trademark notices, + Source Location references, modification notices (subsection + 3.3(b)) and all notices that refer to this Licence and to the + disclaimer of warranties that are included in the Covered + Source. + + 1.11 'Licensee' or 'You' means any person exercising rights under + this Licence. + + 1.12 'Licensor' means a natural or legal person who creates or + modifies Covered Source. A person may be a Licensee and a + Licensor at the same time. + + 1.13 'Convey' means to communicate to the public or distribute. + + +2 Applicability + + 2.1 This Licence governs the use, copying, modification, Conveying + of Covered Source and Products, and the Making of Products. By + exercising any right granted under this Licence, You irrevocably + accept these terms and conditions. + + 2.2 This Licence is granted by the Licensor directly to You, and + shall apply worldwide and without limitation in time. + + 2.3 You shall not attempt to restrict by contract or otherwise the + rights granted under this Licence to other Licensees. + + 2.4 This Licence is not intended to restrict fair use, fair dealing, + or any other similar right. + + +3 Copying, modifying and Conveying Covered Source + + 3.1 You may copy and Convey verbatim copies of Covered Source, in + any medium, provided You retain all Notices. + + 3.2 You may modify Covered Source, other than Notices, provided that + You irrevocably undertake to make that modified Covered Source + available from a Source Location should You Convey a Product in + circumstances where the recipient does not otherwise receive a + copy of the modified Covered Source. In each case subsection 3.3 + shall apply. + + You may only delete Notices if they are no longer applicable to + the corresponding Covered Source as modified by You and You may + add additional Notices applicable to Your modifications. + Including Covered Source in a larger work is modifying the + Covered Source, and the larger work becomes modified Covered + Source. + + 3.3 You may Convey modified Covered Source (with the effect that You + shall also become a Licensor) provided that You: + + a) retain Notices as required in subsection 3.2; + + b) add a Notice to the modified Covered Source stating that You + have modified it, with the date and brief description of how + You have modified it; + + c) add a Source Location Notice for the modified Covered Source + if You Convey in circumstances where the recipient does not + otherwise receive a copy of the modified Covered Source; and + + d) license the modified Covered Source under the terms and + conditions of this Licence (or, as set out in subsection + 8.3, a later version, if permitted by the licence of the + original Covered Source). Such modified Covered Source must + be licensed as a whole, but excluding Available Components + contained in it, which remain licensed under their own + applicable licences. + + +4 Making and Conveying Products + +You may Make Products, and/or Convey them, provided that You either +provide each recipient with a copy of the Complete Source or ensure +that each recipient is notified of the Source Location of the Complete +Source. That Complete Source is Covered Source, and You must +accordingly satisfy Your obligations set out in subsection 3.3. If +specified in a Notice, the Product must visibly and securely display +the Source Location on it or its packaging or documentation in the +manner specified in that Notice. + + +5 Research and Development + +You may Convey Covered Source, modified Covered Source or Products to +a legal entity carrying out development, testing or quality assurance +work on Your behalf provided that the work is performed on terms which +prevent the entity from both using the Source or Products for its own +internal purposes and Conveying the Source or Products or any +modifications to them to any person other than You. Any modifications +made by the entity shall be deemed to be made by You pursuant to +subsection 3.2. + + +6 DISCLAIMER AND LIABILITY + + 6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products + are provided 'as is' and any express or implied warranties, + including, but not limited to, implied warranties of + merchantability, of satisfactory quality, non-infringement of + third party rights, and fitness for a particular purpose or use + are disclaimed in respect of any Source or Product to the + maximum extent permitted by law. The Licensor makes no + representation that any Source or Product does not or will not + infringe any patent, copyright, trade secret or other + proprietary right. The entire risk as to the use, quality, and + performance of any Source or Product shall be with You and not + the Licensor. This disclaimer of warranty is an essential part + of this Licence and a condition for the grant of any rights + granted under this Licence. + + 6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to + the maximum extent permitted by law, have no liability for + direct, indirect, special, incidental, consequential, exemplary, + punitive or other damages of any character including, without + limitation, procurement of substitute goods or services, loss of + use, data or profits, or business interruption, however caused + and on any theory of contract, warranty, tort (including + negligence), product liability or otherwise, arising in any way + in relation to the Covered Source, modified Covered Source + and/or the Making or Conveyance of a Product, even if advised of + the possibility of such damages, and You shall hold the + Licensor(s) free and harmless from any liability, costs, + damages, fees and expenses, including claims by third parties, + in relation to such use. + + +7 Patents + + 7.1 Subject to the terms and conditions of this Licence, each + Licensor hereby grants to You a perpetual, worldwide, + non-exclusive, no-charge, royalty-free, irrevocable (except as + stated in subsections 7.2 and 8.4) patent license to Make, have + Made, use, offer to sell, sell, import, and otherwise transfer + the Covered Source and Products, where such licence applies only + to those patent claims licensable by such Licensor that are + necessarily infringed by exercising rights under the Covered + Source as Conveyed by that Licensor. + + 7.2 If You institute patent litigation against any entity (including + a cross-claim or counterclaim in a lawsuit) alleging that the + Covered Source or a Product constitutes direct or contributory + patent infringement, or You seek any declaration that a patent + licensed to You under this Licence is invalid or unenforceable + then any rights granted to You under this Licence shall + terminate as of the date such process is initiated. + + +8 General + + 8.1 If any provisions of this Licence are or subsequently become + invalid or unenforceable for any reason, the remaining + provisions shall remain effective. + + 8.2 You shall not use any of the name (including acronyms and + abbreviations), image, or logo by which the Licensor or CERN is + known, except where needed to comply with section 3, or where + the use is otherwise allowed by law. Any such permitted use + shall be factual and shall not be made so as to suggest any kind + of endorsement or implication of involvement by the Licensor or + its personnel. + + 8.3 CERN may publish updated versions and variants of this Licence + which it considers to be in the spirit of this version, but may + differ in detail to address new problems or concerns. New + versions will be published with a unique version number and a + variant identifier specifying the variant. If the Licensor has + specified that a given variant applies to the Covered Source + without specifying a version, You may treat that Covered Source + as being released under any version of the CERN-OHL with that + variant. If no variant is specified, the Covered Source shall be + treated as being released under CERN-OHL-S. The Licensor may + also specify that the Covered Source is subject to a specific + version of the CERN-OHL or any later version in which case You + may apply this or any later version of CERN-OHL with the same + variant identifier published by CERN. + + 8.4 This Licence shall terminate with immediate effect if You fail + to comply with any of its terms and conditions. + + 8.5 However, if You cease all breaches of this Licence, then Your + Licence from any Licensor is reinstated unless such Licensor has + terminated this Licence by giving You, while You remain in + breach, a notice specifying the breach and requiring You to cure + it within 30 days, and You have failed to come into compliance + in all material respects by the end of the 30 day period. Should + You repeat the breach after receipt of a cure notice and + subsequent reinstatement, this Licence will terminate + immediately and permanently. Section 6 shall continue to apply + after any termination. + + 8.6 This Licence shall not be enforceable except by a Licensor + acting as such, and third party beneficiary rights are + specifically excluded. diff --git a/MIGRATION_MAP.md b/MIGRATION_MAP.md new file mode 100644 index 0000000..711783b --- /dev/null +++ b/MIGRATION_MAP.md @@ -0,0 +1,69 @@ +# JII private-history sanitized replay map + +This file maps the pinned private AMBIT history to the publication-candidate +parallel replay. It is an audit record, not permission to push any ref. + +> **Buildability disclosure:** removing the vendor SDK makes rewritten commits 1–22 +> intentionally non-buildable: retained application files still include the now-absent +> `lib/ADPD6000/adi_adpd6000.h` and use ADPD6000 interface/API identifiers. Commit 23 +> introduces the clean-room replacement; commits 23–24 no longer have that dangling +> vendor include. The replay preserves historical attribution and development topology, +> not a buildable artifact at every redacted revision. + +> **Private PR-number disclosure:** the preserved subjects `Merge pull request #1`, `#2`, +> and `#3` refer to private `Jan-IngenHousz-Institute/ambit-iot` pull requests. GitHub may +> auto-link those numbers to unrelated pull requests in the public `ambit` fork; those +> public links are not the historical review records. + +## Fixed inputs and transformation + +- Public base: `c81c928c77849833fec08a50fd2dc1d5d7a5749f` (`Jan-IngenHousz-Institute/ambit/main`). +- Private source: `8126a5f2ae174848d40d5b12f464da1f24bbb055` (`Jan-IngenHousz-Institute/ambit-iot/main`). +- Sanitized replay tip: `1cb4e60c2f33579ded7fb29b715a5dd97ee73f7f`. +- Exactly 24 commits are replayed from one parallel root; the observed graph has three merge commits. +- Author and committer names/emails, timestamps/time zones, full commit messages, ordering, parent counts, and parent topology are preserved. +- `src/src/adpd/lib/ADPD6000/**` is removed from every rewritten tree. Historical include paths and API identifiers outside that prefix remain intentionally and are license-review inputs. +- `260605_amBIT_DEMO.ipynb` is parsed as JSON; all 12 source cells are retained, code-cell `outputs` become `[]`, and `execution_count` becomes `null`. It is serialized as sorted, one-space-indented UTF-8 JSON with a trailing newline. +- Existing commit signatures are omitted because they sign the original object IDs and cannot validly sign rewritten objects. No replacement signature is invented. +- Empty and degenerate commits are retained by direct `git commit-tree` construction; no prune operation is performed. + +## One-to-one commit proof + +| # | Original commit / parents | Rewritten commit / parents | Author; committer | Dates | Message | Redaction disposition | +| ---: | --- | --- | --- | --- | --- | --- | +| 1 | `e620ef1dea403708768ab9b3ede288b700cbd637`
parallel root | `33fbfbdc002993add8a1fa04ff09af73494ab67f`
parallel root | A: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org>
C: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org> | A: `1781699352 +0200`
C: `1781699352 +0200` | first commit | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained | +| 2 | `e8527a302d09283483c087d3e48ba32510b0b3d5`
`e620ef1dea403708768ab9b3ede288b700cbd637` | `41e7df3b775751ada5136680704ddfd698a71cab`
`33fbfbdc002993add8a1fa04ff09af73494ab67f` | A: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org>
C: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org> | A: `1781700563 +0200`
C: `1781700563 +0200` | first commit | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained | +| 3 | `db54fd6807ab5dd86a2a3544e077c389ece56a8e`
`e8527a302d09283483c087d3e48ba32510b0b3d5` | `c53709ada4a7df1e7197e7de360bcfaf1672bf97`
`41e7df3b775751ada5136680704ddfd698a71cab` | A: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org>
C: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org> | A: `1781709648 +0200`
C: `1781709648 +0200` | first commit | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained | +| 4 | `403ef011d40e3e88fdcbc445e04dd1d3ca3384af`
`db54fd6807ab5dd86a2a3544e077c389ece56a8e` | `3b97bc660af228b8b46c340263936feb9fbfd2d9`
`c53709ada4a7df1e7197e7de360bcfaf1672bf97` | A: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org>
C: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org> | A: `1781710626 +0200`
C: `1781710626 +0200` | cloud: add get_gains / get_currents (read back staged ADPD config) | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained | +| 5 | `57810a483cd6a96cd321a05d90326992181318b8`
`403ef011d40e3e88fdcbc445e04dd1d3ca3384af` | `0e537765f395554f55afb94c034c355577588cfd`
`3b97bc660af228b8b46c340263936feb9fbfd2d9` | A: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org>
C: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org> | A: `1781710651 +0200`
C: `1781710651 +0200` | cloud mode | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained | +| 6 | `5b18b9a5c723476f1c17e866ff6542c21535d3ab`
`57810a483cd6a96cd321a05d90326992181318b8` | `c4b7ccdcdd8ce1a60c82cc399359ec5443aebbfb`
`0e537765f395554f55afb94c034c355577588cfd` | A: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org>
C: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org> | A: `1781710679 +0200`
C: `1781710679 +0200` | cloud mode | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained | +| 7 | `b5beac36386154cc45a3309e57b4f77f8c05680e`
`5b18b9a5c723476f1c17e866ff6542c21535d3ab` | `50f0a8dd994bc697364e42e7fc54b6e98b92e26b`
`c4b7ccdcdd8ce1a60c82cc399359ec5443aebbfb` | A: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org>
C: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org> | A: `1781710792 +0200`
C: `1781710792 +0200` | cloude mode | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained | +| 8 | `d07107aa580af3bf7580b644d7b60243c3da63ef`
`b5beac36386154cc45a3309e57b4f77f8c05680e` | `875bc4cad56407e2617f74db98dbb4731e3e5e4c`
`50f0a8dd994bc697364e42e7fc54b6e98b92e26b` | A: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org>
C: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org> | A: `1781725062 +0200`
C: `1781725062 +0200` | Add CERN-OHL-S-2.0 licence | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained | +| 9 | `81a529a53bcfe658f3f24e2156ce0091387a5935`
`d07107aa580af3bf7580b644d7b60243c3da63ef` | `4919aa3dfdc8a9c0ccd5881f2f0986c2aa4136b2`
`875bc4cad56407e2617f74db98dbb4731e3e5e4c` | A: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org>
C: Ludo-lab <ludovico.caracciolo@jan-ingenhousz-institute.org> | A: `1781865170 +0200`
C: `1781865170 +0200` | changed array name to be more transparent for user. Returns fluo = s_630/r_630 | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained | +| 10 | `8cf5313dbe4fe847ccbb73655870a86b33d55678`
`81a529a53bcfe658f3f24e2156ce0091387a5935` | `f4ffa07c1f6f5497090f187a524b607feb72fe8b`
`4919aa3dfdc8a9c0ccd5881f2f0986c2aa4136b2` | A: dominikvrbic <dominik.vrbic@jii.org>
C: dominikvrbic <dominik.vrbic@jii.org> | A: `1785839702 +0200`
C: `1785839702 +0200` | docs: add AGENTS.md and CLAUDE.md agent guidance | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained; invalidated original signature omitted | +| 11 | `da03492c2a38d037b3517a24b62f05ccc2c7e14a`
`8cf5313dbe4fe847ccbb73655870a86b33d55678` | `d5e9393b0ffac1c4e7a94107df473e1054d13861`
`f4ffa07c1f6f5497090f187a524b607feb72fe8b` | A: dominikvrbic <dominik.vrbic@jii.org>
C: dominikvrbic <dominik.vrbic@jii.org> | A: `1785840032 +0200`
C: `1785840032 +0200` | feat(fw): single-image firmware with runtime host routing | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained; invalidated original signature omitted | +| 12 | `43ee5c189a930f26c0f88eddadd1d62550549e1b`
`da03492c2a38d037b3517a24b62f05ccc2c7e14a` | `0972449eaab0466d11925f56c2b76dc15235a03b`
`d5e9393b0ffac1c4e7a94107df473e1054d13861` | A: dominikvrbic <dominik.vrbic@jii.org>
C: dominikvrbic <dominik.vrbic@jii.org> | A: `1785840178 +0200`
C: `1785840178 +0200` | ci: add semantic-release versioning and release workflows | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained; invalidated original signature omitted | +| 13 | `545879ddd951d3db8267e65a46a3ae0296832f23`
`81a529a53bcfe658f3f24e2156ce0091387a5935`
`43ee5c189a930f26c0f88eddadd1d62550549e1b` | `b1e9a17e7f66d2a3bce231aa4dade646474d62e9`
`4919aa3dfdc8a9c0ccd5881f2f0986c2aa4136b2`
`0972449eaab0466d11925f56c2b76dc15235a03b` | A: Ludovico Caracciolo <ludovico.caracciolo@jan-ingenhousz-institute.org>
C: GitHub <noreply@github.com> | A: `1785847615 +0200`
C: `1785847615 +0200` | Merge pull request #1 from Jan-IngenHousz-Institute/feat/single-image-firmware | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained; invalidated original signature omitted | +| 14 | `531d7387fd30f5fb6a8412ac8f1bce10ad16cf70`
`545879ddd951d3db8267e65a46a3ae0296832f23` | `bba87254702e619df407f651fb0d5afb05e87225`
`b1e9a17e7f66d2a3bce231aa4dade646474d62e9` | A: dominikvrbic <dominik.vrbic@jii.org>
C: dominikvrbic <dominik.vrbic@jii.org> | A: `1785848826 +0200`
C: `1785848826 +0200` | ci(release): resolve the release PR via the API, not the commit subject | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained; invalidated original signature omitted | +| 15 | `b9008f0bfed95ae6c3f61008b98d4df5bd140dd4`
`531d7387fd30f5fb6a8412ac8f1bce10ad16cf70` | `1f7f0622a75ed2607797975cd21c4395b0c72386`
`bba87254702e619df407f651fb0d5afb05e87225` | A: dominikvrbic <dominik.vrbic@jii.org>
C: dominikvrbic <dominik.vrbic@jii.org> | A: `1785850344 +0200`
C: `1785850344 +0200` | fix(router): never let a finished envelope stall the first-byte router | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained; invalidated original signature omitted | +| 16 | `fda6d46b3780392f13e9e9d2139405cc789be331`
`b9008f0bfed95ae6c3f61008b98d4df5bd140dd4` | `1218d74c9f65bd712ad648d8afea957b451dee08`
`1f7f0622a75ed2607797975cd21c4395b0c72386` | A: dominikvrbic <dominik.vrbic@jii.org>
C: dominikvrbic <dominik.vrbic@jii.org> | A: `1785850896 +0200`
C: `1785850896 +0200` | fix(uart): stop ARDUHAL writing ASCII onto the protocol port | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained; invalidated original signature omitted | +| 17 | `4c9b90fce36a6a1ed786e5b8c7baa5bef18e1dbd`
`fda6d46b3780392f13e9e9d2139405cc789be331` | `27971fa0ef61d44eb13aacdad59995465bf26793`
`1218d74c9f65bd712ad648d8afea957b451dee08` | A: dominikvrbic <dominik.vrbic@jii.org>
C: dominikvrbic <dominik.vrbic@jii.org> | A: `1785851115 +0200`
C: `1785851115 +0200` | Revert "fix(uart): stop ARDUHAL writing ASCII onto the protocol port" | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained; invalidated original signature omitted | +| 18 | `0bf93c41af9484875f4d4cf20a8394d0e8b47779`
`4c9b90fce36a6a1ed786e5b8c7baa5bef18e1dbd` | `65fd01ee25d170a0680da7ecec5af13899ec4a9e`
`27971fa0ef61d44eb13aacdad59995465bf26793` | A: dominikvrbic <dominik.vrbic@jii.org>
C: dominikvrbic <dominik.vrbic@jii.org> | A: `1785851307 +0200`
C: `1785851307 +0200` | Reapply "fix(uart): stop ARDUHAL writing ASCII onto the protocol port" | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained; invalidated original signature omitted | +| 19 | `4dde4b31aa65d5a76947396a0c92aab4d83c06b6`
`0bf93c41af9484875f4d4cf20a8394d0e8b47779` | `1be78f45d575cffb8b0f66904d21c5cc89b4008c`
`65fd01ee25d170a0680da7ecec5af13899ec4a9e` | A: dominikvrbic <dominik.vrbic@jii.org>
C: dominikvrbic <dominik.vrbic@jii.org> | A: `1785856170 +0200`
C: `1785856170 +0200` | fix(json): honor arrun request length | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained; invalidated original signature omitted | +| 20 | `30aaf6f6022056ce75241a04394b7a5a814df9e1`
`4dde4b31aa65d5a76947396a0c92aab4d83c06b6` | `9732b2f288e57796d98d3862afc98c7f84b7f586`
`1be78f45d575cffb8b0f66904d21c5cc89b4008c` | A: dominikvrbic <dominik.vrbic@jii.org>
C: dominikvrbic <dominik.vrbic@jii.org> | A: `1785916007 +0200`
C: `1785916007 +0200` | fix(router): keep unknown host discovery awake | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained | +| 21 | `3b10be18e75d230ef8b4f4cc02feeddc05da3861`
`30aaf6f6022056ce75241a04394b7a5a814df9e1` | `4ed94decbbd57157efd9a2e9e61768ae197e70e7`
`9732b2f288e57796d98d3862afc98c7f84b7f586` | A: dominikvrbic <dominik.vrbic@jii.org>
C: dominikvrbic <dominik.vrbic@jii.org> | A: `1785917230 +0200`
C: `1785917230 +0200` | docs(hw): record rc4 conformance run | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained | +| 22 | `97b18c30ebc3abb73a436411223df31cdf789a75`
`531d7387fd30f5fb6a8412ac8f1bce10ad16cf70`
`3b10be18e75d230ef8b4f4cc02feeddc05da3861` | `e80e81e7f8921d802af257c9dafb51d56874ef85`
`bba87254702e619df407f651fb0d5afb05e87225`
`4ed94decbbd57157efd9a2e9e61768ae197e70e7` | A: Dominik Vrbic <dominikvrbic01@gmail.com>
C: GitHub <noreply@github.com> | A: `1785958795 +0100`
C: `1785958795 +0100` | Merge pull request #2 from Jan-IngenHousz-Institute/fix/openjii-router-stall | removed 9 ADPD6000 vendor paths; notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; non-buildable after vendor removal; dangling interface references retained; invalidated original signature omitted | +| 23 | `bebd67b6e4141588a7a8943be70c4ee3ebd3ee41`
`3b10be18e75d230ef8b4f4cc02feeddc05da3861` | `8fb51cbed3f653b2c3d8f3922cb90c796bacf41c`
`4ed94decbbd57157efd9a2e9e61768ae197e70e7` | A: dominikvrbic <dominik.vrbic@jii.org>
C: dominikvrbic <dominik.vrbic@jii.org> | A: `1785959810 +0200`
C: `1785959810 +0200` | chore(adpd): replace vendor SDK with JII clean-room driver | notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; clean-room replacement present; invalidated original signature omitted | +| 24 | `8126a5f2ae174848d40d5b12f464da1f24bbb055`
`97b18c30ebc3abb73a436411223df31cdf789a75`
`bebd67b6e4141588a7a8943be70c4ee3ebd3ee41` | `1cb4e60c2f33579ded7fb29b715a5dd97ee73f7f`
`e80e81e7f8921d802af257c9dafb51d56874ef85`
`8fb51cbed3f653b2c3d8f3922cb90c796bacf41c` | A: Dominik Vrbic <dominikvrbic01@gmail.com>
C: GitHub <noreply@github.com> | A: `1785960902 +0100`
C: `1785960902 +0100` | Merge pull request #3 from Jan-IngenHousz-Institute/fix/openjii-router-stall | notebook: retained 12 source cells; cleared 9 outputs and 8 execution counts; clean-room replacement present; invalidated original signature omitted | + +## Historical private tag-target mapping + +These rows are mapping evidence only. This candidate creates, moves, and pushes no tag. + +| Private tag | Original type / tag object | Original commit target | Rewritten commit target | Candidate action | +| --- | --- | --- | --- | --- | +| `v1.0.0` | annotated; `3aea116d094377ac2da48e7a378a30587042190b` | `81a529a53bcfe658f3f24e2156ce0091387a5935` | `4919aa3dfdc8a9c0ccd5881f2f0986c2aa4136b2` | mapping only; no ref created | +| `v1.1.0` | lightweight; not applicable | `531d7387fd30f5fb6a8412ac8f1bce10ad16cf70` | `bba87254702e619df407f651fb0d5afb05e87225` | mapping only; no ref created | +| `v1.1.1` | lightweight; not applicable | `97b18c30ebc3abb73a436411223df31cdf789a75` | `e80e81e7f8921d802af257c9dafb51d56874ef85` | mapping only; no ref created | + +Existing public annotated `v1.1.2` and `v1.1.3-rc1` remain unchanged and peel to the pinned public base. diff --git a/ORIGIN.md b/ORIGIN.md new file mode 100644 index 0000000..3d5fc2e --- /dev/null +++ b/ORIGIN.md @@ -0,0 +1,18 @@ +# Origin and continuation history + +This repository is a genuine GitHub fork of +[`hjc2023/ambit`](https://github.com/hjc2023/ambit). The upstream v1 work and its authorship +remain visible in the inherited Git history and are not re-authored or presented as JII's +later work. + +The first JII continuation snapshot is based on upstream commit +`64b3bf72a757b594e61fd48398dd1db9a893fa93` and imports the maintained firmware tree from +JII private commit `bebd67b6e4141588a7a8943be70c4ee3ebd3ee41`. The private +repository's commits are deliberately not grafted onto the public fork: the snapshot records +a new continuation boundary without inventing historical ancestry. + +The continuation removes the ADPD6000 vendor SDK subtree from the current source tree and +uses a JII-owned driver written against public hardware-interface facts. This removal does +not rewrite or conceal objects already present in the inherited upstream history. Modified +inherited files retain their existing author notices; JII-authored files carry JII copyright +and SPDX identifiers. Other bundled third-party components retain their own notices. diff --git a/PUBLICATION_AUDIT.md b/PUBLICATION_AUDIT.md new file mode 100644 index 0000000..9821d35 --- /dev/null +++ b/PUBLICATION_AUDIT.md @@ -0,0 +1,56 @@ +# Local publication audit — attributed-history restoration candidate + +This record defines the local candidate gate. It authorizes no push, PR, tag, release, +workflow, hardware, private-repository, or downstream change. + +## Object scope + +- Existing public base: `c81c928c77849833fec08a50fd2dc1d5d7a5749f`. +- Unsanitized private source (comparison only; excluded from transfer): `8126a5f2ae174848d40d5b12f464da1f24bbb055`. +- Sanitized parallel replay tip: `1cb4e60c2f33579ded7fb29b715a5dd97ee73f7f`. +- Candidate ref: `refs/heads/history/restoration-candidate`; its exact object ID is resolved and recorded in the separately retained private audit package after the merge exists. +- Intended transfer refspec: only `refs/heads/history/restoration-candidate:refs/heads/history/restoration-candidate`; no tag or wildcard refspec. +- `git rev-list --objects history/restoration-candidate --not refs/remotes/public/main` yields a conservative review and push superset of 96 objects / 33 blobs, including the candidate object. It is not the literal wire pack or exact server-missing set: four historical wrapper blobs are already reachable from pinned public `main`, while 92 objects / 29 blobs are not reachable from that main tip; negotiation may omit further objects available through other server refs. +- A commit cannot self-contain its own ID. The candidate ID, raw superset manifest, pack, bundle, SHA-256 attestations, 47-name inventory, and 4/29 reachability proof are retained in a separate external audit package supplied to independent reviewers. That package is not published at a dead in-repository path; public readers can inspect this record and `MIGRATION_MAP.md` directly. + +## Sanitization accuracy and buildability + +The direct `git commit-tree` replay never prunes empty or degenerate commits. It removes only the ADPD6000 vendor prefix and clears notebook output/execution fields; all other historical blobs are copied by object ID. Commit signatures are omitted because their object IDs changed. + +Rewritten commits 1–22 are intentionally non-buildable because retained application files still include the removed `lib/ADPD6000/adi_adpd6000.h`. Commit 23 introduces the clean-room replacement, and commits 23–24 no longer have the dangling vendor include. The complete retained set is 47 interface/register/API names: every name occurs in four historical wrapper blobs already reachable from pinned public `main`; four names—`ADPD6000`, `adi_adpd6000`, `API_ADPD6000_ERROR_OK`, and `ADPD6000_ERROR_RETURN`—recur as quotations in the two new audit Markdown files; zero occur in any genuinely new non-documentation blob. No excluded vendor implementation body is introduced. + +The publication owner has technically dispositioned the already-public wrapper interface/register/API names as preservable for truthful attributed history while excluding vendor implementation bodies. This is a technical scope decision, not legal advice or a license grant. + +The preserved merge subjects `#1`, `#2`, and `#3` identify private `ambit-iot` PRs. Any GitHub auto-link to the same number in public `ambit` is unrelated. + +## Machine-enforced local gates + +The private audit runner exits nonzero unless all of these checks pass: + +1. Both remote pins match; a fresh reconstruction byte-matches the approved 24-row mapping and replay tip without creating or moving a replay ref. +2. Author/committer identities and raw timestamps, message bytes, ordering, parent counts, mapped topology, and all three merges match. +3. Every replay tree lacks the excluded vendor prefix; notebook source arrays match while outputs, execution counts, widgets, attachments, and cell metadata are absent. +4. Commits 1–22 have the disclosed missing vendor include and commits 23–24 do not; the independently derived 47-name set is exact, all 47 occur in four already-public wrappers, only four names recur in new documentation, and zero occur in new non-documentation blobs. +5. The candidate has the pinned public first parent, approved replay second parent, accountable JII author/committer, and a tree differing from public only by this file and `MIGRATION_MAP.md`. +6. Existing public tag objects/types/peels match remote state; historical private tag targets map to rewritten commits without creating local tag refs. +7. The conservative 96-object review/push superset and pack contain no original private commit, excluded ADPD blob, or original notebook blob; their four already-public / 92 not-reachable split is asserted, and strict fsck, pack verification, and portable bundle verification pass. +8. Pinned Gitleaks 8.28.0 reports zero findings both for Git patches and for all 33 reviewed blobs—29 not reachable from pinned public `main` plus four already-public wrappers; the count and byte total are recorded. +9. The private audit package lives outside the worktree; both `git status --porcelain` and `git add -A --dry-run` are empty. + +## Human-reviewed inventories + +The runner produces contributor identities, privacy/device and URL candidates, the complete residual identifier/interface inventory, and MIME/size inventories. Contributor-identity privacy approval was supplied by the user's explicit choice to preserve exact attribution and execute publication. The publication-owner technical disposition permits the 47 already-public wrapper names to remain for truthful history while excluding vendor implementation bodies. That disposition is not legal advice or a license grant. + +## Reproducible toolchain + +- `git version 2.55.0` +- `jq-1.8.1` +- `Python 3.14.6` +- `file-5.46` +- Gitleaks `8.28.0`; release archive SHA-256 `a65b5253807a68ac0cafa4414031fd740aeb55f54fb7e55f386acb52e6a840eb`; extracted binary SHA-256 `5fd1b3b0073269484d40078662e921d07427340ab9e6ed526ccd215a565b3298`. + +The separately supplied private audit package contains the approved mapping, deterministic reconstruction generator, documentation generator, single verification entry point, exact commands, raw inventories, checksums, pack, and portable bundle. Each run uses a new output directory and does not delete prior evidence. + +## Review boundary and residuals + +Contributor-identity privacy approval and the 47-name publication-owner technical disposition are resolved for this corrective lane. Original signatures cannot survive rewritten object IDs and remain transparently omitted. Ticket 01 remains open for broader work but is not a dependency of this sanitized ticket-12 lane. A fresh independent review of the exact rebuilt candidate and later hosted-graph review remain mandatory. No public operation is authorized by this local audit. diff --git a/README.md b/README.md new file mode 100644 index 0000000..eb50749 --- /dev/null +++ b/README.md @@ -0,0 +1,410 @@ +# AMBIT firmware + +AMBIT is an ESP32-C3 leaf-sensor platform developed by the Jan IngenHousz +Institute. This repository contains the firmware that coordinates three sensing +paths on the AMBIT board: + +- ADPD6100 optical measurements for fluorescence and PAM-style experiments; +- AS7341 spectral measurements and PAR calculation; and +- MLX90632 leaf and ambient temperature measurements. + +This repository is the public JII continuation of +[`hjc2023/ambit`](https://github.com/hjc2023/ambit). Its public upstream ancestry +and the attributed JII development record are preserved explicitly; see +[`ORIGIN.md`](ORIGIN.md), [`MIGRATION_MAP.md`](MIGRATION_MAP.md), and +[`PUBLICATION_AUDIT.md`](PUBLICATION_AUDIT.md) for the provenance boundary. + +One firmware image serves the supported hosts at runtime. It can communicate +with an Ambyte datalogger over the binary UART protocol, with the openJII app and +Calibratron over the legacy text console, or with an openJII JSON-envelope host. +It also stores device identity, calibration, and measurement metadata in NVS. + +This README is an operator and integrator orientation. The source and linked +plans remain authoritative for protocol bytes, measurement behavior, and +hardware-conformance requirements. + +## Hardware and interface boundaries + +The PlatformIO target is `esp32-c3-devkitm-1`, used to build firmware for the +AMBIT ESP32-C3 board. The attached sensors and firmware buses are: + +| Function | Device | Firmware interface | +| --- | --- | --- | +| Fluorescence / optical acquisition | ADPD6100 | SPI | +| Spectral channels / PAR | AS7341 | I2C, address `0x39` | +| Leaf and ambient temperature | MLX90632 | I2C, address `0x3A` | +| Host protocol | Ambyte, openJII, or Calibratron | UART0, 115200 8N1 | + +UART0 is mapped to GPIO20 RX and GPIO21 TX. The Ambyte uses that UART over the +board's FFC connection. Native USB CDC is disabled in the firmware build; +USB-Serial-JTAG on GPIO18/19 is a recovery/programming path, not an additional +live host-protocol port. + +> [!CAUTION] +> This firmware repository does not define the board connector pinout, supply +> voltage limits, or a safe external-powering procedure. Do not infer those +> electrical limits from the generic PlatformIO board name or the GPIO numbers +> above. Use the AMBIT hardware documentation and approved programmer/fixture +> for the exact board revision. Disconnect the device from measurement hosts +> before attaching programming hardware unless the fixture instructions +> explicitly support that arrangement. + +Flashing is also a data boundary: never erase NVS on a calibrated device merely +to troubleshoot communication. A full flash must use a mutually matching set +of release assets and the offsets in that release's `manifest.json`. + +## Use a release or build from source + +For field devices, prefer a published [GitHub release](https://github.com/Jan-IngenHousz-Institute/ambit/releases). +Release assets are produced together, hashed in a manifest, and intended for +use by release-aware tooling such as the Calibratron. Do not combine binaries +from different releases. + +Build from source when developing or validating a change. The project uses the +Arduino framework through PlatformIO and has one environment, `ambit`. + +1. Install Git and PlatformIO Core (or a PlatformIO-enabled IDE). +2. Clone this repository and enter its root directory. +3. On Linux or macOS, provide a writable `LOCALAPPDATA` because the build output + is deliberately redirected outside the working tree: + + ```sh + export LOCALAPPDATA="$HOME/.cache" + ``` + +4. Build the firmware: + + ```sh + pio run + ``` + +For a development board connected through a PlatformIO-detected upload port, +add the upload target: + +```sh +pio run -t upload +``` + +The serial monitor settings are 115200 baud, 8 data bits, no parity, one stop +bit. Uploading directly is a development/recovery operation; field upgrades +should follow the release and OTA guidance below. + +### Build-time version identity + +Do not edit a version literal in the source. [`tools/version.py`](tools/version.py) +injects one version using this precedence: + +1. `AMBIT_RELEASE_VERSION`, set by CI for a release or PR candidate; +2. `git describe --tags --always --dirty` for a local build; or +3. `0.0.0-dev` when neither source is available. + +The complete string is reported by text `hello`, the JSON identity, and is +embedded in the application image. The frozen binary firmware-info response +(command 33, subtype 2) also exposes legacy `uint8` major/minor/batch fields. +Those three bytes are parsed only when the version begins with +`..`; otherwise they are `0.0.0`. + +This matters for non-release images: + +- a local build described as `1.2.3-4-gabcdef` reports that complete string and + numeric identity `1.2.3`; +- a PR candidate such as `pr-42-abcdef123456` reports that complete string but + numeric identity `0.0.0`; and +- the current release automation publishes stable semantic versions from + `main`; it does not define a separate prerelease channel. + +Use the complete text/JSON version (or the release manifest version) when +distinguishing candidate builds. Numeric command 33/2 identity remains useful +for compatibility with existing Ambyte readers, but is not unique for every +development build. + +## One image, three host frontends + +There are no current `ambyte` and `cloud` firmware variants. `platformio.ini` +builds one image, and [`loop()`](src/ambit-1.ino) routes UART0 by the first byte: + +| First input | Frontend | Intended host | Orientation | +| --- | --- | --- | --- | +| Binary framing byte (`> 127`) | Frozen Ambyte adapter / FSM | Ambyte datalogger over FFC | [`src/run_esp.cpp`](src/run_esp.cpp), [`src/data_utils.cpp`](src/data_utils.cpp), [wire-contract notes](plans/MERGE_PLAN.md) | +| `{` or `[` | openJII JSON envelope | JSON-capable openJII integration | [`src/frontend_json.cpp`](src/frontend_json.cpp), [`lib/openjii_proto/`](lib/openjii_proto/) | +| Other printable text | Legacy line console | openJII app and Calibratron | [`src/do_command.h`](src/do_command.h) | + +Unknown and text hosts remain awake so their first request is not lost. A valid +Ambyte wake latches the binary host behavior, including its light-sleep and +idle-heartbeat contract. The text latch is released after two minutes without +traffic so the same device can later be attached to an Ambyte without rebooting. + +All three frontends share the same command core and staged measurement +configuration. Transport-specific encodings are normalized in their adapter; +they are not part of the core measurement API. + +### Text console + +The text console accepts the established openJII/Calibratron dialect. Send a +printable command line over UART0. For example, `hello` replies in the form: + +```text +NEW Ready FW: +``` + +`NEW` and `Ready` are compatibility sentinels used by existing hosts. The +complete command dispatch, argument reads, and response shapes are defined in +[`src/do_command.h`](src/do_command.h). Some commands mutate calibration or NVS; +integrators should not probe unfamiliar verbs on a calibrated field device. + +### openJII JSON envelope + +Only requests beginning with `{` or `[` reach the JSON-envelope frontend. The +currently registered surface includes identity, temperature, raw/calibrated +PAR, staged current/gain accessors, and streamed array runs. See +[`frontend_json_register()`](src/frontend_json.cpp) for the exact command set +and [`openjii_proto`](lib/openjii_proto/openjii_proto.cpp) for accepted envelope +forms, response framing, size limits, and errors. + +Bare printable lines do **not** enter the protocol library's generic LINE mode +in this firmware; they belong to the legacy text console above. + +### Ambyte binary protocol + +The binary path is a compatibility contract with deployed Ambyte firmware. +Framing values, struct sizes and offsets, checksums, gain indexing, and length +encodings must remain byte-compatible. Start with the frozen-contract section +of [`plans/MERGE_PLAN.md`](plans/MERGE_PLAN.md) and validate any change with +[`plans/HW_CONFORMANCE.md`](plans/HW_CONFORMANCE.md). The Ambyte implementation +is the final source of truth for the bytes it sends and accepts. + +UART0 is both the protocol stream and the only configured serial console. Extra +debug text can corrupt a binary session, so production code deliberately +suppresses ESP/Arduino logging on that port. The existing `BOOT` line and +startup metadata/calibration/firmware dump predate the frozen contract and are +tolerated by deployed hosts; expect them after reset or OTA reboot, but do not +add further unsolicited output. + +## Releases and flashing + +Every PR is designed to build one candidate asset set. When a semantic release +is required after merge, the release workflow retrieves and republishes that +exact PR-built artifact rather than rebuilding it. The release contains: + +| Asset | Role | Full-flash offset | +| --- | --- | --- | +| `ambit-fw-v.bin` | Application / OTA image | `0x10000` | +| `bootloader.bin` | ESP32-C3 bootloader | `0x0` | +| `partitions.bin` | Pinned dual-OTA partition table | `0x8000` | +| `boot_app0.bin` | OTA data initialization | `0xe000` | +| `manifest.json` | Flash metadata and SHA-256 hashes | n/a | + +Treat a published release as immutable. If any firmware bit or companion asset +must change, publish a new semantic version; do not replace an asset under an +existing tag. Before flashing, verify each selected file against the SHA-256 in +the same manifest. + +### OTA versus full flash + +Use an **OTA update** for a normally operating installed device whose partition +layout is already compatible. The Ambyte sends only the application image in +sequenced, CRC-checked chunks to the inactive OTA slot. The firmware verifies +the completed image, boots it pending confirmation, and retains the previous +slot for rollback if the Ambyte cannot confirm the new image. + +Use a **full flash** for first installation, recovery, or an explicitly planned +bootloader/partition migration. Release-aware tooling must write all four +regions at the manifest offsets. A field device's partition table should be +read and verified before rewriting it; the build pins Arduino-ESP32's 4 MB +dual-OTA `default.csv` layout specifically to prevent an unnoticed layout +change. + +An application-only OTA does not rewrite NVS. A full flash using the same +partition layout also leaves the NVS offset unchanged, but calibration is not +protected from an erase operation or an incompatible partition-table change. +Back up or record device calibration before recovery work. + +## Calibration, metadata, and NVS + +At boot, [`src/nvs1.cpp`](src/nvs1.cpp) loads the AMBIT name, calibration +factors, emissivity, actinic set points, ADPD baselines, and hardware revision +from the `config` NVS namespace. Site and measurement metadata use a separate +`metadata` namespace. MLX90632 coefficients are read from the sensor and +included in the calibration record. + +The field-facing paths apply the stored calibration consistently: + +- text `PAR`, JSON-envelope `PAR`, and binary command 31 multiply `get_PAR()` by + `spec_coef`; text/JSON `get_par` deliberately remain raw diagnostic endpoints; +- the six ADPD baselines map to `s_630`, `r_630`, `sun`, `leaf`, `s_730`, and + `r_730`, and are subtracted with saturation at zero exactly once before data + enters a result buffer; +- older devices with no stored sun/leaf offsets retain the historical 65,000 + correction until a complete baseline vector is calibrated; and +- both text and binary baseline operations save all six values in one NVS + commit, verify readback, and update runtime state only after success. + +Calibration acquisition and the raw PAR endpoints remain uncorrected on +purpose: the Calibratron needs those raw observations to derive coefficients. + +Binary command 6 retains its deployed silent acknowledgement contract: it +always returns `ESP_CMD_DONE` followed by `ESP_CMD_END`, with no payload or +success/failure status byte. Acquisition, sensor, timeout, validation, or NVS +failures therefore cannot be diagnosed from that reply. In particular, an +acquired `s_630` baseline above 400 is rejected and not persisted. The +authoritative calibration workflow is the Calibratron's text-console +`baseline` capture, which prints the six acquired values and explicitly reports +acquisition, range, and save failures. + +The deployed scalar/vector calibration setters are also silent on validation +failure: binary command 4 types 2 and 4, command 17, and command 18 preserve +their existing acknowledgement framing and add no status byte. Invalid +actinic/spec coefficients, or an invalid command-18 vector, are rejected +without persistence, but the acknowledgement alone does not prove that a new +value was saved. Use the text-console setters/readback during calibration when +operator-visible success or failure is required. + +Binary commands and selected text-console operations can update this state. +Metadata is persisted only when its frozen end marker is valid and its +longitude, latitude, and altitude pass the source's range checks. The binary +command acknowledges the transfer before those persistence checks, so its +framing acknowledgement alone does not prove the metadata was saved. +Calibration and metadata structures also travel over the deployed binary +protocol, so changing their layouts is a compatibility change, not ordinary +refactoring. + +> [!WARNING] +> The text command `clean_nvs` erases the NVS partition. Do not use it as a +> general reset on a calibrated device. Reflashing firmware is not a substitute +> for restoring calibration. + +## Validation + +The minimum software gate is a clean PlatformIO build: + +```sh +pio run +``` + +Host-side unit tests under [`test/adpd6000/`](test/adpd6000/) and +[`test/calibration/`](test/calibration/) verify the transport boundary and +baseline math, including legacy defaults and saturating subtraction. Run them +before approving a release candidate; PR CI compiles both with C++11 and treats +all warnings as errors. Release automation policy is covered by +[`test/release_process/`](test/release_process/) and the staged-draft procedure +is documented in [`plans/RELEASE_PROCESS.md`](plans/RELEASE_PROCESS.md). +Hardware remains the release-quality regression gate for transport and +measurement changes. + +Any change touching the binary adapter, FSM framing, or first-byte router must +run the real-device procedure in +[`plans/HW_CONFORMANCE.md`](plans/HW_CONFORMANCE.md). That procedure exercises a +real Ambyte, verifies framing/length/checksum behavior, and separates stable +wire bytes from sensor values that naturally vary with measurement noise. +Record the hardware, image identity, scope, and result in the conformance log. + +## Repository layout + +| Path | Purpose | +| --- | --- | +| [`src/ambit-1.ino`](src/ambit-1.ino) | Boot, sensor initialization, host latch, and runtime router | +| [`src/core.*`](src/core.h) | Transport-neutral configuration and command operations | +| [`src/PAM.cpp`](src/PAM.cpp) | Measurement engine, array/MPF runs, triggers, and output selection | +| [`src/run_esp.cpp`](src/run_esp.cpp) | Frozen Ambyte binary command adapter and OTA receiver | +| [`src/data_utils.cpp`](src/data_utils.cpp) | Binary data FSM, framing, and checksums | +| [`src/do_command.h`](src/do_command.h) | Legacy text-console command dispatch | +| [`src/frontend_json.cpp`](src/frontend_json.cpp) | AMBIT handlers for the openJII JSON envelope | +| [`lib/openjii_proto/`](lib/openjii_proto/) | Device-independent openJII protocol module | +| [`src/nvs1.*`](src/nvs1.h) | Persistent calibration, metadata, hardware revision, and firmware identity | +| [`src/src/`](src/src/) | Sensor drivers and board-level sensor interfaces | +| [`plans/`](plans/) | Merge rationale and reusable hardware-conformance procedure | +| [`.github/workflows/`](.github/workflows/) | PR validation/build and immutable-artifact release pipeline | +| [`tools/version.py`](tools/version.py) | Single-source build version injection | + +[`plans/MERGE_PLAN.md`](plans/MERGE_PLAN.md) is a historical design record. Its +old build-variant proposal is superseded by the single runtime-routed image, but +its frozen-wire rationale remains relevant. + +## Troubleshooting + +### PlatformIO cannot create the build directory + +`platformio.ini` uses `${sysenv.LOCALAPPDATA}` for a Windows OneDrive/antivirus +workaround. On Linux or macOS, set it to a writable location before building: + +```sh +export LOCALAPPDATA="$HOME/.cache" +pio run +``` + +### A text or JSON host gets no reply + +- Confirm 115200 8N1 on UART0 and that TX/RX are connected in the correct + directions for the approved interface. +- Native USB CDC is disabled; attaching to an unrelated USB serial port will + not expose the runtime protocol. +- JSON envelopes must begin with `{` or `[`. Bare printable input is interpreted + as a legacy text-console command. +- After a partial JSON request, allow its one-second receive timeout or reset the + device before switching protocols. + +### An Ambyte session reports framing or checksum errors + +- After reset or OTA reboot, expect and skip the firmware's legacy boot and NVS + text before starting a binary exchange. Unexpected ASCII during an active + exchange still indicates a writer corrupting the binary stream; remove any + terminal monitor or external debug writer from UART0. +- Confirm the Ambyte is using the deployed framing and struct definitions + referenced by the merge plan. +- Reproduce the failure with the capture and byte-level checks in + [`plans/HW_CONFORMANCE.md`](plans/HW_CONFORMANCE.md). + +### The reported version is `0.0.0` or differs between fields + +Compare the full `FW:`/JSON version first. A non-semantic development or PR +identifier intentionally maps to `0.0.0` in the legacy numeric binary fields. +For a published release, verify the running image against that release's +`manifest.json` and retrieve binary command 33/2 after an OTA reboot. + +### Calibration appears to be missing + +Stop before writing more flash. Check whether NVS was erased or whether a +different partition table was installed. Restore calibration using the +approved calibration workflow; do not guess coefficient values from defaults. + +## Contributing + +Keep changes focused and preserve the deployed transport contracts. Before a +pull request: + +1. read [`AGENTS.md`](AGENTS.md) and the relevant plan/source files; +2. build with `pio run`; +3. run hardware conformance when the router or binary path is affected; +4. use a Conventional Commit-style PR title, for example + `fix(router): preserve binary wake framing`; and +5. include verification evidence and call out any operator-visible or wire-level + effect. + +PR titles drive semantic release: `feat` requests a minor release; +`fix`, `perf`, and `revert` request a patch; breaking changes request a major; +and documentation, tests, refactors, build, CI, style, and chores do not publish +firmware on their own. + +Do not add ordinary logging to UART0, hand-edit version fields, reformat vendored +drivers wholesale, or change frozen structs/bytes without coordinating the host +side and completing conformance testing. + +## License and attribution + +The repository's covered source is provided under the +[CERN Open Hardware Licence Version 2 – Strongly Reciprocal](LICENSE.txt) +(`CERN-OHL-S-2.0`). Retain copyright, license, and source notices when modifying +or redistributing it. + +Vendored or adapted sensor code retains its upstream terms and attribution: + +- the AS7341 driver is from Adafruit Industries under the + [BSD 3-Clause License](src/src/as7341/license.txt); +- the MLX90632 driver retains Melexis N.V. copyright and Apache-2.0 notices in + [`src/src/mlx90632/`](src/src/mlx90632/); and +- the ADPD configuration wrapper retains its MSU-PRL Kramer Lab and Jan + IngenHousz Institute attribution in [`src/src/adpd/`](src/src/adpd/). + +The main repository license does not replace those third-party notices. Keep +them with redistributed source and review all applicable terms for a binary or +hardware distribution. diff --git a/adpd6000.pdf b/adpd6000.pdf deleted file mode 100644 index ba50964..0000000 Binary files a/adpd6000.pdf and /dev/null differ diff --git a/ambit-1.code-workspace b/ambit-1.code-workspace deleted file mode 100644 index 54d6f23..0000000 --- a/ambit-1.code-workspace +++ /dev/null @@ -1,19 +0,0 @@ -{ - "folders": [ - { - "path": "." - }, - { - "path": "D:/Python_env/conn_env" - } - ], - "settings": { - "files.associations": { - "bitset": "cpp", - "string_view": "cpp", - "*.tcc": "cpp", - "random": "cpp" - }, - "python-envs.defaultPackageManager": "ms-python.python:conda" - } -} \ No newline at end of file diff --git a/ambyte.ipynb b/ambyte.ipynb deleted file mode 100644 index 611363e..0000000 --- a/ambyte.ipynb +++ /dev/null @@ -1,574 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import serial\n", - "import numpy as np\n", - "import time\n", - "import matplotlib.pyplot as plt\n", - "from IPython.display import clear_output, display\n", - "\n", - "plt.style.use('dark_background')\n" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "NEW Name Here Ready\n", - "\n" - ] - } - ], - "source": [ - "with serial.Serial(\"COM31\", 115200, rtscts=False) as ser:\n", - " ser.write(b'hello\\n')\n", - " l = ser.readline().decode()\n", - " print(l)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "R mode\n", - "[ 30383][V][data_transfer.cpp:47] get_arr_len(): [DATA_TRANS] GET Array length 4, 4, 4, 4, 4, 4\n", - "[ 30383][V][data_transfer.cpp:61] allocate(): [DATA_TRANS] Allocation 16 bytes\n", - "[ 30422][E][data_transfer.cpp:108] get_data_arr(): [DATA_TRANS] Data length1:4, double check:4\n", - "0\n", - "0\n", - "data arr 0: 0,65536,256,1,\n", - "Arr 1 not init\n", - "Arr 2 not init\n", - "Arr 3 not init\n", - "Arr 4 not init\n", - "Arr 5 not init\n", - "[ 30433][V][data_transfer.cpp:14] ~DataPtk(): [DATA_TRANS] memory arr[0] freed.\n" - ] - } - ], - "source": [ - "info_outputs = \"\"\n", - "other_outputs = \"\"\n", - "with serial.Serial(\"COM31\", 115200) as ser:\n", - " start_timer = time.time()\n", - " ser.write(\"R,\".encode())\n", - " time.sleep(0.04)\n", - " ser.write(bytearray([72, 1, 0, 4] * 6))\n", - " time.sleep(0.04)\n", - " ser.write(bytearray([42, 1, 1, 3, 0, 4]))\n", - "\n", - " ser.write(bytearray([0, 0, 0, 1]))\n", - " ser.write(bytearray([0, 0, 1, 0]))\n", - " ser.write(bytearray([0, 1, 0, 0]))\n", - " ser.write(bytearray([1, 0, 0, 0]))\n", - " \n", - "\n", - " \n", - "\n", - " while (time.time() - start_timer < 1):\n", - " if ser.in_waiting > 0:\n", - " start_timer = time.time()\n", - " l = ser.read().decode()\n", - " print(l, end = \"\")\n", - "\n", - " else:\n", - " time.sleep(.5)\n", - " \n" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "R mode\n", - "b'\\xca'\n", - "[125977][E][data_transfer.cpp:56] get_arr_len(): [DATA_TRANS] TIMEOUT before getting ID\n", - "[125977][V][data_transfer.cpp:66] allocate(): [DATA_TRANS] Allocation 0 bytes\n", - "[125980][E][data_transfer.cpp:69] allocate(): [DATA_TRANS] Allocation failed\n", - "[125987][E][data_transfer.cpp:80] get_data_arr(): [DATA_TRANS] array expected length is 0\n", - "-1\n", - "Arr 0 not init\n", - "Arr 1 not init\n", - "Arr 2 not init\n", - "Arr 3 not init\n", - "Arr 4 not init\n", - "Arr 5 not init\n", - "BAD COMMAND\n" - ] - } - ], - "source": [ - "info_outputs = \"\"\n", - "other_outputs = \"\"\n", - "with serial.Serial(\"COM31\", 115200) as ser:\n", - " start_timer = time.time()\n", - " ser.write(\"R,\".encode())\n", - " time.sleep(0.04)\n", - " ser.write(bytearray([72, 1, 0, 4] * 6))\n", - " time.sleep(0.04)\n", - " ser.write(bytearray([42, 1, 1, 3, 0, 4]))\n", - "\n", - " ser.write(bytearray([0, 0, 0, 1]))\n", - " ser.write(bytearray([0, 0, 1, 0]))\n", - " ser.write(bytearray([0, 1, 0, 0]))\n", - " ser.write(bytearray([1, 0, 0, 0]))\n", - " \n", - "\n", - " \n", - "\n", - " while (time.time() - start_timer < 1):\n", - " if ser.in_waiting > 0:\n", - " start_timer = time.time()\n", - " l = ser.read()\n", - " if l[0] < 127:\n", - " print(l.decode(), end = \"\")\n", - " else:\n", - " print(l)\n", - "\n", - " else:\n", - " time.sleep(.5)\n", - " \n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def wait_for_control(ser, n, timeout = .1):\n", - " start_timer = time.time()\n", - " while (time.time() - start_timer < timeout):\n", - " if ser.in_waiting > 0:\n", - " b = ser.read()\n", - " if b[0] < 127:\n", - " print(b.decode(), end = \"\")\n", - " if b == bytes([n]):\n", - " return 1\n", - " else:\n", - " if b == bytes([n]):\n", - " return 1\n", - " return -1" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[ 2058][V][run_esp.cpp:23] do_esp_cmd(): [ESP] parse ESP commands\n", - "[ 2064][V][run_esp.cpp:41] do_esp_cmd(): [ESP] cmd is 1, 2, 0, 0, 0, 0, 0, 0\n", - "[ 2071][V][run_esp.cpp:52] do_esp_cmd(): [ESP] gains are 1, 0, 0, 0, 0, 0\n", - "[ 2082][V][run_esp.cpp:23] do_esp_cmd(): [ESP] parse ESP commands\n", - "[ 2088][V][run_esp.cpp:41] do_esp_cmd(): [ESP] cmd is 2, 110, 40, 20, 0, 0, 0, 0\n", - "[ 2095][V][run_esp.cpp:60] do_esp_cmd(): [ESP] currents are 110, 40, 20\n", - "[ 2102][V][run_esp.cpp:23] do_esp_cmd(): [ESP] parse ESP commands\n", - "[ 2108][V][run_esp.cpp:41] do_esp_cmd(): [ESP] cmd is 10, 0, 0, 0, 0, 0, 0, 0\n", - "[ 2115][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[ 2127][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[ 2138][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[ 2149][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[ 2159][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[ 2169][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[ 2179][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[ 2190][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[ 2200][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n" - ] - } - ], - "source": [ - "with serial.Serial(\"COM31\", 115200) as ser:\n", - " ser.write(bytes([160] + [1, 2, 0, 0, 0, 0, 0, 0]))\n", - "\n", - " ser.write(bytes([160] + [2, 110, 40, 20, 0, 0, 0, 0]))\n", - " \n", - " ser.write(bytes([160] + [10, 0, 0, 0, 0, 0, 0, 0]))\n", - "\n", - " wait_for_control(ser, 244, 1)\n", - " \n" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def get_trace(ser):\n", - " wait_for_control(ser, 212, 35)\n", - " ser.write(bytes([202]))\n", - " wait_for_control(ser, 212, 5)\n", - " a = ser.read(7)\n", - " wait_for_control(ser, 255, 0.01)\n", - " ser.write(bytes([200]))\n", - " arr1 = np.frombuffer(ser.read((a[2] * 256 + a[3]) * 4), dtype=np.uint32)\n", - " cs = arr1.sum()%256\n", - " cs1 = ser.read(4)[3]\n", - " if (cs == cs1): print('checked')\n", - "\n", - " wait_for_control(ser, 255, 0.01)\n", - " ser.write(bytes([180]))\n", - " return (cs == cs1), arr1" - ] - }, - { - "cell_type": "code", - "execution_count": 103, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[2393388][V][run_esp.cpp:23] do_esp_cmd(): [ESP] parse ESP commands\n", - "[2393394][V][run_esp.cpp:41] do_esp_cmd(): [ESP] cmd is 1, 2, 0, 0, 0, 0, 0, 0\n", - "[2393401][V][run_esp.cpp:52] do_esp_cmd(): [ESP] gains are 1, 0, 0, 0, 0, 0\n", - "[2393413][V][run_esp.cpp:23] do_esp_cmd(): [ESP] parse ESP commands\n", - "[2393419][V][run_esp.cpp:41] do_esp_cmd(): [ESP] cmd is 2, 110, 40, 20, 0, 0, 0, 0\n", - "[2393426][V][run_esp.cpp:60] do_esp_cmd(): [ESP] currents are 110, 40, 20\n", - "[2393433][V][run_esp.cpp:23] do_esp_cmd(): [ESP] parse ESP commands\n", - "[2393439][V][run_esp.cpp:41] do_esp_cmd(): [ESP] cmd is 20, 0, 0, 0, 0, 0, 0, 0\n", - "[2393446][V][PAM.cpp:298] MPF(): [PAM] RUN MPF with mode:0, pulse current:110, DC current 0\n", - "[2393454][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[2393461][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[2393468][V][data_utils.cpp:33] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[2393474][V][data_utils.cpp:64] clear(): [DATA_UTIL] ARR reset to 0\n", - "[2393481][V][data_utils.cpp:33] init(): [DATA_UTIL] Allocation 4320 bytes\n", - "[2393487][V][data_utils.cpp:64] clear(): [DATA_UTIL] ARR reset to 0\n", - "[2393493][V][PAM.cpp:303] MPF(): [PAM] Memory allocation completed\n", - "[2393502][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[2393514][V][PAM.cpp:341] MPF(): [PAM] Phase-0 Started\n", - "[2403519][V][PAM.cpp:352] MPF(): [PAM] Phase-0 Completed\n", - "[2403524][V][PAM.cpp:359] MPF(): [PAM] Phase-1 Config\n", - "[2403529][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[2403537][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 1\n", - "[2403546][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 2\n", - "[2403555][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 3\n", - "[2403564][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 4\n", - "[2403572][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 5\n", - "[2403581][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 6\n", - "[2403590][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 7\n", - "[2403598][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 8\n", - "[2403607][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 9\n", - "[2403616][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 10\n", - "[2403625][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 11\n", - "[2403639][V][PAM.cpp:369] MPF(): [PAM] Phase-1 LED ON\n", - "[2403653][V][PAM.cpp:386] MPF(): [PAM] Phase-1 Completed; Phase-2 start\n", - "[2403963][V][PAM.cpp:414] MPF(): [PAM] Phase-2 Completed; Phase-3 Start\n", - "[2403970][V][PAM.cpp:421] MPF(): [PAM] LED set to 220\n", - "[2404036][V][PAM.cpp:421] MPF(): [PAM] LED set to 190\n", - "[2404102][V][PAM.cpp:421] MPF(): [PAM] LED set to 160\n", - "[2404168][V][PAM.cpp:421] MPF(): [PAM] LED set to 130\n", - "[2404235][V][PAM.cpp:421] MPF(): [PAM] LED set to 100\n", - "[2404301][V][PAM.cpp:421] MPF(): [PAM] LED set to 70\n", - "[2404367][V][PAM.cpp:421] MPF(): [PAM] LED set to 40\n", - "[2404433][V][PAM.cpp:421] MPF(): [PAM] LED set to 10\n", - "[2404498][V][PAM.cpp:444] MPF(): [PAM] Phase-3 Completed; Phase-4 Start\n", - "[2404505][V][u_adpd6100.cpp:826] preset_config_ext_fast(): [ADPD] External trigger test: 0\n", - "[2404815][V][PAM.cpp:484] MPF(): [PAM] Phase-4 Completed\n", - "[2404821][V][PAM.cpp:488] MPF(): [PAM] Phase-minus1 Started\n", - "[2420947][V][PAM.cpp:507] MPF(): [PAM] Measurement Completed\n", - "[2420952][V][data_utils.cpp:263] send_esp(): [DATA_UTIL] #0 wakeup call\n", - "[2420979][V][data_utils.cpp:269] send_esp(): [DATA_UTIL] Wake response in 27 ms\n", - "[2420986][V][data_utils.cpp:291] send_esp(): [DATA_UTIL] #0 data call\n", - "checked\n", - "[2421180][V][data_utils.cpp:317] send_esp(): [DATA_UTIL] Spend 174 ms sending 518 data\n", - "[2421211][V][data_utils.cpp:263] send_esp(): [DATA_UTIL] #0 wakeup call\n", - "[2421237][V][data_utils.cpp:269] send_esp(): [DATA_UTIL] Wake response in 26 ms\n", - "[2421244][V][data_utils.cpp:291] send_esp(): [DATA_UTIL] #0 data call\n", - "[2421429][V][data_utils.cpp:317] send_esp(): [DATA_UTIL] Spend 174 ms sending 518 data\n", - "checked\n" - ] - } - ], - "source": [ - "with serial.Serial(\"COM31\", 115200, timeout = 50) as ser:\n", - " ser.write(bytes([160] + [1, 2, 0, 0, 0, 0, 0, 0]))\n", - "\n", - " ser.write(bytes([160] + [2, 110, 40, 20, 0, 0, 0, 0]))\n", - " \n", - " ser.write(bytes([160] + [20, 0, 0, 0, 0, 0, 0, 0]))\n", - "\n", - " wait_for_control(ser, 212, 35)\n", - " ser.write(bytes([202]))\n", - "\n", - " wait_for_control(ser, 212, 5)\n", - " a = ser.read(7)\n", - " wait_for_control(ser, 255, 0.01)\n", - " ser.write(bytes([200]))\n", - " arr1 = np.frombuffer(ser.read((a[2] * 256 + a[3]) * 4), dtype=np.uint32)\n", - " cs = arr1.sum()%256\n", - " cs1 = ser.read(4)[3]\n", - " if (cs == cs1): print('checked')\n", - "\n", - " wait_for_control(ser, 255, 0.01)\n", - " ser.write(bytes([180]))\n", - "\n", - " wait_for_control(ser, 212, 5)\n", - " ser.write(bytes([202]))\n", - " wait_for_control(ser, 212, 5)\n", - " a = ser.read(7)\n", - " ser.write(bytes([200]))\n", - " arr2 = np.frombuffer(ser.read((a[2] * 256 + a[3]) * 4), dtype=np.uint32)\n", - " cs = arr2.sum()%256\n", - " cs1 = ser.read(4)[3]\n", - " wait_for_control(ser, 255, 0.01)\n", - " ser.write(bytes([180]))\n", - " \n", - " if (cs == cs1): print('checked')\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 104, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 104, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiwAAAGdCAYAAAAxCSikAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAABNXklEQVR4nO3deXhU9b0/8Hcms2SbyZ5MVpYQwiYEEAQM4BZUakHQol5twesVm9oKei1XvC3YKyK/q+JSpegt2krFSq1rFY0iKksCJEYIgRAghOyTPZNl9vn+/ggzOGYhk+1MJu/X83yfh5w5c/KZIzpvv9vxASBARERE5MFkUhdAREREdDkMLEREROTxGFiIiIjI4zGwEBERkcdjYCEiIiKPx8BCREREHo+BhYiIiDweAwsRERF5PLnUBQyk2NhYtLS0SF0GERERuUGtVqOysrLHc7wmsMTGxqKiokLqMoiIiKgP4uLiegwtXhNYHD0rcXFx7GUhIiIaJtRqNSoqKi773e01gcWhpaWFgYWIiMjLcNItEREReTwGFiIiIvJ4DCxERETk8RhYiIiIyOMxsBAREZHHY2AhIiIij8fAQkRERB6PgYWIiIg8Xp8CS0ZGBoqLi2EwGJCTk4O0tLQez1cqldi0aRNKSkpgNBpx9uxZ3Hvvvc7XV65cCSFEp6ZSqfpSHhEREXkZt3e6XbFiBV544QX86le/wsGDB/HAAw9gz549mDRpEsrKyrp8z+7duxEdHY377rsPZ8+eRVRUFORy11/d3NyMlJQUl2Mmk8nd8oiIiMhLCXdadna22LZtm8uxkydPis2bN3d5/o033igaGxtFaGhot9dcuXKlaGxsdKuOHze1Wi2EEEKtVvfrOmxsbGxsbGxD13r7/e3WkJBCocDMmTORmZnpcjwzMxPz5s3r8j1LlixBTk4O1q1bh/Lycpw+fRrPPPMM/Pz8XM4LCgpCSUkJysrK8PHHHyM1NbXHWpRKJdRqtUsjIiIi7+TWkFBERATkcjl0Op3LcZ1OB61W2+V7xo4di7S0NBiNRixbtgwRERHYtm0bwsLCcN999wEACgsLsWrVKuTn50Oj0WDNmjU4ePAgpk2bhrNnz3Z53fXr1+OJJ55wp3wiokGniYrEjMWLUHXmHGwWC7TjxkAdEQEAsFksaKqugdLfD21NzfCVy6HwU8HY2ga5UoGgsFBACBz/8hvUl5VL/EmIPIsPOrpaeiUmJgaVlZWYO3cusrOznccff/xx/PznP8fEiRM7vefzzz/H/PnzodVqodfrAQDLli3Du+++i8DAQBiNxs5F+fjgu+++w7fffos1a9Z0WYtSqXSZlOt4PLVGo+HTmolIMnc8+d+Yfest/bqG1WzG0z9ZgaZq3eVPHgK+cjluWL0Kkxam4YtX30BbUxPGTJ8KXXEJinO/h0HfAlVgAEZNnQy7zY7EKyahKOsIyk+ehlyphCYyHI2V1RCi1183NIKo1Wro9frLfn+71cNSV1cHq9XaqTclKiqqU6+LQ1VVFSoqKpxhBQBOnToFmUyG+Pj4LntQhBA4evQokpOTu63FbDbDbDa7Uz4R0aCLn3hp8UDN+QtoqW9ARWERIABNZDi048aiqboGcqUCVrMFVosZ/mo1zAYDWhuaMGvpYsiVSkycPw9Z/3hfwk9yyZJ1a5B21+0AgLu3PAGlv+uQfn15BYLCwqAK8Hceu/k3D+DA2+9i1pLF8Neo8elL27H3//46pHWTd3ErsFgsFuTm5iI9PR0ffPCB83h6ejo+/PDDLt9z8OBB/OxnP0NgYCDa2toAAOPHj4fNZkN5efddnqmpqcjPz3enPCIiSfnIZIgcnQgAeOrm29BQXun2NWovlGLxQ79EytVXeURgiZsw3hlWADjDyqn9hxA5KhERifEIj4/r9D6Zry8W3HOH8+fUG69nYKF+cXtZ89atW7Fz507k5OQgKysLq1evRmJiIrZv3w4A2Lx5M+Li4rBy5UoAwK5du/D73/8eb7zxBjZu3IiIiAg888wzeP31153DQRs2bEB2djbOnDkDjUaDhx56CKmpqXjwwQcH8KMSEQ2usLhYKFQqmA1GNFZW9+kapw8exuKHfolxs2dCJveF3Wob4CrdM/3mdADAscyv8NWOnbhh9Soc/udHOLX/EHwVClx9120wtrShrOAk6krLERwVidaGRvzqjW2ISU7C2SO5GD93NmJTkhEYGoK2xiZJPw8Nb24vQcrIyBDnz58XRqNR5OTkiPnz5ztfe+ONN8S+fftczk9JSRGZmZmira1NlJaWimeffVb4+fk5X9+6dasoKSkRRqNR6HQ68dlnn4k5c+YMyrIoNjY2tsFqk6+dL57LzxIPv/OXPl/DRyYT//PtHvFcfpYYnTpV8s/035+9J57LzxJX3HBNn6/x6Ht/E8/lZ4mpi66T/POweV7r7fe32z0sAPCnP/0Jf/rTn7p87Yc72DqcPn0aixYt6vZ6jzzyCB555JG+lEJE5DGix44BAOiKz/f5GsJuR1H2UUy/OR0p82aj5PvjA1We2xKnTkZYXAyMbW04tT+rz9c5fegwYpKTsOCeO1BxqgiNlVWw26TtOaLhp0+BhYiIOotOGg0AqD7b98ACAEWHjmD6zelYlHEf4iamQBUYgNLjJ6CJioRCpUJNyQUER0Wi5vwFNOtqYDGaEBgWCk1EOExt7bDb7WhtaMSZ7KOIHJUAVWAATO0GBARr0FBRhbC4GNSXVyJ+YgpCtFFoqauHqa0dbU3NUAb4Q6FUorm2DrOW/gQAcPLrA7D2Y+fxg3//JxbccwfGTJ+Kxz/9BwDA0NIKg74FBn0L2pv1aGlogL62Di11DfBTB8LU1g6bxYrIUQmoL6uA1WLGqf3ZXO49gjGwENGI469Rw18dhKbqGthtNshVKij9VGhv1l/+zT2ITup/DwsAnDqQBYvJBIVKhcnXdDyrbdysGf26Zn98/9mX/Xp/Q3klsv7xAa6+8zbYrFb4yuXwVwfBXx0ExMX0+jqL1xjwZPpSGPTcumIkYmAhohEhRBuNxKmTMWvpYky4eg5kvr6wmEzw8fGBXKmEqd2AV1b9EhWnivp0fR8fH0SPGQ0A0J3rX2BpqavHM7fejfFzZ8Nus8JfrUbClIlorNJB4aeCKsAfbY3NGDV1MmxWK+QqJdoamtDa0IjQuBjYrTYER0ciJjkJbU3NUKhUkMl90VBeidBYLawmM/w1atSVlnfcA5kMVpMZvgo5zO0GWC0WRI0ZhcDQEBQdOoLCA9mXL/oy3t/8HD59aTtMbe0ICNbAX6NGgEYNf40GgSEaqMPDoY4IhyYyHBajCcoAf6gCAtBa34CAkGBccf1CqAL8MXbGNBR8faDf9dDww8BCRF4tIjEeP9v4GMbNnuly3GqxQPGDzSdVAf5Ytv4/8af7fg2bxdLltfyCAqFNGgu73YYJaXPRXFOL5ppaqMNCkXjFZCj9/WAxmVDfh+XMP1ZfXtHvZc2BIcFoa2qGXKmEXKWEsaXV+ZrM17fHeSQ+MtnFFU+GftXgIIRw/v62xia3Vwvd9vt1mLdiGZJmzWBgGaEYWIjIK4XGaDF7+U9xw+pVkMk6HptWX16JvD1f4OiHn6ChvBKRoxIgk/tCOy4Jd/zP4xgzfSr+97tv0drQiLqycjTrahEYEoyIxHhYLRaExcZA5uvb4++tL6uAsNuH4iNeVltTM4COnXOtP9po83KTXoXdPmBhZSAU5+Rh3oplGHvldKlLIYkwsBCRV5ErlfjNztcQP+nSjrMnvz2ID//3RdRdKHM5V1dcAgCoKjqHtsZG3PP//geBoSEICgvteK5PF+w2G2S+vmjS1aD2fCkCQjRoqWuAjw+QPGcW8r/6ZtA+20h2LicPQMdGdgmTJ6LqbHG/JgLT8OPWs4Q8WW+fRUBE3m3O7Uvxs42PwWaxourMOXz7t3eQ+/GeXr3XV6FAYGgI1GGhCE+IgyYyAjJfX5SfLITdZkdDRSVa6hugHTcWunPnO/VSyJXKTj0ZNHAyXn/FOfnYZrGipaEBPvC5+GiAUOhr61B4MBttDU2oLS1D3ITxKDyYDZlMhramZhhaWhCZmICa8xf4XCMP0tvvbwYWIvIaSn8//Oc/dyIiIR4fbHke+9/aLXVJNICuWv5TrPjD4316r91mg9lghF9QIPb88VV8+dpfBrY46rNBefghEZEnW/LbNYhIiEezrhaH3/tY6nJogOV+kokxM6ahrKAQBfv2IzwhDolTJiIkRouWunqk/dvP0FLfAKW/HyIS4mG32zuWrSsUkPn6wi8oEACw4J478PVfdrE3bJhhDwsReYUxM6bh13/teKbZn+77Nc4eyZW4IpKKr1yOiFEJ0J07D5m8Y5J0REI8lP7+WL39eQSGhgAADr3zHtqamtHW2IS8PV+gtaGxy+vJVSqMumISDC2tqD5XDLvVBr+gQFiMJtis1qH6WF6LQ0JENGL4yuV4ePdfEJOchKx3P8C7f/h/UpdEHmrhL+7Ckt8+1Om42WDEyW8OoLmmFiXf58NmtSIwOBhX3roY8RNToAoIAAC0N+tx5nAOJi24GkIIXDh+ArUlpTi1PwsWoxHGtnaY2tpgbG1DS109AHSaLxMYGgKDvoWPJ7iIgYWIRoz5d6/ArY89jNaGRmz56Z0w6Pu3Yy15N6W/H5KunIEJaXOgUKkw7qqZCI+P6/E9LfUN8FXIEaDR9Pr32CxW+CrkuHDsBCqLzqKisAijpk7BrKWLkfuvz7Br/R/6+1G8AgMLEY0Ya97egcQpk/De5udw8O13pS6HhpnAkGBcs+rfYDIYEZOchLgJ49HerIfVYsbZw7k4/sU+5xL4aYuuQ9zE8ai7UIbIUYkwtrUhclQixl01E8aWVij9/eEXFAg/dZBz/5/u/HZ6GuxW9rJw0i0RjQjB0ZFInDIJdrsdxzL3Sl0ODUNtTc345IU/9erc7z/7slfPVlL4qTBq6hSY2g2IHZ+ElKvnICIhHv4aNcIuPj8pfmIKSvNP9qv2kYSBhYiGtdQbbwAAXDh2Aq31XU+aJBpqFqPJOfG77MRJl1Vr9764BVOuW4ikWTMYWNzAwEJEHkMVGABTWzv8ggLhr1EjKDQUPr4yRCYmwGa1QqFSIjAkBIFhIQgMCYHdbsPka+YDAHI++lTi6ol65/x3xzHluoWInzRB6lKGFQYWIhow/hrNxQcHCtgsVvhr1IgcnYj4iSnw16gRGBqCwJBgxE9Mga9CgfqyCshVSqgC/KH09+94Zo/ZDLlS6dbvbayqxtEPGVhoeGjS1QAA1OFhElcyvDCwEFEnQeGhGHflxS3QbTZox42Fyt8fCn8/KP38oFAp0dLQCG3SGMRPngC71Qab1Qp1RPhlJxr+kGMs/4ccYcVsMKK9uRkyuRwttfUwtbfDZDA4n/Tb1tQMX19fBIaG4LtPM7t9wjKRp2mpbwCAbp9XRV1jYCHyUnKVCqEx0ZD5+sJiMsFiMCIoPAx+gQEIi4tFWHwsAEATGYGxM6ZB4aeC3WqD3W5H9NjRA1aHzWLFmcM5aNbVoLm2Dqa2dlSeLoLNYkWINhpmgwGmdgMAgbrScmgiIlB9rhjG1rYBq4HIkzg2qGNgcQ8DC9EwFBCsQdzEFBhbWuEXFIigsFBEjkpAeGI8EqdMQlB4qFv7RXSl/ORpmNrb4RcYiMqiM2htaILFaITZaISw2RE1ZhR0xSUoyj4Ku9UKhZ8K+tp6wMcHFqMJvgpfGFva4CPr+Lm3Giqq+lU3kadrvdjDEhgSDJncl0ube4mBhchD+CoUiBozCkGhIVAFBkAVEABNZDhiU5Kh8PODvzoINqsVQaGh0I4bC1/F5f/1tdtssNtsEHYBhZ8KrQ2NMLW3o6GiCg0VVbDbbTC2tKHk++PQ19ZBoVJBrlSi8vQZZ7c1EQ2s9mY97DYbZL6+CArteMo0XR4DC9Eg85HJIOx2RCeNwagrJqP2Qil8FQrMu2M51OFhEEKguaYWE9LmuNUrUldWjoBgDSAA3bnzqK+oRGt9I2ovlMLUbkBxbh4M+laYDQYAHdvX87knRNITQqC1sQmaiHCow8MYWHqJgYVogCXNmgFt0hiMTr0CYfGxSLxiMsztBueTYnvSrtejWVcLU1s7TO3taG/Wo+rMOZja2mExGgEfH7Q2NKKq6KzbQycMK0Seo7W+AZqIcM5jcQMDC1EfBIWHIjgyEn5BgUi5eg6CwkIRFheDEG00IkcldDrfLygQNqsVFYVFiEsZD5vVitxPPkPRoSMIDAmBJjIcJcdOoOjQ4U4PSiMi78OJt+5jYCHqJU1kBMZdNRNpd96OUdOmdHuexWhC8Xffo7LwDGovlOJC/klYzWa0NTbDoNdD4aeCsAtYzeYhrJ6IPIljjhj3Yuk9BhaiH5H5+iJxyiRoxych6crpSJl3FWS+vvBXBznPsdvtaK1vgFypxIXjJ1B6vAB15RVoa2hC6YlTPT4t2J0VM0TkndjD4j4GFiJ0hJTYlGTMvOUmzFq6GP4adZfnlZ44iZLv87Hv9b9xohwR9ZkzsLCHpdcYWGhEu+q2JZh6w7UYPf0K+AVemhTb3qxHybF8VJ8txslvDqKtsQn62jpuZkZEA8K52204e1h6i4GFRhQfmQwT0uZixuJ0jLvqSmgiwp2vGfQtOJfzHQ698z6Kso9C2O0SVkpE3szxZHEOCfUeAwuNCDJfX0y+Jg03/Xo1tOPGOo9bzWZkbn8dJ785iOqzxQwpRDQkOOnWfQws5JXkSiUmpM3BqGlTkHrTDQiLvfSQvfZmPXI+2oOT3xxA1ZlzzrFkIqKhwkm37mNgIa/i4+ODpFkzsPS/1iJ2/DiX11obGpH17gf4+i+7YGxplahCIqJLgUWuUMBPHcT/JvUCAwt5jbgJ43HX5g2ISU4C0PEcneNf7MOxzK9QcuwEWusbYLfxIWNEJD2r2QxDSyv81UFQh4cxsPQCAwsNa74KBUKio7B4zS8xNf1ayHx9YWhpRXFOHj575f9QefqM1CUSEXWptaER/uogBIWForakVOpyPB4DCw1LMePH4Y4n/xsJkya4HD/x1Td4Z8NmtDd3v3EbEZEnaG1oROSoBE687SUGFhpW5CoVpt90PW555NfOyWp2ux2nDx3GJ89vQ1XRWYkrJCLqnZa6egCAJjL8MmcSwMBCw0jEqARk/PmPCNFGAwBqL5Thzw/+J9oam2DQt0hcHRGRe5qqawAAIdHRElcyPDCw0LAw52e3YvFDv0RgSDCadDX47pPPcWDXP9Csq5W6NCKiPmmsqgYAhMZqJa5keGBgIY/mI5NhxR/WY/attwAAKovO4tX7H+LeKUQ07DkCS0gMe1h6g4GFPFZQeCjufPJ3mDh/HmxWK/71/Cs4sOsfsFu5NJmIhr/Gyos9LFr2sPQGAwt5JE1UJB547UVok8bAarFg1/o/4Njne6Uui4howDRV6wAA6shw+MrlsFmtElfk2WRSF0D0Y8lzZuGR3X+BNmkMmnQ12Hr7LxhWiMjrtDY0wmI0QSaTIVgbJXU5Hq9PgSUjIwPFxcUwGAzIyclBWlpaj+crlUps2rQJJSUlMBqNOHv2LO69916Xc5YvX46CggIYjUYUFBTg1ltv7UtpNMyNTp2K+7dthTo8DBWFRXhlVQZ0xSVSl0VENCicE29jOCx0OW4HlhUrVuCFF17AU089henTp2P//v3Ys2cPEhISun3P7t27cf311+O+++5DSkoK7rrrLhQWFjpfnzNnDt555x3s3LkT06ZNw86dO7F7927Mnj27b5+KhqXQGC1+/uyT8FXIcfzLr/HSPavRUF4pdVlERIOGgcU9wp2WnZ0ttm3b5nLs5MmTYvPmzV2ef+ONN4rGxkYRGhra7TX//ve/i08//dTl2J49e8SuXbt6XZdarRZCCKFWq936PGye0dQR4WL9J/8Qz+Vnid9+sEuoAgIkr4mNjY1tsNuKJ9aL5/KzxA0P3Ct5LVK13n5/u9XDolAoMHPmTGRmZrocz8zMxLx587p8z5IlS5CTk4N169ahvLwcp0+fxjPPPAM/Pz/nOXPnzu10zc8//7zbawIdw0xqtdql0fCk8FPhP155DhGJ8agvr8RrD6yBqb1d6rKIiAZdw8UeljD2sFyWW6uEIiIiIJfLodPpXI7rdDpou1mWNXbsWKSlpcFoNGLZsmWIiIjAtm3bEBYWhvvuuw8AoNVq3bomAKxfvx5PPPGEO+WTh/rZxscQPykFLfUN2H7/b7gZHBGNGE1VHd993Ivl8vo06VYI4fKzj49Pp2POXyCTQQiBu+++G0ePHsWePXvwyCOPYNWqVS69LO5cEwCefvppaDQaZ4uLi+vLRyGJzb97BWbechNsVit2Pvo7zlkhohGlsbIKAOew9IZbPSx1dXWwWq2dej6ioqI69ZA4VFVVoaKiAnr9pafnnjp1CjKZDPHx8Th79iyqq6vduiYAmM1mmM1md8onD5N05XT89NHfAAA+fvaPOJeTJ3FFRERDq/HiXiwMLJfnVg+LxWJBbm4u0tPTXY6np6fj0KFDXb7n4MGDiI2NRWBgoPPY+PHjYbPZUF5eDgDIysrqdM1FixZ1e00a/kK00fj5s5vgK5cj5+M92P/WbqlLIiIacs3VNbDb7VD4qZxPoKfuuTejecUKYTKZxL333ismTJggtm7dKlpaWkRiYqIAIDZv3iz++te/Os8PDAwUpaWlYvfu3WLixIli/vz54vTp0+K1115znjN37lxhsVjEunXrREpKili3bp0wm81i9uzZAz7LmE365hcUKNb+/XXxXH6WePidvwiFn0rymtjY2Nikar//4gPxXH6WSJw6WfJapGhufH+7f/GMjAxx/vx5YTQaRU5Ojpg/f77ztTfeeEPs27fP5fyUlBSRmZkp2traRGlpqXj22WeFn5+fyzm33XabOHXqlDCZTOLkyZNi2bJlg/WB2SRsPj4+4jd/e008l58l/ufbPSI0Vit5TWxsbGxStl/++Y/iufwsMfOWmySvRYo2qIHFExsDy/BoY6+cLp7LzxJPZX8p4iaMl7weNjY2Nqnb7Rv+SzyXnyVufPB+yWuRog3KPixE/TX71lsAAN/v+RIVhUUSV0NEJL3aklIAQOSo7neMJz78kIbQ9MWLMGvpYgDA0Q8+kbgaIiLPUFdaBgCIYGDpEQMLDQmZ3Bc//c9fAwC+efNtlBzLl7giIiLPUHuhI7BEJjKw9ISBhYbElOsWIjgqEvq6enzy/DapyyEi8hj1ZRWwms3wCwpEWFyM1OV4LAYWGnTqiHDcum4tAODwPz+CzWqVtiAiIg9is1pRWXQWAJAwZZLE1XguBhYadIsy7kNwdCSqz53Hvjf+JnU5REQep+zEKQBAIgNLtxhYaFCpAgIw4yeLAADvPfUsTG18CjMR0Y+VFXQEloQpEyWuxHMxsNCgmr3sFvgFBkJXXIJzR7+TuhwiIo/k6GGJn5QCHxm/mrvCu0KDRq5U4tp77wEAfPu3dySuhojIc+mKS2Bqb4cqIABRY0ZJXY5HYmChQTNt0XUIjo5Ek66G+64QEfVA2O0oP3kaAJDIYaEuMbDQoJlz+1IAwKF33oPNYpG4GiIiz+YYFuJKoa4xsNCgiB47GmNnpsJmtbJ3hYioF8pOnATAibfdYWChQXHVxd6Vk98chL62TuJqiIg8X+nFlUKxKcnwVSgkrsbzMLDQgJMrlZi1pOOZQdnvfiBtMUREw0RDeSXaGpsgVygQO36c1OV4HAYWGnBT069BQLAGDZVVOH3oiNTlEBENG2UFhQA4LNQVBhYacHNuvxUAcPi9jyHsdmmLISIaRhwbyCVeMVniSjwPAwsNqKgxo5B05XTYbTYcef9fUpdDRDSsFOd+DwAYN3uGtIV4IAYWGlCOpcwnvz0IfU2txNUQEQ0v5/OOwWo2IzRGi4jEeKnL8SgMLDSgpi26DgBw5L2PJa6EiGj4sRhNKDl2AgAwfu5siavxLAwsNGBiU5IRoo2Gqd3AybZERH106ttDADqexUaXMLDQgJl0TRoA4Mzho7CazRJXQ0Q0PB394F+wmExImDwRiVM5+daBgYUGzJU/vRkAUPDVfokrISIavtqamnHs868AAKk33SBxNZ6DgYUGRNKsGYgclQBjWxu+/3yv1OUQEQ1r+Xu/BgBMuXa+tIV4EAYWGhAzf3IjACBvzxcwGwwSV0NENLwVZR2BxWhCeHwcopPGSF2OR2BgoX7zkcmc81cc3ZhERNR3ZoMRVWfPAQDC4mIlrsYzMLBQv41OvQLq8DC0N+txLuc7qcshIvIKxpZWAIC/OlDiSjwDAwv126QF8wAAhQeyYLfaJK6GiMg7GFvbAAB+QUESV+IZGFio31LmzQEAnDqQJXElRETeg4HFFQML9UtQeCjiJo4HABRxszgiogFjaOWQ0A8xsFC/pMy9CgBQfvI0WhsaJa6GiMh7OOawsIelAwML9UvK1R2B5fShwxJXQkTkXZxDQmoGFoCBhfrBx8cHKfM6AkvhwWyJqyEi8i6Xelg4JAQwsFA/xE0cj6CwUBjb2nDh+3ypyyEi8irOOSwcEgLAwEL94FgddPZILmxWq8TVEBF5Fw4JuWJgoT5zzl85yPkrREQDjUNCrhhYqE9UgQEYPe0KAJy/QkQ0GBxDQlwl1IGBhfok+aor4auQo/ZCGRrKK6Uuh4jI6xgvBhZVYAB8fHwkrkZ6DCzUJ47VQVzOTEQ0OIwtHXNYZDIZVIEBElcjPQYW6pPkObMAcP4KEdFgsZrNsJrNADgsBDCwUB8EhYUiclQCAKD4u++lLYaIyIsZHBNvuVKIgYXcN2raFABA1ZlzzlnsREQ08Ext7QAAf64U6ltgycjIQHFxMQwGA3JycpCWltbtuQsXLoQQolNLSUlxnrNy5couz1GpVH0pjwbZ6NSO1UEXjp2QuBIiIu/GlUKXyN19w4oVK/DCCy/gV7/6FQ4ePIgHHngAe/bswaRJk1BWVtbt+8aPHw+9Xu/8uba21uX15uZmlxADACaTyd3yaAg4elhKjnF3WyKiwWTkkJCT24HlkUcewY4dO7Bjxw4AwMMPP4wbb7wRGRkZePzxx7t9X01NDZqbm7t9XQgBnU7nbjk0xGRyXyROngSAPSxERIPNudttIIeE3BoSUigUmDlzJjIzM12OZ2ZmYt68eT2+Ny8vD5WVlfjyyy9xzTXXdHo9KCgIJSUlKCsrw8cff4zU1NQer6dUKqFWq10aDb64lPFQ+KnQ3qxHbUmp1OUQEXk1x14sfmoGFrcCS0REBORyeaeeEJ1OB61W2+V7qqqqcP/99+O2227D8uXLcfr0aezduxfz5893nlNYWIhVq1ZhyZIluOuuu2A0GnHw4EGMGzeu21rWr18PvV7vbBUVFe58FOqjHw4HCSEkroaIyLs5VwlxDov7Q0IAOn1R+fj4dPvlVVRUhKKiIufP2dnZSEhIwKOPPor9+/cDAA4fPozDhy/t53Hw4EF89913+M1vfoM1a9Z0ed2nn34aW7dudf6sVqsZWoZA4hUXh4OOF0hcCRGR93MMCflzDot7PSx1dXWwWq2delOioqLcmn+SnZ2N5OTkbl8XQuDo0aM9nmM2m9HS0uLSaPDFjO/o9ao4VXSZM4mIqL/4AMRL3AosFosFubm5SE9Pdzmenp6OQ4cO9fo606dPR1VVVY/npKamXvYcGlq+cjmixowCAFSfOSdxNURE3s/IZc1Obg8Jbd26FTt37kROTg6ysrKwevVqJCYmYvv27QCAzZs3Iy4uDitXrgQArFmzBiUlJSgoKIBSqcQ999yD22+/HcuXL3dec8OGDcjOzsaZM2eg0Wjw0EMPITU1FQ8++OAAfUwaCJFjRkGuUMDQ0orGqmqpyyEi8noGxyohTrp1P7Ds3r0b4eHh2LBhA2JiYnDixAksXrwYpaUdK0ZiYmKQmJjoPF+pVOLZZ59FXFwcDAYDCgoKsHjxYuzZs8d5TkhICF577TVotVo0NzcjLy8PCxYswNGjRwfgI9JAiUlOAsDeFSKioeIYEvJnDwsAQHhDU6vVQggh1Gq15LV4a/vJ2gzxXH6WWP7fj0peCxsbG9tIaIlXTBLP5WeJ//7sPclrGazW2+9vPkuIes0x4baqiD0sRERDwcghIScGFuo1x5BQFYeEiIiGhHMfFu50y8BCveOvUSNEGw0AqD7LwEJENBTM7QYAgMzXF/IR/kBgBhbqFUfvSkNFlbOLkoiIBpflBw8BVvoxsBBdVnTSGABA9dliiSshIho57DYbrGYzAEDp5ydxNdJiYKFeiRzdsVS95vwFiSshIhpZzEYjAEDhz8BCdFlRFwNL7QU+oZmIaChZDB3DQkoGFqLLc/awlDCwEBENJbOhY+Ith4SILsNXoUBYbAwAoJZDQkREQ8o5JMTAQtSziIQ4yHx9YWxtQ0t9g9TlEBGNKBYjh4QABhbqhUvDQexdISIaauxh6cDAQpflCCx1F8okroSIaOSxGDoCC/dhIbqMyFGccEtEJBX2sHRgYKHLci5pZmAhIhpynMPSgYGFLitiVAIABhYiIimwh6UDAwv1yF+jhjo8DABQyzksRERD7tIcFgYWom5FXuxdadLVODcvIiKioXOph4WTbom6FTl6FAAOBxERScVyMbBwDgtRDyJHc/4KEZGUzBcn3XIOC1EPohw9LJy/QkQkCc5h6cDAQj1yzGHhLrdERNLgHJYODCzUo4hEx5AQe1iIiKTgnMPCHhairgWFh0Lp7we73Y7GqiqpyyEiGpEcc1iUAf4SVyItBhbqVmhMDABAX1sHu9UmcTVERCOTY0sJDgkRdSM0JhoA0FhZLXElREQjl5mTbgEwsFAPQmO0AIDGKgYWIiKpWLg1PwAGFupByMUeliYGFiIiyTjnsDCwEHUtLLajh6WBQ0JERJJx9LD4KuSQyX0lrkY6DCzULcek26YqncSVEBGNXI45LMDI7mVhYKFuOYaEOIeFiEg6NosFdlvHSs2RPI+FgYW6pPT3R2BIMAAGFiIiqZm5eRwDC3Ut9OL8lXa9Hqa2domrISIa2SyOByCO4Cc2M7BQl7gHCxGR57i0F8vI3TyOgYW6xAm3RESeg3uxMLBQNzjhlojIc3AOCwMLdcOxB0sje1iIiCTHOSwMLNSNYG0UAO5yS0TkCdjDwsBC3QiOjAQANNfWSVwJERFZDI45LJx0S+RCHREOANDXMLAQEUnNsUpI5e8vcSXSYWChTvyCAqEK6PiXQl/HwEJEJDXHkBDnsBD9gCYyAgBg0Lc4J3oREZF0LEbuw9KnwJKRkYHi4mIYDAbk5OQgLS2t23MXLlwIIUSnlpKS4nLe8uXLUVBQAKPRiIKCAtx66619KY0GgCOwcP4KEZFnMHMfFvcDy4oVK/DCCy/gqaeewvTp07F//37s2bMHCQkJPb5v/Pjx0Gq1znbmzBnna3PmzME777yDnTt3Ytq0adi5cyd2796N2bNnu/+JqN80UR2BpaW2XuJKiIgIACyGjt5urhJywyOPPIIdO3Zgx44dKCwsxMMPP4yysjJkZGT0+L6amhrodDpns9vtztfWrl2LL774Alu2bMHp06exZcsW7N27F2vXrnX7A1H/BTt7WGolroSIiADOYQHcDCwKhQIzZ85EZmamy/HMzEzMmzevx/fm5eWhsrISX375Ja655hqX1+bOndvpmp9//vllr0mDQx3JHhYiIk/COSyA3J2TIyIiIJfLodO57n6q0+mg1Wq7fE9VVRXuv/9+5ObmQqVS4ec//zn27t2La665Bvv37wcAaLVat64JAEqlEirVpX9warXanY9CPVCHhQIAWuobJK6EiIgAzmEB3AwsDkIIl599fHw6HXMoKipCUVGR8+fs7GwkJCTg0UcfdQYWd68JAOvXr8cTTzzRh+rpcvyDNQCA9uZmiSshIiLg0sZxSu7D0jt1dXWwWq2dej6ioqI69ZD0JDs7G8nJyc6fq6ur3b7m008/DY1G42xxcXG9/v3UswCNI7DoJa6EiIgAwNTeDgBQBQZIXIl03AosFosFubm5SE9Pdzmenp6OQ4cO9fo606dPR1VVlfPnrKysTtdctGhRj9c0m81oaWlxaTQwAkOCATCwEBF5CmPbxcASMHIDi9tDQlu3bsXOnTuRk5ODrKwsrF69GomJidi+fTsAYPPmzYiLi8PKlSsBAGvWrEFJSQkKCgqgVCpxzz334Pbbb8fy5cud13zxxRfx7bffYt26dfjwww+xdOlS3HDDDT3u70KDJ+DikFBbE4eEiIg8gbndAADOXchHIrcDy+7duxEeHo4NGzYgJiYGJ06cwOLFi1FaWgoAiImJQWJiovN8pVKJZ599FnFxcTAYDCgoKMDixYuxZ88e5zlZWVm48847sWnTJjz55JM4d+4c7rjjDhw5cmQAPiK5w0cmg586CADQrmcPCxGRJzC1cUjIB0D3M1uHEbVaDb1eD41Gw+GhfggI1uDJA58DANZNnw+b1SpxRURE5K/RYNPBjv82/3Z6GuxWm8QVDZzefn/zWULkwjEcZGxrY1ghIvIQpvY2559VAYESViIdBhZy4Qgs7U0cDiIi8hR2qw0WU8f2/H4jdFiIgYVcBHCFEBGRR3JMvFWO0Im3DCzkwtnDwgm3REQexTjCJ94ysJAL56ZxXNJMRORRHJvHcUiICIC/puOZTIaWVokrISKiHzKN8M3jGFjIheNJoGaDQeJKiIjoh8zO7fm5SojI+SRQx5NBiYjIM1zanp+TbomcTwK1GEwSV0JERD800h+AyMBCLpxDQuxhISLyKJzDQvQDCv+LQ0Kcw0JE5FFMjgcgsoeFCFBenMNiYQ8LEZFHGekPQGRgIRfOSbecw0JE5FEM+o4HAwZc3H5ipGFgIRdKf/awEBF5orbmjg09A4KDJa5EGgws5ELpxzksRESeyHDxGW+OR6iMNAws5MI56ZY9LEREHqXt4iNTHA+pHWkYWMjFpUm3nMNCRORJHM94C+SQENGlOSxmA3tYiIg8iWMOi69CPiL3YmFgISeZry/kSiUAzmEhIvI0FqPJ2fsdEDLy5rEwsJCTQqVy/tnMISEiIo/TfnHibeAInMfCwEJOCv9LgcVqYmAhIvI0I3lpMwMLOTkm3Dq2fyYiIs/imHg7Epc2M7AQAMBPHYT/+vgdANw0jojIUzmWNnNIiEasmbfcBLlCAQCwWawSV0NERF3hHBYa8YKjIi/9OTqyhzOJiEgqLXX1AAB1ZITElQw9BhYCAMSmjJO6BCIiuowmXQ0AICQ6SuJKhh4DCwEA4iaMl7oEIiK6jOaaWgCuveIjBQMLYfatt0Dzg+7FA7v+IWE1RETUneaLPSwjceheLnUBJK2gsFAs/92jAICvdryJr//6tnPZHBEReZZmXUcPS1BYKORKJaxms8QVDR32sIxwc25fCoVKhdL8k/jkhT+hrbEJQgipyyIioi60N+ud2/NrokbWxFsGlhEsflIKrv33ewAA+3ftlrgaIiLqjZE6j4WBZYTykclw11Mb4BcYiDPZOfj+sy+lLomIiHrBuVJIGy1xJUOLgWWEWnDPHdCOG4t2vR5//c/HYbfapC6JiIh6oaG8EgAQkRgvcSVDi5NuR6Dr7vs5frL2VwCAr/78Jgz6FokrIiKi3qo5XwIAiBozStpChhh7WEaYxCsm4eaHfgkA+PyV/8PXf9klcUVEROSOmpJSAEDk6ESJKxlaDCwjzNyfLYNMJkPep5nI3P46VwQREQ0zNecvAACiRo+Cj4+PxNUMHQaWEUThp8LURdcCAA7+/Z8SV0NERH1RX14Bm8UKVYD/iFopxMAygky/eRH8AgNRV1aO83nHpS6HiIj6wG61oa6sHAAQPW6sxNUMHQYWL5cy7yrcuel38FMHIe3fbgcAZO3+QNqiiIioXyoLiwAA8RNTJK5k6HCVkBeLmzgeq199AQDgq1AgbsJ4WIwmHH7vY2kLIyKifik/eRrTFy9C/KSRE1jYw+KlfBUK/Nvmjc6fZyxeBAA4lvkVDHq9VGUREdEAKDtZCACInzRB4kqGDgOLl/rJ2gxouxjbPPw+e1eIiIa7ilOnAQBhcTEIDAmWuJqh0afAkpGRgeLiYhgMBuTk5CAtLa1X75s3bx4sFgvy8vJcjq9cuRJCiE5NpVL1pbwRL/mqK7HwF3cBAD7Y8rzzeO2FMhTn5HX3NiIiGiaMrW3O5c0JV0ySuJqh4XZgWbFiBV544QU89dRTmD59Ovbv3489e/YgISGhx/dpNBq8+eab2Lt3b5evNzc3Q6vVujSTyeRueQQg/Zf/DqBj6fL+t3aj/eIQ0JH3/yVlWURENIAuHD8BABg97QqJKxkabgeWRx55BDt27MCOHTtQWFiIhx9+GGVlZcjIyOjxfa+++ip27dqFrKysLl8XQkCn07k0ct+U6xYg6crpsJrN+PL//goA+Oavb+N83nEc/ueHEldHREQDpeRYR2AZNW2KxJUMDbcCi0KhwMyZM5GZmelyPDMzE/Pmzev2fatWrUJSUhL+8Ic/dHtOUFAQSkpKUFZWho8//hipqak91qJUKqFWq13aSOevUeNnGx8DAHy78+/QX3wE+Zev/QUv/+IBtDU1S1keERENoAvH8gF0PHLFR+b9U1Ld+oQRERGQy+Wdej90Oh20Wm2X7xk3bhy2bNmCu+++GzZb108ELiwsxKpVq7BkyRLcddddMBqNOHjwIMaNG9dtLevXr4der3e2iooKdz6KV0p/4F4EhYWi+mwxPnvlz1KXQ0REg6j67HkY29rgFxiI6KQxUpcz6PoUyX78/BkfH58un0kjk8mwa9cubNy4EWfOnOn2eocPH8Zbb72F48eP48CBA1ixYgWKiorwm9/8ptv3PP3009BoNM4WFxfXl4/iNQJDgjHvjuUAgI+eeQk2i0XiioiIaDAJux1l+acAAKNHwLCQW4Glrq4OVqu1U29KVFRUl3NO1Go1Zs2ahZdffhkWiwUWiwUbNmxAamoqLBYLrr322i5/jxACR48eRXJycre1mM1mtLS0uLSRbPbyn0KhUqGs4BROHzosdTlERDQESo53DAuNhHksbgUWi8WC3NxcpKenuxxPT0/HoUOHOp2v1+sxZcoUpKamOtv27dtRWFiI1NRUHD7c/Rdramoqqqqq3ClvxPJVKJB2V8e2+3yoIRHRyHHh+4sTb6d6f2Bxe2v+rVu3YufOncjJyUFWVhZWr16NxMREbN++HQCwefNmxMXFOfdWKSgocHl/TU0NjEajy/ENGzYgOzsbZ86cgUajwUMPPYTU1FQ8+OCD/fx4I8Oc25YgRBuNJl0N8j79QupyiIhoiDiWNkePHQ1/jcardzJ3O7Ds3r0b4eHh2LBhA2JiYnDixAksXrwYpaWlAICYmBgkJia6dc2QkBC89tpr0Gq1aG5uRl5eHhYsWICjR4+6W96IdNVtSwAAX+3YCavZLHE1REQ0VNqb9agtKUXk6ESMmjoJhQeypS5p0PgA6DxbdhhSq9XQ6/XQaDQjaj5LeHwcHt/zLmxWKzYu/IlXp2siIurszk2/x6yli5G5/XV8/sr/SV2O23r7/e39C7e9kK9cjhsfvB9TrluAqenXAADO5eQxrBARjUAXHBvIefkW/W4PCZH0bn7ol7j23rsBAA2VHROTv//sSylLIiIiiVQUdjwIUZucJHElg4s9LMNMUHioM6wAQFhsDFrqG5D7r88lrIqIiKSiKy4BAARHRcJf4727vjOwDDNXLV/S6djRD/4FKx8USUQ0Ipna2tFYVQ0A0HrxjrcMLMOIzNcX81Ys63S8KIurqYiIRrLqc+cBANHjxkpcyeBhYBlGJi28GiHaaLQ2NOJY5lfO4+e/z5ewKiIikprubEdgYQ8LeYT599wBADj83sfI2v0+AODC8QIOBxERjXC1Fzr2QgtP8N7n6nGV0DAxccHVGDdrBqxmMw698x6aqnV48e7/QG1JmdSlERGRxBoqKgEAYXGxElcyeBhYhokbVq8EAOz/2240VXc8aLL0eEFPbyEiohGivrxji4uw2BiJKxk8HBIaBrTjxmL0tCtgs1jx9Zu7pC6HiIg8TFNVNex2O5T+flCHh0ldzqBgYBkGHEuZC77ej9b6RomrISIiT2OzWtGsqwEAhMV757AQA4uHkyuVmPnTmwAAh9/7SOJqiIjIUzVUXBwW8tJ5LAwsHm7KdQsQGBKMpmodTh86InU5RETkoS5NvPXOeSwMLB7uqts6hoOOvP8vCLtd4mqIiMhTNZR3BJZw9rDQUAuLj8X4ObNgt9tx5P1/SV0OERF5sPqLQ0Kh7GGhoZZ64/UAgLOHc5zPiSAiIuoKh4RIMldcfw0AuGzDT0RE1BVHYAnVauEj876vd+/7RF4iJDoKiVdMgt1ux4l930pdDhEReTh9TR2sZjN8FXKEREdJXc6AY2DxUClXXwUAKM0v4N4rRER0WUIINFZ2TB/wxmEhBhYPNX7ubABAEZcyExFRLznnsXjh5nEMLB7Ix8cHyVddCQDce4WIiHqt4eICjdAYrcSVDDwGFg8UNXY0AkNDYGpvR+kJPuCQiIh6xzEkFBrLwEJDYHTqFQCA0uMnYbfaJK6GiIiGi0b2sNBQcgSWkuP5EldCRETDCXtYaEiNSZ0KACj5noGFiIh6zxFYQrTR8PHxkbiagcXA4mFCoqMQOToRdpsNF46dkLocIiIaRvS1dbBZrZArFFBHRkhdzoBiYPEwl/ZfOQmDvkXiaoiIaDix22xo1tUCAMK8bB4LA4uHSbl6DgDg9MFsiSshIqLhyDnx1svmsTCweJCIxHhMuW4BAKDg6wMSV0NERMORt068ZWDxINfeew985XKc/OYgKgqLpC6HiIiGIW9d2szA4kGS58wCABzY9Q+JKyEiouGqsbIKAHtYaJBooiIRHh8Lu83G5cxERNRn7GGhQTVmesfeK1VF52Bqb5e4GiIiGq4aOIeFBtPYmakAgPPfH5e2ECIiGtaaqnQAAFVAAAKCNRJXM3AYWDzEhIvLmYuy+HRmIiLqO6vZDH1dPQDv6mVhYPEAEYnxiEiMh9ViwdnDuVKXQ0REw5xzaXNMjMSVDBwGFg8wfu5sAMD53GOcv0JERP3mjZvHMbB4gIQpEwEA5/OOSVwJERF5A2/cPI6BxQPET5oAACg/WShxJURE5A28cWkzA4vE5CoVoseOBgCUnTwtbTFEROQV2MNCAy42ZRx85XLo6+qhr6mVuhwiIvICjVUdu9160xOb+xRYMjIyUFxcDIPBgJycHKSlpfXqffPmzYPFYkFeXl6n15YvX46CggIYjUYUFBTg1ltv7Utpw04Ch4OIiGiAOXpYAkNDoPT3k7iageF2YFmxYgVeeOEFPPXUU5g+fTr279+PPXv2ICEhocf3aTQavPnmm9i7d2+n1+bMmYN33nkHO3fuxLRp07Bz507s3r0bs2fPdre8YefS/BUOBxER0cAwtrbBoG8B4D3zWNwOLI888gh27NiBHTt2oLCwEA8//DDKysqQkZHR4/teffVV7Nq1C1lZWZ1eW7t2Lb744gts2bIFp0+fxpYtW7B3716sXbvW3fKGnfhJKQDYw0JERAPL25Y2uxVYFAoFZs6ciczMTJfjmZmZmDdvXrfvW7VqFZKSkvCHP/yhy9fnzp3b6Zqff/55j9dUKpVQq9UubbiRq1SIThoDgIGFiIgGlrdtHudWYImIiIBcLodOp3M5rtPpoNV2neDGjRuHLVu24O6774bNZuvyHK1W69Y1AWD9+vXQ6/XOVlFR4c5H8Qix45PgK5ejpb4BzTpOuCUiooEzontYHIQQLj/7+Ph0OgYAMpkMu3btwsaNG3HmzJkBuabD008/DY1G42xxcXFufALPwP1XiIhosHjb0ma5OyfX1dXBarV26vmIiorq1EMCAGq1GrNmzcL06dPx8ssvA+gIMTKZDBaLBYsWLcK+fftQXV3d62s6mM1mmM1md8r3OJxwS0REg6XByzaPc6uHxWKxIDc3F+np6S7H09PTcejQoU7n6/V6TJkyBampqc62fft2FBYWIjU1FYcPHwYAZGVldbrmokWLurymN7k04ZaBhYiIBtaI7mEBgK1bt2Lnzp3IyclBVlYWVq9ejcTERGzfvh0AsHnzZsTFxWHlypUQQqCgoMDl/TU1Nc69VhxefPFFfPvtt1i3bh0+/PBDLF26FDfccEOv93cZjnwVCmiTxgLgkBAREQ08x+ZxmsgI+MrlsFmtElfUP24Hlt27dyM8PBwbNmxATEwMTpw4gcWLF6O0tBQAEBMTg8TERLeumZWVhTvvvBObNm3Ck08+iXPnzuGOO+7AkSNH3C1v2IhIjIevQg5DSyuaqrsf+iIiIuqL1vpGWIwmKPxUCNZGoaG8UuqS+k14Q1Or1UIIIdRqteS19KZNuW6heC4/S6x5e4fktbCxsbGxeWf7r4/+Lp7LzxJJs2ZIXkt3rbff33yWkEQiR3fsDFx3oUziSoiIyFs5ljaHecE8FgYWiUSO6hg2qykplbgSIiLyVpc2j2NgoT6KHNXRw1LLwEJERIPk0uZxw3+3WwYWiUSO7uhhqb3AwEJERIOjobJjpZA3LG1mYJGAnzoI6vAwAEDdhXKJqyEiIm9VV9rxHePo1R/OGFgkEJnY8RenuaYWpvZ2iashIiJvVVvSsbAjRBsNpb+fxNX0DwOLBBwrhGq5QoiIiAaRQa9Ha0MjACAicXj3sjCwSMCxQojzV4iIaLA5/uc4arR7m7p6GgYWCTjGEutK2MNCRESDy7EaNYKBhdwVwRVCREQ0RBzfNexhIbc592DhHBYiIhpkNec7AotjOsJwxcAyxNQR4fALDITdZkN9WYXU5RARkZdz9LAM96XNDCxDzLFhXENF1bB/1DcREXm++rIK2O12+GvUCAoPlbqcPmNgGWKXhoM4f4WIiAaf1WxG48Udb4fzsBADyxDjQw+JiGioOTaQG84TbxlYhljkqHgAQH0pt+QnIqKhUVNyAQB7WMgNjidm1pdzwi0REQ2NuourUiPHMLBQL4VdDCyNldUSV0JERCOFYxoCe1ioV/zUQfDXqAEAjVUMLERENDQcu92GJ8RB5usrcTV9w8AyhEJjtACA1oZGmA1GiashIqKRollXA7PBCLlC4ZyaMNwwsAyhsNiOwNJwcXkZERHRUBBCoK50eK8UYmAZQqGcv0JERBKpPnceABCbkixxJX3DwDKEQi/2sDCwEBHRUCsrOAUASJgyUeJK+oaBZQg5VghxSIiIiIZa2QkGFuql0DgOCRERkTQqThXBbrMhJDoK6ohwqctxGwPLEAqL4aRbIiKShtlggK64BACQOAx7WRhYhojS3x+BoSEA4HwIFRER0VByzGOJn8zAQt1wTLhtb9bD1NYucTVERDQSDed5LAwsQ4Rb8hMRkdQcgSWRPSzUnbA4rhAiIiJpVRadhdVsRmBoCCIS46Uuxy0MLEMklBNuiYhIYjaLBSXHTgAAkq+aJXE17mFgGSJc0kxERJ7gzOEcAEDynCslrsQ9DCxDxNHDwhVCREQkpTPZRwEA42bPhI+Pj8TV9B4DyxAJYw8LERF5gLITp2BsbUNgSDBiJwyf5woxsAwBhZ8K6vAwAJzDQkRE0rLbbDh39DsAwPg5w2ceCwPLEHAMBxlb22DQt0hcDRERjXSOeSzj586WuJLeY2AZAuEJHUvHGqs4HERERNIrPJgNAEiaNWPYPFeIgWUIjJ05DcClLZGJiIikVFtSivN5x+Erl2PW0p9IXU6vyKUuYLhJmjUDEQlxAIAmXS2E3Y66snIo/fwQnTQGNosVdaVliE4aA6vJBENrGyakzQVwqQuOiIhIaoff+whjpk/FVct/in2v74QQQuqSesTA4oaEyRPxq9df6fP7zx75bgCrISIi6rtjn+/F0nVrEZEYj6RZM3D2SK7UJfWIQ0JuiJs4HgDQrKvF2aPfQV9XD4vRBACw2+1orql1Prq7+mwxSvNPQl9XDwA4l5MHfU2tJHUTERH9mNlgRN6nmQCAq++8TeJqLo89LG4Ij48FABz/ch8+2PI8AEDm6wu5UgGLyQxhtwMAfBUK2CwW5/uCwkLR1tQ89AUTERH14MDb72LeHcsx5fqFiBiVgLoLZVKX1K0+9bBkZGSguLgYBoMBOTk5SEtL6/bcq6++GgcOHEBdXR3a29tx6tQprF271uWclStXQgjRqalUqr6UN2jC4jvmrtSXVzqP2W02mA1GZ1gB4BJWAKC1odHldSIiIk+gO3ceBV8fgEwmw12bfg9fhULqkrrldmBZsWIFXnjhBTz11FOYPn069u/fjz179iAhIaHL89va2vDyyy9jwYIFmDhxIjZt2oRNmzbh/vvvdzmvubkZWq3WpZlMpr59qkHi6GFpKK+QuBIiIqKB8eH/voh2vR6jU6/AsscfkbqcHgl3WnZ2tti2bZvLsZMnT4rNmzf3+hr//Oc/xZtvvun8eeXKlaKxsdGtOn7c1Gq1EEIItVrdr+v01J488Ll4Lj9LaMeNHbTfwcbGxsbGNtQt5eo54pljB8Vz+Vli/t0rhvR39/b7260eFoVCgZkzZyIzM9PleGZmJubNm9era6SmpmLevHn45ptvXI4HBQWhpKQEZWVl+Pjjj5GamtrjdZRKJdRqtUsbTP4aNQKCNQCAhorKy5xNREQ0fJw+mI1PX/wTAODWxx7G6u3Pw08dJHFVrtwKLBEREZDL5dDpdC7HdTodtFptj+8tKyuD0WhETk4OXnnlFezYscP5WmFhIVatWoUlS5bgrrvugtFoxMGDBzFu3Lhur7d+/Xro9Xpnq6gY3GEax/b6LfUNMBuMg/q7iIiIhtq+1/+Gfa//DVaLBSlXz8G6D3bhJ2szsODndyJydKLU5QFwo9smJiZGCCHEnDlzXI4//vjj4tSpUz2+d/To0WLKlCniP/7jP0RdXZ248847uz3Xx8dH5OXliRdffLHbc5RKpVCr1c4WGxs7qENCE+fPE8/lZ4m177whedcdGxsbGxvbYLXYlGTx35+9J57Lz3JpG/f9SyTNmjHgv6+3Q0JuLWuuq6uD1Wrt1JsSFRXVqdflx0pKSgAAJ06cQHR0NJ544gn8/e9/7/JcIQSOHj2K5OTuH3ttNpthNpvdKb9fgrVRAIBmXc2Q/U4iIqKhVnn6DLbcsgLTFy/CFTcsRGBwMBKnToYmIhwtF/cWk4JbgcVisSA3Nxfp6en44IMPnMfT09Px4Ycf9vo6Pj4+l12ynJqaivz8fHfKG1Qh0Y7Aws3fiIjIu9msVuR89ClyPvoUACBXqRA3IRm1JaWS1eT2xnFbt27Fzp07kZOTg6ysLKxevRqJiYnYvn07AGDz5s2Ii4vDypUrAQC/+tWvUFpaisLCQgBAWloaHn30Ufzxj390XnPDhg3Izs7GmTNnoNFo8NBDDyE1NRUPPvjgQHzGAREcHQmAgYWIiEYeq8mEC8dOSFqD24Fl9+7dCA8Px4YNGxATE4MTJ05g8eLFKC3tSF0xMTFITLw0OUcmk+Hpp5/GmDFjYLVace7cOTz22GN49dVXneeEhITgtddeg1arRXNzM/Ly8rBgwQIcPXp0AD7iwAiO6ggsTRwSIiIiGnI+6JjMMuyp1Wro9XpoNBq0tLQM+PV/+8EuaJPG4E/3/drjHxBFREQ0XPT2+5sPP+wl5xwWPsCQiIhoyDGw9IIqMAB+QYEAOIeFiIhICgwsveCYv9Ku18NsMEhcDRER0cjDwNILIVouaSYiIpISA0svBHMPFiIiIkkxsPSCY0iIu9wSERFJg4GlFxw9LNyDhYiISBoMLL3AJc1ERETSYmDphUvb8rOHhYiISAoMLL0QGtPxdOomTrolIiKSBAPLZQQEaxAQrAEA1JeVS1wNERHRyMTAchkRifEAOibcWowmiashIiIamRhYLiNiVAIAoO5CmcSVEBERjVwMLJcRmXgxsJRyOIiIiEgqDCyX4exhKWUPCxERkVQYWC4jIqFjDkvtBfawEBERSUUudQGe7tDu91DyfT4qi85IXQoREdGIxcByGUc/+ETqEoiIiEY8DgkRERGRx2NgISIiIo/HwEJEREQej4GFiIiIPB4DCxEREXk8BhYiIiLyeAwsRERE5PEYWIiIiMjjMbAQERGRx2NgISIiIo/HwEJEREQej4GFiIiIPB4DCxEREXk8r3tas1qtlroEIiIi6qXefm97TWBxfOCKigqJKyEiIiJ3qdVqtLS0dPu6DwAxdOUMrtjY2B4/bF+o1WpUVFQgLi5uwK9NvL+Difd28PDeDi7e38HjqfdWrVajsrKyx3O8pocFwGU/bH+0tLR41D9cb8P7O3h4bwcP7+3g4v0dPJ52b3tTCyfdEhERkcdjYCEiIiKPx8ByGSaTCU888QRMJpPUpXgl3t/Bw3s7eHhvBxfv7+AZzvfWqybdEhERkXdiDwsRERF5PAYWIiIi8ngMLEREROTxGFiIiIjI4zGwXEZGRgaKi4thMBiQk5ODtLQ0qUvyePPnz8dHH32EiooKCCGwdOnSTuds3LgRFRUVaG9vx759+zBp0iSX15VKJV566SXU1taitbUVH374IeLi4obqI3isxx57DEeOHIFer4dOp8P777+P8ePHdzqP99d9v/zlL3Hs2DE0NzejubkZhw4dwk033eRyDu/rwHjssccghMDzzz/vcpz3t282btwIIYRLq6qq6nSON9xbwdZ1W7FihTCZTOK+++4TEyZMEM8//7xoaWkRCQkJktfmye2mm24STz75pFi2bJkQQoilS5e6vL5u3TrR3Nwsli1bJiZPnizefvttUVFRIYKCgpznbNu2TZSVlYnrr79epKamir1794q8vDwhk8kk/3xStj179oiVK1eKSZMmialTp4qPP/5YlJSUiICAAN7ffrZbbrlF3HzzzSI5OVkkJyeLTZs2CZPJJCZNmsT7OoDtyiuvFMXFxeL7778Xzz//vPM472/f28aNG0V+fr6Ijo52toiICG+8t5IX4LEtOztbbNu2zeXYyZMnxebNmyWvbbi0rgJLZWWlWLdunfNnpVIpGhsbxerVqwUAodFohMlkEitWrHCeExMTI6xWq1i0aJHkn8mTWkREhBBCiPnz5/P+DkKrr68X//7v/877OkAtMDBQnD59Wlx//fVi3759LoGF97fvbePGjSIvL6/b173l3nJIqBsKhQIzZ85EZmamy/HMzEzMmzdPoqqGvzFjxiAmJsblvprNZnzzzTfO+zpz5kwolUqXc6qqqnDixAne+x8JDg4GADQ0NADg/R0oMpkMd9xxBwIDA5GVlcX7OkBeeeUVfPLJJ9i7d6/Lcd7f/ktOTkZFRQWKi4vx9ttvY8yYMQC869561cMPB1JERATkcjl0Op3LcZ1OB61WK1FVw5/j3nV1X0eNGuU8x2QyoampqdM5vPeutm7div3796OgoAAA729/TZkyBVlZWfDz80NrayuWLVuGU6dOYe7cuQB4X/vjjjvuwIwZMzBr1qxOr/Hvbf8cPnwYv/jFL1BUVITo6Gj87ne/w6FDhzB58mSvurcMLJchhHD52cfHp9Mxcl9f7ivvvauXX34ZU6dO7XIiOO9v35w+fRqpqakICQnBbbfdhr/+9a9YuHCh83Xe176Jj4/Hiy++iEWLFvW4JTzvb9989tlnzj+fOHECWVlZOHfuHFauXIns7GwA3nFvOSTUjbq6Olit1k7pMioqqlNSpd6rrq4GgB7va3V1NVQqFUJCQro9Z6R76aWXsGTJElx77bWoqKhwHuf97R+LxYJz584hNzcXjz/+OI4dO4Y1a9bwvvbTzJkzER0djdzcXFgsFlgsFlxzzTV46KGHYLFYnPeH93dgtLe3Iz8/H8nJyV71d5eBpRsWiwW5ublIT093OZ6eno5Dhw5JVNXwd/78eVRVVbncV4VCgYULFzrva25uLsxms8s5Wq0WU6ZM4b0H8Mc//hHLly/Hddddh5KSEpfXeH8Hlo+PD1QqFe9rP+3duxdTpkxBamqqsx09ehRvvfUWUlNTUVxczPs7gJRKJSZOnIiqqiqv+7sr+cxfT22OZc333nuvmDBhgti6datoaWkRiYmJktfmyS0wMFBMmzZNTJs2TQghxNq1a8W0adOcy8HXrVsnGhsbxa233iomT54s3nrrrS6X2JWWlorrrrtOpKamii+//NITl9gNeXvllVdEY2OjWLBggcsSRj8/P+c5vL99a0899ZRIS0sTo0aNElOmTBGbNm0SVqtV3HDDDbyvg9B+vEqI97fv7ZlnnhELFiwQo0ePFrNnzxYfffSRaG5udn5XedG9lbwAj24ZGRni/Pnzwmg0ipycHJflo2xdt4ULF4quvPHGG85zNm7cKCorK4XBYBBff/21mDx5sss1VCqVeOmll0RdXZ1oa2sTH330kYiPj5f8s0ndurNy5UqX83h/3W9//vOfnf+u63Q68cUXXzjDCu/rwLcfBxbe3743x74qJpNJlJeXi3fffVdMnDjR6+6tz8U/EBEREXkszmEhIiIij8fAQkRERB6PgYWIiIg8HgMLEREReTwGFiIiIvJ4DCxERETk8RhYiIiIyOMxsBAREZHHY2AhIiIij8fAQkRERB6PgYWIiIg8HgMLERERebz/DwgiWHnpNbJpAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(arr1[1:] / arr2[1:])" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[403873][V][run_esp.cpp:23] do_esp_cmd(): [ESP] parse ESP commands\n", - "[403879][V][run_esp.cpp:41] do_esp_cmd(): [ESP] cmd is 1, 2, 6, 6, 2, 6, 6, 0\n", - "[403886][V][run_esp.cpp:52] do_esp_cmd(): [ESP] gains are 1, 5, 5, 1, 5, 5\n", - "[403897][V][run_esp.cpp:23] do_esp_cmd(): [ESP] parse ESP commands\n", - "[403903][V][run_esp.cpp:41] do_esp_cmd(): [ESP] cmd is 2, 110, 60, 20, 0, 0, 0, 0\n", - "[403910][V][run_esp.cpp:60] do_esp_cmd(): [ESP] currents are 110, 60, 20\n", - "[403917][V][run_esp.cpp:23] do_esp_cmd(): [ESP] parse ESP commands\n", - "[403923][V][run_esp.cpp:41] do_esp_cmd(): [ESP] cmd is 10, 0, 0, 0, 0, 0, 0, 0\n", - "[403930][V][u_adpd6100.cpp:618] preset_config_1(): [ADPD] Preset_config 1 set for timeslot:0. Two ambient channels. Total 2 x 3 bytes\n", - "[403942][V][u_adpd6100.cpp:666] preset_config_2(): [ADPD] Preset_config 2 set for timeslot:1. Total 2 x 2 x 3 bytes\n", - "[403953][V][u_adpd6100.cpp:714] preset_config_3(): [ADPD] Preset_config 3 set for timeslot:2. Total 2 x 3 bytes\n", - "[403964][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:3. Total 0 bytes\n", - "[403974][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:4. Total 0 bytes\n", - "[403984][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:5. Total 0 bytes\n", - "[403995][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:6. Total 0 bytes\n", - "[404005][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:7. Total 0 bytes\n", - "[404015][V][u_adpd6100.cpp:763] preset_config_4(): [ADPD] Preset_config 4 set for timeslot:8. Total 0 bytes\n", - "[404025][V][run_esp.cpp:23] do_esp_cmd(): [ESP] parse ESP commands\n", - "[404031][V][run_esp.cpp:41] do_esp_cmd(): [ESP] cmd is 21, 3, 0, 0, 0, 0, 0, 0\n", - "[404039][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:50, freq: 50, actinic: 0, subfactor 2\n", - "[404048][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:50, freq: 50, actinic: 50, subfactor 2\n", - "[404059][V][PAM.cpp:116] run_preprocess_type1(): [PAM] Run type:1, number:50, freq: 50, actinic: 0, subfactor 2\n", - "[404068][V][PAM.cpp:134] run_arr_type1(): [PAM] Sample & Ref: 150, optionals: 18\n", - "[404076][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[404082][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[404089][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[404096][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[404102][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[404109][V][data_utils.cpp:10] dataclass(): [DATA_UTIL] DATACLASS Created\n", - "[404115][V][data_utils.cpp:33] init(): [DATA_UTIL] Allocation 600 bytes\n", - "[404122][V][data_utils.cpp:64] clear(): [DATA_UTIL] ARR reset to 0\n", - "[404128][V][data_utils.cpp:33] init(): [DATA_UTIL] Allocation 600 bytes\n", - "[404134][V][data_utils.cpp:64] clear(): [DATA_UTIL] ARR reset to 0\n", - "[404140][V][data_utils.cpp:33] init(): [DATA_UTIL] Allocation 72 bytes\n", - "[404147][V][data_utils.cpp:64] clear(): [DATA_UTIL] ARR reset to 0\n", - "[404153][V][data_utils.cpp:33] init(): [DATA_UTIL] Allocation 72 bytes\n", - "[404159][V][data_utils.cpp:64] clear(): [DATA_UTIL] ARR reset to 0\n", - "[404165][V][data_utils.cpp:33] init(): [DATA_UTIL] Allocation 72 bytes\n", - "[404171][V][data_utils.cpp:64] clear(): [DATA_UTIL] ARR reset to 0\n", - "[404177][V][data_utils.cpp:33] init(): [DATA_UTIL] Allocation 72 bytes\n", - "[404184][V][data_utils.cpp:64] clear(): [DATA_UTIL] ARR reset to 0\n", - "[404190][V][PAM.cpp:150] run_arr_type1(): [PAM] Memory allocation completed\n", - "[406733][V][data_utils.cpp:264] send_esp(): [DATA_UTIL] #0 wakeup call\n", - "[406759][V][data_utils.cpp:270] send_esp(): [DATA_UTIL] Wake response in 26 ms\n", - "[406766][V][data_utils.cpp:292] send_esp(): [DATA_UTIL] #0 data call\n", - "checked\n", - "[406835][V][data_utils.cpp:318] send_esp(): [DATA_UTIL] Spend 51 ms sending 150 data\n", - "[406856][V][data_utils.cpp:264] send_esp(): [DATA_UTIL] #0 wakeup call\n", - "[406882][V][data_utils.cpp:270] send_esp(): [DATA_UTIL] Wake response in 26 ms\n", - "[406889][V][data_utils.cpp:292] send_esp(): [DATA_UTIL] #0 data call\n", - "checked\n", - "[406959][V][data_utils.cpp:318] send_esp(): [DATA_UTIL] Spend 51 ms sending 150 data\n", - "[406974][V][data_utils.cpp:264] send_esp(): [DATA_UTIL] #0 wakeup call\n", - "[407000][V][data_utils.cpp:270] send_esp(): [DATA_UTIL] Wake response in 26 ms\n", - "[407007][V][data_utils.cpp:292] send_esp(): [DATA_UTIL] #0 data call\n", - "checked\n", - "[407026][V][data_utils.cpp:318] send_esp(): [DATA_UTIL] Spend 0 ms sending 18 data\n", - "[407049][V][data_utils.cpp:264] send_esp(): [DATA_UTIL] #0 wakeup call\n", - "[407075][V][data_utils.cpp:270] send_esp(): [DATA_UTIL] Wake response in 26 ms\n", - "[407082][V][data_utils.cpp:292] send_esp(): [DATA_UTIL] #0 data call\n", - "checked\n", - "[407102][V][data_utils.cpp:318] send_esp(): [DATA_UTIL] Spend 0 ms sending 18 data\n", - "[407125][V][data_utils.cpp:264] send_esp(): [DATA_UTIL] #0 wakeup call\n", - "[407151][V][data_utils.cpp:270] send_esp(): [DATA_UTIL] Wake response in 26 ms\n", - "[407158][V][data_utils.cpp:292] send_esp(): [DATA_UTIL] #0 data call\n", - "checked\n", - "[407177][V][data_utils.cpp:318] send_esp(): [DATA_UTIL] Spend 0 ms sending 18 data\n", - "[407199][V][data_utils.cpp:264] send_esp(): [DATA_UTIL] #0 wakeup call\n", - "[407225][V][data_utils.cpp:270] send_esp(): [DATA_UTIL] Wake response in 26 ms\n", - "[407232][V][data_utils.cpp:292] send_esp(): [DATA_UTIL] #0 data call\n", - "checked\n", - "[407250][V][data_utils.cpp:318] send_esp(): [DATA_UTIL] Spend 0 ms sending 18 data\n" - ] - } - ], - "source": [ - "with serial.Serial(\"COM31\", 115200) as ser:\n", - " ser.write(bytes([160] + [1, 2, 6, 6, 2, 6, 6, 0])) # gains\n", - "\n", - " ser.write(bytes([160] + [2, 110, 60, 20, 0, 0, 0, 0]))\n", - " \n", - " ser.write(bytes([160] + [10, 0, 0, 0, 0, 0, 0, 0]))\n", - "\n", - " ser.write(bytes([160] + [21, 3, 0, 0, 0, 0, 0, 0]))\n", - "\n", - " ser.write(bytes([1, 0, 0, 50, 0, 50, 0, 2]))\n", - " ser.write(bytes([1, 0, 0, 50, 0, 50, 50, 2]))\n", - " ser.write(bytes([1, 0, 0, 50, 0, 50, 0, 2]))\n", - " time.sleep(2.5)\n", - "\n", - " _, arr1 = get_trace(ser)\n", - " _, arr2 = get_trace(ser)\n", - " _, arr3 = get_trace(ser)\n", - " _, arr4 = get_trace(ser)\n", - " _, arr5 = get_trace(ser)\n", - " _, arr6 = get_trace(ser)\n", - "\n", - " " - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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rc1XantzbmvzldrenNvafKXQn3NLx87GcgAAIKAIHwAAIKDaVPioq6vTPffco7q6OquHEjBtbc5tbb5S25tzW5uv1Pbm3NbmK7W9OQdlwykAAAhfbaryAQAArEf4AAAAAUX4AAAAAUX4AAAAAdVmwse0adOUk5OjmpoarVy5UqNGjbJ6SKb54x//qOXLl8vtdqugoEDvvPOOevXqddR5d999t/Lz81VdXa2FCxeqX79+FozWfH/84x9lGIZmzZrV7PFwm296errmzp2roqIiVVVVafXq1Ro2bFizc8JlzpGRkbrvvvuUk5Oj6upq7dy5U3fddZciIiKanRfK8x09erTef/995efnyzAMXXTRRUedc7z5ORwOzZ49W4WFhaqsrNR7772njIyMQE2hVX5ovna7XQ888IDWrVunyspK5efn6+WXX1ZaWlqz1wil+Uot+x17PfPMMzIMQ7fcckuzx0Ntzq1hhPvxs5/9zKirqzOuvfZao0+fPsasWbOMiooKo3PnzpaPzYzjww8/NKZOnWr069fPGDRokDFv3jwjLy/PiI2N9Z0zffp0o7y83Lj44ouN/v37G6+99pqRn59vxMfHWz7+kzlGjBhh5OTkGGvWrDFmzZoVtvNNSkoycnNzjRdffNEYOXKkkZWVZYwbN87o1q1bWM75jjvuMAoLC40f//jHRlZWlnHJJZcYbrfbuPnmm8Nmvj/60Y+M++67z7j44osNwzCMiy66qNnzLZnf008/bezZs8c455xzjCFDhhifffaZsXr1asNms1k+v9bMNyEhwViwYIHx05/+1OjVq5dx6qmnGsuWLTNWrFjR7DVCab4t+R17j4suushYvXq1sXfvXuOWW24J6Tm34rB8AH4/vvrqK+Ppp59u9timTZuM+++/3/Kx+eNISUkxDMMwRo8e7Xts3759xvTp031fOxwOo7S01Lj++ustH++JHnFxccbWrVuNc845x1i4cGGz8BFu8505c6bxxRdf/OA54TTnefPmGS+88EKzx9566y3jlVdeCcv5HuuD6XjzS0hIMOrq6oyf/exnvnPS0tKMhoYGY+LEiZbPqbXz/e4xYsQIwzAM3z8SQ3m+PzTn9PR0Y8+ePUa/fv2M3NzcZuEj1Of8Q0fYX3aJiorS8OHDtWDBgmaPL1iwQGeccYZFo/KvxMRESVJJSYkkKTs7W2lpac1+Bh6PR4sWLQrpn8FTTz2l+fPn67PPPmv2eDjO98ILL9TKlSv1xhtvqKCgQKtWrdJ1113nez7c5rx48WKdc8456tmzpyRp0KBBGjVqlD744ANJ4Tff72rJ/IYPHy6Hw9HsnP3792vDhg1h8TNITExUU1OTysrKJIXnfCMiIjR37lw99NBD2rRp01HPh+OcvYJyYzkzpaSkyG63q6CgoNnjBQUFSk1NtWhU/vXoo4/qyy+/1MaNGyXJN89j/QyysrICPj4zXHbZZRo2bJhGjhx51HPhON9u3bpp2rRpevTRR3X//ffrlFNO0ezZs1VXV6e5c+eG3Zz/9re/KTExUVu2bFFjY6MiIyN155136vXXX5cUnr/jb2vJ/FJTU1VXV+f7cP72OaH+d5vT6dQDDzygV1991bdRWTjO9/bbb1dDQ4Nmz559zOfDcc5eYR8+vAzDaPZ1RETEUY+FgyeffNL3r8TvCpefQWZmph5//HFNnDjxB5ciDpf5SpLNZtPKlSt15513SpLWrFmj/v37a9q0aZo7d67vvHCZ82WXXaYrrrhCU6ZM0caNGzVkyBA99thj2rdvn1555RXfeeEy3+9zIvML9Z+B3W7X66+/LpvNpl/96lfHPT9U5zts2DDdcsstRzWNt0Sozvnbwv6yS1FRkRoaGo5KiR07djzqXxWhbvbs2brwwgt19tlnKz8/3/f4gQMHJClsfgbDhw9Xp06d9M0336i+vl719fU666yzdPPNN6u+vt43p3CZr3So1PrdsuzmzZvVpUsXSeH3O37ooYf0wAMP6N///rc2bNigf/7zn5o1a5ZmzJghKfzm+10tmd+BAwfkdDqVlJT0veeEGrvdrjfeeEPZ2dmaMGFCs+3Zw22+o0ePVseOHbV7927f32Ndu3bVI488otzcXEnhN+dvC/vwUV9fr2+++UYTJkxo9viECRO0dOlSi0ZlvieeeEI/+clPNG7cOOXl5TV7Ljc3V/v372/2M4iKitLYsWND8mfw2WefacCAARoyZIjvWLFihf71r39pyJAhysnJCav5StKSJUvUu3fvZo/16tVLu3btkhR+v+PY2Fg1NTU1e6yxsVE226G/ssJtvt/Vkvl988038ng8zc5JTU3VgAEDQvJn4A0ePXv21Pjx4309a17hNt+5c+dq0KBBzf4ey8/P10MPPaRzzz1XUvjN+bss73r19+G91fbqq682+vTpYzz66KNGRUWF0aVLF8vHZsbx1FNPGaWlpcaYMWOMTp06+Y7o6GjfOdOnTzdKS0uNSZMmGf379zf+9a9/hdRticc7vnu3S7jNd8SIEYbH4zFmzJhhdO/e3Zg8ebJRWVlpTJkyJSznPGfOHGPPnj2+W20nTZpkHDx40HjggQfCZr5xcXHG4MGDjcGDBxuGYRi//e1vjcGDB/vu7mjJ/J5++mlj9+7dxrhx44whQ4YYn376adDehvlD842MjDTeffddY/fu3cagQYOa/T0WFRUVkvNtye/4u8d373YJxTm34rB8AAE5pk2bZuTm5hq1tbXGypUrm92GGurH95k6dWqz8+6++25j3759Rk1NjfH5558b/fv3t3zsZh3fDR/hON//9//+n7Fu3TqjpqbG2LRpk3HdddcddU64zDk+Pt6YNWuWkZeXZ1RXVxs7duww7rvvvmYfRKE+37Fjxx7z/7dz5sxp8fycTqcxe/Zso6ioyKiqqjLef/99IzMz0/K5tXa+WVlZ3/v32NixY0Nyvi39HX/7OFb4CLU5t/SIOPwHAACAgAj7ng8AABBcCB8AACCgCB8AACCgCB8AACCgCB8AACCgCB8AACCgCB8AACCgCB8AACCgCB8AACCgCB8AACCgCB8AACCgCB8AACCg/j9ZyX34tqZGZgAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(arr1/arr2)" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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U2FMSEAwGFSKTIliUavfjxa3BwDrgEj7ehyARowYgcLCQtTW1sLj8WD//v2YPHmycn9ycjKef/55nDx5Ek1NTThy5AjuvffeiOewWCwoKCjA6dOn4Xa7sXHjRowcOTKij8PhwLp16+ByueByubBu3Tqkpkb+D5eeno6ioiK43W6cPn0azz33HMxmc09fEpGqbEPaTn81ueoRbG1VeTRE3eN+YKQXPQpADocDu3btgt/vx80334wrr7wSjzzyCFwul9Jn1apVyMvLw+LFi3HFFVdg1apVeP755zF37lylz+rVq3H77bdj0aJFmDFjBmw2G9577z0YjeHhrF+/HhkZGcjLy0NeXh4yMjJQWFgYHrjRiM2bNyM5ORkzZszAokWLsGDBAuTn5/fh7SCKPjsLoElDuCM86YlcaFu5cqV8+OGH3fYpKyuTX/ziFxHH9u3bJ//1X/8lACQlJUW8Xq8sXLhQuT8tLU0CgYDk5uYKAJkwYYKIiGRlZSl9srOzRURk/PjxAkDy8vIkEAhIWlqa0ueOO+6Q5uZmsdvtF/R67Ha7iMgF92djG4g2eU6u5JeVyL0vP6/6WNjYzteWvfhbyS8rkax5/6r6WNjit/XH7+8ezQDNnTsX+/btw4YNG1BdXY2///3vuPvuuyP6/O1vf8PcuXMxYsQIAMANN9yA8ePHY/v27QCAKVOmwGKxoLi4WHlMZWUlDh8+jOnTpwMApk2bBpfLhb179yp99uzZA5fLFdHn8OHDqKysVPps374diYmJmDJlSk9eFpGqbFwEkTSEM0CkFz0KQJdeeinuu+8+HDt2DDfddBNeeuklFBQU4K677lL6PPTQQzhy5AgqKirg8/mwbds23H///di1axcAwOl0wuv1Rpw2A4Dq6mo4nU6lT01NTafvX1NTE9Gnuro64n6XywWv16v06chiscBut0c0IrWFN0KtU3kkROcXXg2aNUCkbaaedDYajdi3bx9+/vOfAwAOHDiAiRMn4r777lPqcx566CFMnToVt956K7788ktcf/31WLNmDSorK/H++++f87kNBgNERLl99td96XO2FStW4Iknnrig10oULVwEkbTE0/7HK2eASOt6NANUWVmJI0eORBz77LPPMHr0aABAYmIinnrqKfzkJz/Be++9h7KyMrz44ov4wx/+gEcffRQAUFVVBavVCofDEfE8w4YN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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.plot(arr4)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/data_utils.cpp b/data_utils.cpp deleted file mode 100644 index 1c4ac6a..0000000 --- a/data_utils.cpp +++ /dev/null @@ -1,718 +0,0 @@ -#include "data_utils.h" - - -static const char* TAG = "DATA_UTIL"; -extern bool FLAG_DEICE; - - -// return 1, 2, 3 -// or -1 -int serial_read_until(uint8_t target1, uint8_t target2 = 0, uint8_t target3 = 0, uint16_t timeout = 20, bool remove = false){ - unsigned long start_t = millis(); - uint8_t counter, b = 0; - - while (((millis() - start_t) < timeout)){ - if (Serial.available() > 0){ - b = Serial.peek(); - if (b == target1){ - if (remove) Serial.read(); - return 1; - } else if ((target2 > 0) && (b == target2)){ - if (remove) Serial.read(); - return 2; - } else if ((target3 > 0) && (b == target3)){ - if (remove) Serial.read(); - return 3; - } - - b = Serial.read(); - if (b > 127){ - Serial.print(b); - }else{ - Serial.write(b); - } - } - else{ - delayMicroseconds(10); - } - } - return -1; -} - -uint16_t flush_serial(uint8_t timeout){ - uint16_t c = 0; - uint8_t r = 0; - delay(1); - unsigned long start_t = millis(); - if (Serial.available() < 1) return 0; - - while (((millis() - start_t) < timeout)){ - if (Serial.available() > 0){ - r = Serial.read(); - if (r < 127) { - Serial.write(r); - }else{ - Serial.print(r); - } - c += 1; - }else{ - break; - } - } - return c; -} - - - -dataclass::dataclass(){ - dataclass::available = false; - dataclass::write_available = false; - dataclass::read_available = false; - ESP_LOGV(TAG, "DATACLASS Created"); - return; -}; - -dataclass::~dataclass(void){ - ESP_LOGV(TAG, "DATACLASS destroied"); - if (dataclass::available) dataclass:clean(); - return; -}; - -void dataclass::clean(void){ - if (dataclass::available) free((void*) (dataclass::arr)); - ESP_LOGV(TAG, "Memory freed"); - dataclass::arr = NULL; - dataclass::available = false; - dataclass::write_available = false; - dataclass::read_available = false; - return; -}; - - -bool dataclass::init(uint16_t length){ - if ((length > 0) and length < MAX_DATACLASS_SIZE){ - ESP_LOGV(TAG, "Allocation %d bytes", length * 4); - (dataclass::arr) = (uint32_t*) heap_caps_calloc(length, sizeof(uint32_t), MALLOC_CAP_32BIT); - if (dataclass::arr == NULL){ - ESP_LOGE(TAG, "Allocation %d bytes failed", length * 4); - return false; - } - dataclass::_length = length; - dataclass::available = true; - dataclass::clear(); - dataclass::write_available = true; - dataclass::write_ptr = 0; - dataclass::read_ptr = 0; - dataclass::read_available = false; - dataclass::_overwritten_counter = 0; - dataclass::loop_ahead = false; - return true; - } - else{ - ESP_LOGE(TAG, "Out-of-range: %d bytes ", length * 4); - return false; - } - return false; -}; - -// set data array to 0 -void dataclass::clear(void){ - if (dataclass::available){ - for(uint16_t i = 0; i < dataclass::_length; i++) (dataclass::arr)[i] = 0; - dataclass::write_ptr = 0; - dataclass::read_ptr = 0; - dataclass::loop_ahead = false; - ESP_LOGV(TAG, "ARR reset to 0"); - }else{ - ESP_LOGE(TAG,"RESET failed, not initialized"); - } -} - -// add the next point to the data array -// update loop_over -void dataclass::put(uint32_t data){ - if (!(dataclass::available)){ - ESP_LOGE(TAG, "Add failed, not initialized"); - return; - } - - // write point cycling around and reach read pointer - if ((dataclass::write_ptr >= dataclass::read_ptr) && (dataclass::loop_ahead)){ - dataclass::read_ptr = dataclass::write_ptr + 1; - dataclass::length -= dataclass::write_ptr - dataclass::read_ptr + 1; - if (dataclass::read_ptr >= dataclass::_length) dataclass::read_ptr = 0; - dataclass::_overwritten_counter += 1; - ESP_LOGW(TAG, "Data over-written, total: %d", dataclass::_overwritten_counter); - } - - dataclass::arr[write_ptr] = data; - dataclass::length += 1; - dataclass::write_ptr += 1; - - dataclass::read_available = true; - if (dataclass::write_ptr >= dataclass::_length){ - dataclass::write_ptr = 0; - dataclass::loop_ahead = true; - } - -} - -uint16_t dataclass::get_length(void){ - if (!(dataclass::available)){ - ESP_LOGE(TAG, "Add failed, not initialized"); - return 0; - } - if (!(dataclass::read_available)){ - ESP_LOGW(TAG, "No data available"); - return 0; - } - if (dataclass::loop_ahead){ - if (dataclass::write_ptr <= dataclass::read_ptr) return - dataclass::_length - dataclass::read_ptr + dataclass::write_ptr; - ESP_LOGW(TAG, "write_ptr > read_ptr After loop around!!!!"); - return 0; - }else{ - if (dataclass::write_ptr > dataclass::read_ptr) return (dataclass::write_ptr - dataclass::read_ptr); - if (dataclass::write_ptr == dataclass::read_ptr){ - dataclass::read_available = false; - return 0; - } - ESP_LOGW(TAG, "write_ptr < read_ptr WITHOUT loop around!!!!"); - return 0; - } - return 0; // should not reach here -} - - - -uint32_t dataclass::send(void (*func) (uint32_t*, uint16_t), uint8_t* c){ - uint8_t checksum = 0; - if (dataclass::get_length() == 0){ - dataclass::read_available = false; - ESP_LOGW(TAG, "No data available"); - return 0; - } - - if (dataclass::loop_ahead){ - if (dataclass::write_ptr <= dataclass::read_ptr){ - func(this->arr + this->read_ptr, this->_length - this->read_ptr); - func(this->arr, this->read_ptr); - for (uint16_t i = 0; i < this->_length - this->read_ptr; i++) checksum += this->arr[this->read_ptr + i]; - for (uint16_t i = 0; i < this->read_ptr; i++) checksum += this->arr[i]; - *c = checksum; - return 1; - } - ESP_LOGW(TAG, "write_ptr > read_ptr After loop around!!!!"); - return 0; - }else{ - if (dataclass::write_ptr > dataclass::read_ptr){ - func(this->arr + this->read_ptr, this->write_ptr - this->read_ptr); - for (uint16_t i = 0; i < this->write_ptr - this->read_ptr; i++) checksum += this->arr[this->read_ptr + i]; - *c = checksum; - return 1;} - - if (dataclass::write_ptr == dataclass::read_ptr){ - dataclass::read_available = false; - return 0; - } - ESP_LOGW(TAG, "write_ptr < read_ptr WITHOUT loop around!!!!"); - return 0; - } - return 0; -} - -bool dataclass::pop(uint32_t* data){ - if (!(dataclass::available)){ - ESP_LOGE(TAG, "Add failed, not initialized"); - return false; - } - if (!(dataclass::read_available)){ - ESP_LOGW(TAG, "No data available"); - return false; - } - - if (dataclass::get_length() == 0){ - dataclass::read_available = false; - ESP_LOGW(TAG, "No data available"); - return false; - } - - if (dataclass::read_ptr >= dataclass::_length){ - ESP_LOGE(TAG, "Read_pointer outside range"); - return false; - } - - *data = dataclass::arr[dataclass::read_ptr]; - dataclass::read_ptr += 1; - dataclass::length -= 1; - if (dataclass::read_ptr >= dataclass::_length){ - dataclass::read_ptr = 0; - dataclass::loop_ahead = false; - } - return true; -} - -uint32_t dataclass::pop(void){ - uint32_t a; - if (dataclass::pop(&a)) return a; - return 0xABCDEF01; -} - -void dataclass::send_serial(const char* tag){ - - if (!this->available) return; - uint16_t tmp_var = dataclass::get_length(); - Serial.printf("Data:%s,Length:%d\t", tag, tmp_var); - if (tmp_var == 0){ - Serial.println(); - return; - } - for (uint16_t i = 0; i < tmp_var; i++){ - Serial.printf("%d,", dataclass::pop()); - } - Serial.print("\n"); - return; -} - -static void send_binary_array(uint32_t* arr, uint16_t len){ - Serial.write((uint8_t*) arr, len * 4); -} - -int dataclass::fsm_wake_up_calls(bool interrupt){ - if (this->data_fsm_state != DATA_STATUS::WAKEUPCALLS) return -1; - - unsigned int timer1 = millis(); - int ret = -1; - uint8_t wake_up_reason, cmd_wait_time; - int16_t wake_call_counter = 0; - int16_t max_wake_try = 2000; - cmd_wait_time = 15; - flush_serial(10); - // sending wake up calls, first 10 in 10ms, remaining - while (wake_call_counter < max_wake_try){ - Serial.write(WAKE_AMBYTE); - Serial.flush(); - ret = serial_read_until(AMBYTE_AWAKE, AMBYTE_CALLS, AMBYTE_CALLFORRESET, cmd_wait_time, false); - if (ret > 0) break; - // after first 10 fast tries, pulse every second with light sleep - if (wake_call_counter == 10){ - esp_sleep_enable_timer_wakeup(1000000); // set up light sleep timer - cmd_wait_time = 100; - } - if (wake_call_counter > 10){ - wake_up_reason = 0; - - - if (!FLAG_DEICE){ // Power saving mode - esp_light_sleep_start(); - wake_up_reason = esp_sleep_get_wakeup_cause(); - }else{ // Power wasting mode - for (uint8_t j = 0; j < 10; j++){ - ret = serial_read_until(AMBYTE_AWAKE, AMBYTE_CALLS, AMBYTE_CALLFORRESET, 100, false); - if (ret > 0) break; - } - } - - if (wake_up_reason == 8){ // wake up by uart - max_wake_try -= wake_call_counter; - wake_call_counter = 0; - } - - - } - wake_call_counter += 1; - if (millis() - timer1 > 3600000) break; - } - - if (ret == 1){ // Normal - this->data_fsm_state = DATA_STATUS::LENGTHARRAY; - ESP_LOGV(TAG, "Ambyte awake in %d ms with %d tries", millis() - timer1, this->_num_wake_up_calls); - return 1; - }else if (ret == 2){ // Lost sync - ESP_LOGE(TAG, "ERR_LOST_SYNC"); - Serial.read(); - return ERR_LOST_SYNC; - }else if (ret == 3){ - ESP_LOGE(TAG, "Ambyte calls for re-start"); - Serial.read(); - return 0; - }else{ // no response, re-try until timeout - return ERR_NO_DATA_REQUEST; - } -} - - -int dataclass::fsm_wake_up_calls(void){ - if (this->data_fsm_state != DATA_STATUS::WAKEUPCALLS) return -1; - if (this->_num_wake_up_calls > 20) return ERR_TOO_MANY_WKUP; - if (this->num_retry > 18) return ERR_TOO_MANY_RETRY; - - - unsigned int timer1 = millis(); - this->_num_wake_up_calls += 1; - this->num_retry += 1; - - Serial.println("W:"); - flush_serial(10); // remove serial buffer - - // potential status - //1. Normal: run finished, ambyte sleep - //2. Ambyte 170 CMD: Lost_sync, return to Idle - //3. Ambyte 222 reset: Re-do the data send. Re-try + 1 - - Serial.write(WAKE_AMBYTE); // send run-finished, data-ready code - int ret = serial_read_until(AMBYTE_AWAKE, AMBYTE_CALLS, AMBYTE_CALLFORRESET, 100, false); - if (ret == 1){ // Normal - this->data_fsm_state = DATA_STATUS::LENGTHARRAY; - ESP_LOGV(TAG, "Ambyte awake in %d ms with %d tries", millis() - timer1, this->_num_wake_up_calls); - this->_num_wake_up_calls = 0; - return 0; - }else if (ret == 2){ // Lost sync - ESP_LOGE(TAG, "ERR_LOST_SYNC"); - Serial.read(); - return ERR_LOST_SYNC; - }else if (ret == 3){ - ESP_LOGE(TAG, "Ambyte calls for re-start"); - Serial.read(); - return 0; - }else{ // no response, re-try until timeout - return 0; - } -} - -int dataclass::fsm_send_length_info(uint8_t arr_idx){ - if (this->data_fsm_state != DATA_STATUS::LENGTHARRAY) return -1; - unsigned int timer1 = millis(); - Serial.println("L:"); - - int ret = serial_read_until(AMBYTE_AWAKE, 0, 0, 100, true); - if (ret != 1){ - ESP_LOGE(TAG, "Status not match: length got %d", ret); - this->data_fsm_state = DATA_STATUS::WAKEUPCALLS; - return 0; - } - uint16_t tmp_var = dataclass::get_length(); - uint8_t arr[8] = {212, 150, arr_idx, 0, 0, 0, 0, 0}; - arr[3] = ((tmp_var >> 8) & 0xFF); - arr[4] = (((tmp_var) & 0xFF)); - for (uint8_t i = 0; i < 7; i++) arr[7] += arr[i]; - flush_serial(5); - Serial.write(arr, 8); - ret = serial_read_until(AMBYTE_READY_FOR_ARRAY, 0, AMBYTE_CALLFORRESET, 200, false); - // ambyte ready for data, no extra byte to send - - if (ret == 1){ - this->data_fsm_state = DATA_STATUS::SENDDATA; - return 0; - }else if (ret == 2){ - ESP_LOGE(TAG, "del"); - return 0; - }else if (ret == 3){ - ESP_LOGE(TAG, "Ambyte calls for re-start"); - Serial.read(); - this->data_fsm_state = DATA_STATUS::WAKEUPCALLS; - return 0; - }else{ - ESP_LOGE(TAG, "UNknown response"); - this->data_fsm_state = DATA_STATUS::WAKEUPCALLS; - return 0; - } -} - -int dataclass::fsm_send_data(void){ - if (this->data_fsm_state != DATA_STATUS::SENDDATA) return -1; - if (this->_num_checksum_err > 6) return ERR_CHECKSUM_FAILED; - unsigned int timer1 = millis(); - int ret = serial_read_until(AMBYTE_READY_FOR_ARRAY, 0, 0, 100, true); - if (ret != 1){ - ESP_LOGE(TAG, "Status not match: data"); - this->data_fsm_state = DATA_STATUS::WAKEUPCALLS; - return 0; - } - uint8_t arr[4] = {212, 0, 0, 0}; - flush_serial(5); - this->send(send_binary_array, &arr[3]); - Serial.flush(); - Serial.write(arr, 4); - Serial.flush(); - this->_num_checksum_err += 1; - ret = serial_read_until(AMBYTE_DATA_PASS, AMBYTE_READY_FOR_ARRAY, AMBYTE_CALLFORRESET, 500, false); - if (ret == 1){ - this->data_fsm_state = DATA_STATUS::COMPLETED; - Serial.read(); - ESP_LOGV(TAG, "Data sent in %d ms", millis() - timer1); - return 0; - }else if (ret == 2){ - ESP_LOGE(TAG, "Ambyte asked for resending"); - return 0; - }else if (ret == 3){ - ESP_LOGE(TAG, "Ambyte calls for re-start"); - Serial.read(); - this->data_fsm_state = DATA_STATUS::WAKEUPCALLS; - return 0; - } - else{ - ESP_LOGE(TAG, "Unknown response"); - this->data_fsm_state = DATA_STATUS::WAKEUPCALLS; - return 0; - } -} - -// case 1: interrupt by ambyte, ambyte wait for 212 -// case 2: -int dataclass::fsm_send_waitesp(){ - flush_serial(10); - - unsigned int timer = millis(); - uint8_t ret = 0; - while (millis() - timer < 36000000){ - Serial.write(WAKE_AMBYTE); // write 211 - // wait ambyte ready response 210 - ret = serial_read_until(AMBYTE_AWAKE, AMBYTE_CALLS, AMBYTE_CALLFORRESET, 100, false); - if (ret == 1) break; - - } - - - -} -int dataclass::fsm_send_esp(uint8_t arr_idx){ - return this->fsm_send_esp(arr_idx, false); -} - -int dataclass::fsm_send_esp(uint8_t arr_idx, bool use_interrupt){ - if (!this->available) return 1; - int ret = 0; - bool running = true; - unsigned int timer1 = millis(); - - while ((running) && ((millis() - timer1) < 2000)){ - ret = 0; - switch (this->data_fsm_state) - { - case DATA_STATUS::WAKEUPCALLS: - ret = this->fsm_wake_up_calls(use_interrupt); - if (ret == 1) timer1 = millis(); - if (ret < 0) return ret; - break; - - case DATA_STATUS::LENGTHARRAY: - ret = this->fsm_send_length_info(arr_idx); - if (ret < 0) return ret; - break; - - case DATA_STATUS::SENDDATA: - ret = this->fsm_send_data(); - if (ret < 0) return ret; - break; - - case DATA_STATUS::COMPLETED: - running = false; - break; - default: - break; - } - } - ESP_LOGV(TAG, "DATA sending succuss in %d ms", millis() - timer1); - return 1; -} - - - - - -// int dataclass::send_esp(uint8_t arr_num){ -// int _tmp = 0; -// uint8_t ambyte_status, target = 0; -// unsigned int timer1 = 0; -// uint16_t tmp_var = dataclass::get_length(); -// if (tmp_var == 0) return 0; - -// //--- step one, wake up ambyte -// timer1 = millis(); -// ESP_LOGV(TAG, "Wake up calls"); -// Serial.write(211); -// for (uint8_t i = 0; i < 25; i++){ -// Serial.write(211); -// _tmp = read_until(210, 170, 0, 45, true); -// if (_tmp == 1){ -// ambyte_status = AMBYTE_STATUS::AWAKE; -// ESP_LOGV(TAG, "Wake response in %d ms", millis() - timer1); -// break; -// } -// } - -// if (ambyte_status != AMBYTE_STATUS::AWAKE){ -// ESP_LOGE(TAG, "Awake failed"); -// return -1; -// } - - -// //------------------------------ -// //--- step two, send array type and length -// // 150, array number, sizeH, sizeL, 0, checksum -// uint8_t arr[8] = {212, 150, arr_num, 0, 0, 0, 0, 0}; -// arr[3] = ((tmp_var >> 8) & 0xFF); -// arr[4] = (((tmp_var) & 0xFF)); -// for (uint8_t i = 0; i < 7; i++) arr[7] += arr[i]; - -// ESP_LOGV(TAG, "Send length"); -// _tmp = 0; -// for (uint8_t i = 0; i < 25; i++){ -// Serial.write(arr, 8); -// _tmp = read_until(200, 210, 222, 50, true); -// if (_tmp == 1){ -// ambyte_status = AMBYTE_STATUS::WAIT_FOR_DATA; -// break; -// }else if (_tmp == 2){ -// continue; -// }else if (_tmp == 3){ -// ESP_LOGE(TAG, "Ambyte askes for restart, TODO"); -// return -1; -// } - -// } - -// if (ambyte_status != AMBYTE_STATUS::WAIT_FOR_DATA){ -// ESP_LOGE(TAG, "Not available for data"); -// return -1; -// } - -// //-------------------------------- -// // --- step three, send data -// // 151, res, res, res 4 bytes array - -// for (uint8_t n = 0; n < 3; n++){ -// memset(arr, 0, sizeof(arr)); -// arr[0] = 212; -// timer1 = millis(); -// this->send(send_binary_array, &arr[3]); -// Serial.write(arr, 4); -// ESP_LOGV(TAG, "Spend %d ms sending %d data", millis() - timer1, tmp_var); -// _tmp = read_until(180, 200, 222, 50, true); -// Serial.read(); -// if (_tmp == 1) return 0; -// if (_tmp == 2) continue; -// if (_tmp == -1) return -1; -// } - -// return -1; -// } - - - -void dataclass::print_all(void){ - - if (!(dataclass::available)){ - ESP_LOGE(TAG, "Add failed, not initialized"); - return; - } - for (uint16_t i = 0; i < dataclass::_length; i++){ - Serial.print(dataclass::arr[i]); - Serial.print(","); - } - Serial.println(); - return; -} - - -// int wait_for_response_clear(const char* s, uint8_t slen, uint8_t timeout){ -// unsigned long timer1 = millis(); -// uint16_t n = 0; -// Serial.setTimeout(timeout); - -// if (Serial.find(s, slen)){ -// //ESP_LOGI(TAG, "Responsed in %d ms\n", millis() - timer1); -// while(Serial.available()){ -// Serial.read(); -// n += 1; -// } -// if (n > 0) ESP_LOGW(TAG, "received %d unexpected bytes after %s", n, s); -// return 0; -// } -// return -1; -// } - -// bool send_and_wait_rsp(const char *s, const char *r, uint8_t rlen, uint8_t timeout){ -// Serial.println(s); -// uint8_t i = wait_for_response_clear(r, rlen, timeout); -// if (i == 0) return true; -// return false; -// } - -// void write32(uint32_t v){ -// uint8_t a = (v) & 0xFF; -// uint8_t b = ((v) >> 8) & 0xFF; -// uint8_t c = ((v) >> 16) & 0xFF; -// uint8_t d = ((v) >> 24) & 0xFF; -// Serial.write(d); -// Serial.write(c); -// Serial.write(b); -// Serial.write(a); -// return; -// } - -// void send_data(uint32_t* arr, uint16_t len){ - -// char c[10]; - -// // Wake up sleep device -// if (send_and_wait_rsp("Wake!", "Ready", 5, 10)){ -// // send data size -// sprintf(c, "%d", len); -// if (send_and_wait_rsp(c, "GO", 2, 10)){ -// uint32_t checksum = 0; -// for (uint16_t n = 0; n < len; n++){ -// write32((uint32_t) arr[n]); -// checksum += arr[n]; -// } -// write32(checksum); -// send_and_wait_rsp("DONE", "Check", 5, 10); -// } -// } -// } - -/* - insert a number to an array with order - large number will insert towards the end - used for get median - @param arr: array with sorted data - @param length: length of the array - @param c: new data to be inserted -*/ -void sorted_insert(uint32_t arr[], uint16_t length, uint32_t c){ - uint16_t n = 0; - uint16_t nM = length - 1; - - while (n < nM){ - if (arr[n] == 0){ - arr[n] = c; - break; - } - else if (c < arr[n]){ - for (uint8_t g = nM; g > n; g--){ - arr[g] = arr[g - 1]; - } - arr[n] = c; - break; - } else if (arr[n + 1] == 0){ - arr[n + 1] = c; - break; - }else{ - n += 1; - } - } - return; -} - - -uint32_t calc_signal(const uint32_t dark, const uint32_t lit, const uint8_t num){ - if (lit + 250 < dark) return 0; - uint32_t a = (lit - dark + 250); - int32_t b = a + 98 - dark * 0.006 / num; - - //int32_t tmp_var = (lit - dark + 250) - (0.006 / p) * (dark - 16384UL * p); - if (b > 0) return b; - return 0; -} - diff --git a/do_c.cpp b/do_c.cpp deleted file mode 100644 index 7c5c69d..0000000 --- a/do_c.cpp +++ /dev/null @@ -1,229 +0,0 @@ -#include "src/wrench.h" -#include "src/mlx90632/u_mlx.h" -#include "src/as7341/spec_meas.h" -#include "PAM.h" -#define WR_MAX_ARR 640 -static const char* TAG = "DO_C"; - - - - -// local settings for c parser -static uint8_t pulsed_620_current = 110; -static uint8_t pulsed_720_current = 50, dc_current = 50; -static uint8_t gain_fluor = 1; -static uint8_t gain_fluref = 5, gain_720 = 5, gain_720ref = 1, gain_sun = 5, gain_leaf = 4; - - -// Run array operations --------------------------------------------- -uint8_t wr_run_arr[WR_MAX_ARR] = {0}; - -static void arr_reset(WRContext* c,const WRValue* argv,const int argn, WRValue& retVal, void* usr){ - if (argn == 1){ - uint8_t arr_num = (uint8_t) argv[0].asInt() - 1; - if ((arr_num == 0) || (arr_num > 7)) return; - for (uint8_t i = 0; i < WR_MAX_ARR; i++) wr_run_arr[i + arr_num * 64] = 0; - ESP_LOGV(TAG, "array %d reset", arr_num + 1); - }else{ - ESP_LOGE(TAG, "array reset with bad param"); - } -} - - - -static void arr_set(WRContext* c,const WRValue* argv,const int argn, WRValue& retVal, void* usr){ - // line num, linetype, sample num, frequency, actinic, subsampling - if (argn == 8){ - uint8_t arr_num = (uint8_t) argv[0].asInt() - 1; - uint8_t line_num = (uint8_t) argv[1].asInt() - 1; - uint8_t ir_on = (uint8_t) argv[2].asInt(); - uint8_t line_type = (uint8_t) argv[3].asInt(); - uint16_t sample_num = (uint16_t) argv[4].asInt(); - uint16_t freq = (uint16_t) argv[5].asInt(); - uint8_t actinic = (uint8_t) argv[6].asInt(); - uint8_t subsampling = (uint8_t) argv[7].asInt(); - ESP_LOGV(TAG,"Set arr%d, line %d to type %d with %d x %dHz samples, actinic:%d, sub:%d, IR:%d", arr_num, line_num, line_type, sample_num, freq, actinic, subsampling, ir_on); - - - if (arr_num > 7){ - ESP_LOGV(TAG, "arr number > 8"); - return; - } - - if (line_num > 7){ - ESP_LOGV(TAG, "line number > 8"); - return; - } - - uint16_t base_num = arr_num * 64 + line_num * 8; - - - wr_run_arr[base_num + 0] = (uint8_t) line_type; - wr_run_arr[base_num + 1] = (uint8_t) ir_on; - - wr_run_arr[base_num + 2] = (uint8_t) (sample_num >> 8); - wr_run_arr[base_num + 3] = (uint8_t) (sample_num & 0x00FF); - - wr_run_arr[base_num + 4] = (uint8_t) (freq >> 8); - wr_run_arr[base_num + 5] = (uint8_t) (freq & 0x00FF); - - wr_run_arr[base_num + 6] = actinic; - wr_run_arr[base_num + 7] = subsampling; - } - else{ - ESP_LOGE(TAG, "ARR_SET with length %d", argn); - } -} - -static void run(WRContext* c,const WRValue* argv,const int argn, WRValue& retVal, void* usr){ - if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1){ - conf_slow_FR_1(pulsed_620_current, pulsed_720_current, dc_current, gain_fluor, gain_fluref, gain_sun, gain_leaf, gain_720, gain_720ref); - adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; - } - - if (argn == 2){ - uint8_t arr_num = (uint8_t) argv[0].asInt() - 1; - bool led_persist = (bool) argv[1].asInt(); - if ((arr_num < 8)){ - run_arr_type1(8, wr_run_arr + arr_num * 64, led_persist); - } - else{ - ESP_LOGE(TAG, "run with bad arr num %d", arr_num + 1); - } - }else{ - ESP_LOGE(TAG, "BAD run array parameter"); - } -} - -// ultilities------------------------------------------------------ -static void print( WRContext* c, const WRValue* argv, const int argn, WRValue& retVal, void* usr ) -{ - char buf[1024]; - - for( int i=0; i 0) pulsed_620_current = (uint8_t)argv[0].asInt(); - if (argn > 1) pulsed_720_current = (uint8_t)argv[1].asInt(); - if (argn > 2) dc_current = (uint8_t)argv[2].asInt(); - ESP_LOGV(TAG, "set pulsed-620 to: %d, set pulsed-720 to: %d, set far_red to: %d", pulsed_620_current, pulsed_720_current, dc_current); - return; -} - -static void set_gains(WRContext* c,const WRValue* argv,const int argn, WRValue& retVal, void* usr){ - if (argn == 0) return; - if (argn > 0) gain_fluor = (uint8_t)argv[0].asInt(); - if (argn > 1) gain_fluref = (uint8_t)argv[1].asInt(); - if (argn > 2) gain_720 = (uint8_t)argv[2].asInt(); - if (argn > 3) gain_720ref = (uint8_t)argv[3].asInt(); - if (argn > 4) gain_sun = (uint8_t)argv[4].asInt(); - if (argn > 5) gain_leaf = (uint8_t)argv[5].asInt(); - ESP_LOGV(TAG, "Fluor Gain:%d, Fluo ref Gain:%d, 730 Gain:%d, 730ref Gain:%d, Sun Gain:%d, Leaf Gain:%d", gain_fluor, gain_fluref, gain_720, gain_720ref, gain_sun, gain_leaf); - return; -} - -static void run_mpf(WRContext* c,const WRValue* argv,const int argn, WRValue& retVal, void* usr){ - if (argn == 2){ - uint8_t mode = (uint8_t)argv[0].asInt(); - uint8_t actinic = (uint8_t)argv[1].asInt(); - MPF(mode, pulsed_620_current, actinic, gain_fluor, gain_fluref); - adpd_mode = ADPD_CONFIG_MODE::MPF_MODE; - }else{ - ESP_LOGE(TAG, "MPF, Get: %d of arguments", argn); - } -} - -static void idle(WRContext* c,const WRValue* argv,const int argn, WRValue& retVal, void* usr){ - AS_LED_OFF(); -} - - - -void do_c(const char* c){ - - WRState* w = wr_newState(); // create the state - wr_loadMathLib( w ); - - wr_registerFunction( w, "print", print ); // bind a function - wr_registerFunction( w, "disp", disp ); // bind a function - - wr_registerFunction( w, "get_par", wr_get_par ); // bind a function - wr_registerFunction( w, "get_temp", wr_get_leaf_temp ); // bind a function - - wr_registerFunction( w, "reset", arr_reset ); // bind a function - wr_registerFunction( w, "config", detector_preset); // bind a function - wr_registerFunction( w, "set_arr", arr_set ); // bind a function - wr_registerFunction( w, "run", run ); // bind a function - wr_registerFunction( w, "set_currents", set_currents ); // bind a function - wr_registerFunction( w, "set_gains", set_gains ); // bind a function - wr_registerFunction( w, "run_mpf", run_mpf ); // bind a function - - - - - - unsigned char* outBytes; // compiled code is alloc'ed - int outLen; - - int err = wr_compile( c, strlen(c), &outBytes, &outLen ); // compile it - if ( err == 0 ) - { - wr_run( w, outBytes, outLen ); // load and run the code! - delete[] outBytes; // clean up - } - - wr_destroyState( w ); - Serial.println("Cmd Done!"); - - - -} diff --git a/dual_ambit_v2.ipynb b/dual_ambit_v2.ipynb deleted file mode 100644 index 66cb6a0..0000000 --- a/dual_ambit_v2.ipynb +++ /dev/null @@ -1,428 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "582b1998", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Available ports: ['COM6']\n" - ] - } - ], - "source": [ - "import numpy as np\n", - "import serial\n", - "import time\n", - "import matplotlib.pyplot as plt\n", - "import concurrent.futures\n", - "import serial.tools.list_ports\n", - "import pandas as pd\n", - "import os\n", - "import re\n", - "from copy import deepcopy\n", - "\n", - "ports = serial.tools.list_ports.comports()\n", - "ambit_ports = [x[0] for x in ports if \"CH9102\" in x[1]]\n", - "print(\"Available ports:\", ambit_ports)\n", - "import logging\n", - "logger = logging.getLogger(\"serial\")\n", - "logger.setLevel(logging.INFO)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "2ea33ec5", - "metadata": {}, - "outputs": [], - "source": [ - "def readline_from_port(ser: serial.Serial, init_wait: float = 0.25, line_wait: float = 0.5) -> tuple[str, bool]:\n", - " '''\n", - " Read a single line from the serial port. Waits for data to be available before reading. Timeout if no data is received within the specified wait times.\n", - " Parameters:\n", - " ser: serial.Serial\n", - " The serial port to read from.\n", - " init_wait: float\n", - " The initial wait time for data to be available.\n", - " line_wait: float\n", - " The wait time for the rest of the line to be available.\n", - " Returns:\n", - " tuple[str, bool]\n", - " str: The line read from the serial port.\n", - " bool: True if more data is available to read, False otherwise.\n", - " '''\n", - " # initial wait\n", - " _t = time.perf_counter()\n", - " while ser.in_waiting == 0:\n", - " if time.perf_counter() - _t > init_wait + .05:\n", - " return \"\", False\n", - " time.sleep(0.025)\n", - "\n", - " _str:str = \"\"\n", - " _t = time.perf_counter()\n", - "\n", - "\n", - " while True:\n", - " if ser.in_waiting > 0:\n", - " \n", - " c = ser.read()\n", - " if (c == bytes([0])) or (c > bytes([127])):\n", - " continue \n", - " \n", - " _t = time.perf_counter()\n", - " c = c.decode('ascii')\n", - " _str += c\n", - " if c == '\\n':\n", - " return _str, ser.in_waiting > 0\n", - "\n", - " elif time.perf_counter() - _t > line_wait + .05:\n", - " return _str, False\n", - " else:\n", - " time.sleep(0.04)\n", - "\n", - " return _str, False\n", - "\n", - "\n", - "def readlines_from_port(ser: serial.Serial, kw_stop = True, **argv) -> tuple[list[str], int]:\n", - " \n", - "\n", - " line_to_line_wait = argv.get(\"line_to_line_wait\", 2.0)\n", - " chuck_time = argv.get(\"chuck_time\", 999)\n", - " chuck_size = argv.get(\"chuck_size\", 10)\n", - " timeout = argv.get(\"timeout\", -1)\n", - " ret_int = 0\n", - "\n", - " lines: list[str] = []\n", - " _t0_start = time.perf_counter()\n", - " kw = [\"NEW Name Here Ready\", \"Done\"]\n", - " if timeout > 0: \n", - " _time_quick = _t0_start + timeout\n", - " else:\n", - " _time_quick = -1\n", - "\n", - " _t_start = time.perf_counter()\n", - " _counter = 0\n", - " while True:\n", - " ret, more = readline_from_port(ser, init_wait=argv.get(\"init_wait\", 0.25))\n", - " if ret != \"\":\n", - " lines.append(ret)\n", - " _t_start = time.perf_counter()\n", - " _counter += 1\n", - "\n", - " if kw_stop:\n", - " if ret.strip() in kw:\n", - " logger.info(f\"Found stop keyword: {ret.strip()}\")\n", - " ret_int = -1\n", - " break\n", - "\n", - " # premature stops\n", - " if ((time.perf_counter() - _t0_start) > chuck_time) or (_counter >= chuck_size):\n", - " ret_int = 2\n", - " # logger.info(f\"prematured chunk{time.perf_counter() - _t0_start} {_counter}\")\n", - " break\n", - "\n", - " if more:\n", - " ret_int = 1\n", - " continue\n", - "\n", - " # timeout\n", - " if (time.perf_counter() - _t_start) > line_to_line_wait:\n", - " # logger.info(f\"line to line timeout{(time.perf_counter() - _t_start)}\")\n", - " ret_int = 0\n", - " break\n", - "\n", - " if _time_quick > 0 and (time.perf_counter() > _time_quick):\n", - " # logger.info(f\"total timeout{(time.perf_counter() - _t0_start)}\")\n", - " ret_int = 0\n", - " break\n", - " \n", - " return lines, ret_int\n", - "\n", - "\n", - "parse_data_line = re.compile(r'T:([\\-\\.0-9]+),F:([\\.0-9]+),S:([0-9]+),R:([0-9]+),Sun:([0-9]+),L:([0-9]+)\\n')\n", - "def parse_data(lines:list[str])->np.ndarray:\n", - " _data = []\n", - " for line in lines:\n", - " match = parse_data_line.match(line)\n", - " if match:\n", - " _data.append(np.fromiter(match.groups(), dtype=float))\n", - " return np.array(_data)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "c21e5806", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(['NEW Name Here Ready\\r\\n'], -1)\n" - ] - } - ], - "source": [ - "with serial.Serial(ambit_ports[0], 115200, timeout=1) as ser1:\n", - " ser1.write((\"hello\"+'\\n\\r').encode())\n", - " print(readlines_from_port(ser1, True))" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "68711e7d", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib qtagg" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "f8c5507c", - "metadata": {}, - "outputs": [], - "source": [ - "class Trace():\n", - " cmd_str: str \n", - " run_seq: list[list[int]]\n", - " _cmd_arr: np.ndarray\n", - " mea_tml: np.ndarray\n", - " mea_act: np.ndarray\n", - " label: str\n", - "\n", - "\n", - "\n", - " timeline: np.ndarray\n", - " data: np.ndarray\n", - " time_start: float\n", - " time_end: float\n", - " time_t0:float\n", - "\n", - " \n", - " def __init__(self, label:str = \"\", led_persist:bool = False):\n", - " self.run_seq = []\n", - " self.data = np.array([])\n", - " self._led_persist = led_persist\n", - " self.label = label\n", - "\n", - " def add_line(self, num:int, freq:int, actinic:int = 0):\n", - " self.run_seq.append([num, freq, actinic])\n", - " self.compile(self._led_persist)\n", - " self.calc_timeline()\n", - "\n", - " def compile(self, persist:bool = False):\n", - " run_seq = []\n", - " for line in self.run_seq:\n", - " num, freq, actinic = line\n", - " run_seq.append([2, 0, num//256, num%256, freq//256, freq%256, actinic, 1])\n", - "\n", - " self._cmd_arr = np.reshape(run_seq, (-1, 8))\n", - " arr_length = self._cmd_arr.shape[0]\n", - " string = np.array2string(self._cmd_arr.flatten(), separator=',')[1:-1].replace(\" \", \"\")\n", - " self.cmd_str = \"arrun1,\" + str(arr_length) + \",\" + str(int(persist)) + \",\"+ string + \",\\n\"\n", - "\n", - " def calc_timeline(self):\n", - " _t, mea_tml, mea_act = 0, [], []\n", - " for _n, _f, _a in self.run_seq:\n", - " mea_tml.append(np.arange(_n) / _f + _t)\n", - " mea_act.append([_a] * _n)\n", - " _t = mea_tml[-1][-1]\n", - " self.mea_tml = np.concatenate(mea_tml).astype(np.float64) * 0.854\n", - " self.mea_act = np.concatenate(mea_act).astype(np.int8)\n", - "\n", - " def copy(self):\n", - " t_new = Trace()\n", - " t_new.run_seq = self.run_seq.copy()\n", - " t_new._cmd_arr = self._cmd_arr.copy()\n", - " t_new.cmd_str = self.cmd_str\n", - " t_new.mea_tml = self.mea_tml.copy()\n", - " t_new.mea_act = self.mea_act.copy()\n", - " return t_new \n", - " \n", - " \n", - "\n", - "\n", - "t1 = Trace()\n", - "t1.add_line(50, 2, 0)\n", - "# t1.add_line(50, 20, 50)\n", - "t1.add_line(50, 2, 0)\n", - "\n", - "t1.compile()\n", - "t1.calc_timeline()\n", - "# print(t1.mea_tml)\n", - "# print(t1.mea_act)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "d2c877b2", - "metadata": {}, - "outputs": [], - "source": [ - "result_lists = []" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "35db8f44", - "metadata": {}, - "outputs": [], - "source": [ - "SAT_INTENSITY = 240\n", - "ACT_INTENSITY = 10\n", - "sat_Flash_0 = Trace(\"sat\", False)\n", - "sat_Flash_0.add_line(20, 10, 0) # baseline\n", - "sat_Flash_0.add_line(100, 100, SAT_INTENSITY) # 1s Sat\n", - "sat_Flash_0.add_line(20, 100, 0) # decay1\n", - "sat_Flash_0.add_line(20, 10, 0) # decay2\n", - "\n", - "\n", - "\n", - "Steady_state_0 = Trace(\"SS0\", False)\n", - "Steady_state_0.add_line(10, 5, 0)\n", - "\n", - "Steady_state_1 = Trace(\"SS1\", False)\n", - "Steady_state_1.add_line(100, 5, 0)\n", - "\n", - "qE_induction_0 = Trace(\"qE induction\", True)\n", - "qE_induction_0.add_line(40, 20, ACT_INTENSITY) # 2s\n", - "qE_induction_0.add_line(100, 100, SAT_INTENSITY) # 1s\n", - "qE_induction_0.add_line(40, 20, ACT_INTENSITY) # 2s\n", - "\n", - "\n", - "qE_relaxation_0 = Trace(\"qE relaxation\", False)\n", - "qE_relaxation_0.add_line(40, 10, 0) # 4s\n", - "qE_relaxation_0.add_line(100, 100, SAT_INTENSITY) # 2s\n", - "qE_relaxation_0.add_line(40, 5, 0) # 8s\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "5a0b766f", - "metadata": {}, - "outputs": [], - "source": [ - "runlist = [Steady_state_0.copy()] + [qE_induction_0.copy() for _ in range(3)] + [qE_relaxation_0.copy() for _ in range(3)]" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "7cb7233d", - "metadata": {}, - "outputs": [], - "source": [ - "runlist = [sat_Flash_0, Steady_state_1.copy(), Steady_state_1.copy()]" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "24ab24f0", - "metadata": {}, - "outputs": [], - "source": [ - "fig, ax = plt.subplots(1, 1, figsize=(8,4))\n", - "t0 = time.time()\n", - "y_range = [np.inf, -np.inf]\n", - "with serial.Serial(ambit_ports[0], 115200) as ser1:\n", - " for trace in runlist:\n", - " ser1.write((trace.cmd_str+'\\n\\r').encode())\n", - " trace.time_start = time.time()\n", - " current_trace = ax.plot([], [])[0]\n", - " x1 = trace.mea_tml + trace.time_start - t0\n", - " ax.set_xlim(-1, x1[-1])\n", - " _data = []\n", - " while True:\n", - " lines, status = readlines_from_port(ser1, True, chuck_time = 1, line_to_line_wait=1)\n", - " if len(lines) > 0:\n", - " _line = parse_data(lines)\n", - " if len(_line) > 0:\n", - " _data.append(_line)\n", - " y1 = np.vstack(_data)[:, 1]\n", - " current_trace.set_data(x1[:y1.shape[0]], y1)\n", - " if min(y1) < y_range[0]: y_range[0] = min(y1)\n", - " if max(y1) > y_range[1]: y_range[1] = max(y1)\n", - " ax.set_ylim(*y_range)\n", - " fig.canvas.draw()\n", - " fig.canvas.flush_events() #\n", - " if status < 0: break\n", - " data = np.vstack(_data)\n", - " trace.data = data\n", - "result_lists.append(deepcopy(runlist))" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "6c61842e", - "metadata": {}, - "outputs": [], - "source": [ - "for trace in runlist:\n", - " plt.plot(trace.mea_tml + trace.time_start, trace.data[:,1])" - ] - }, - { - "cell_type": "code", - "execution_count": 199, - "id": "9c5e3a0b", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "for res in result_lists:\n", - " x, y = [], []\n", - " for trace in res:\n", - " x.append(trace.mea_tml + trace.time_start)\n", - " y.append(trace.data[:,1])\n", - " x = np.concatenate(x)\n", - " y = np.concatenate(y)\n", - " plt.plot(x - x[0], y)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "afeb1d3e", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "conn_env (3.13.12)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/include/README b/include/README new file mode 100644 index 0000000..49819c0 --- /dev/null +++ b/include/README @@ -0,0 +1,37 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the convention is to give header files names that end with `.h'. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..9379397 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into the executable file. + +The source code of each library should be placed in a separate directory +("lib/your_library_name/[Code]"). + +For example, see the structure of the following example libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +Example contents of `src/main.c` using Foo and Bar: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +The PlatformIO Library Dependency Finder will find automatically dependent +libraries by scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/lib/openjii_proto/openjii_proto.cpp b/lib/openjii_proto/openjii_proto.cpp new file mode 100644 index 0000000..110dea5 --- /dev/null +++ b/lib/openjii_proto/openjii_proto.cpp @@ -0,0 +1,311 @@ +// openjii_proto implementation — see openjii_proto.h and +// CommunicationProtocolOpenJIISerial.md. +#include "openjii_proto.h" +#include + +namespace ojii { +namespace { + +constexpr const char* FRAME_TOKEN = "7A1E3AA1"; // §7.2 (reserved for a checksum) +constexpr size_t RX_MAX = 2048; // §4 +constexpr uint32_t JSON_IDLE_MS = 1000; // §4 +constexpr size_t MAX_CMDS = 48; + +struct Entry { const char* name; SnapshotFn fn; }; +Entry s_cmds[MAX_CMDS]; +size_t s_ncmds = 0; +struct SEntry { const char* name; StreamFn fn; }; +SEntry s_streams[MAX_CMDS]; +size_t s_nstreams = 0; +Identity s_id = { "device", "0", "0" }; + +// receiver state +String s_rx; +enum class Mode { UNKNOWN, LINE, JSON }; +Mode s_mode = Mode::UNKNOWN; +int s_brace = 0, s_bracket = 0; +bool s_inStr = false; +char s_prev = 0; +uint32_t s_last_byte_ms = 0; + +void reset_rx() { + s_rx = ""; + s_mode = Mode::UNKNOWN; + s_brace = 0; s_bracket = 0; s_inStr = false; s_prev = 0; +} + +SnapshotFn find(const String& name) { + for (size_t i = 0; i < s_ncmds; i++) + if (name == s_cmds[i].name) return s_cmds[i].fn; + return nullptr; +} + +StreamFn find_stream(const String& name) { + for (size_t i = 0; i < s_nstreams; i++) + if (name == s_streams[i].name) return s_streams[i].fn; + return nullptr; +} + +// Split a label into command name + comma-args ("set_currents,1,2,3" -> "set_currents","1,2,3"). +void split_label(const String& label, String& cmd, String& args) { + int comma = label.indexOf(','); + if (comma < 0) { cmd = label; args = ""; } + else { cmd = label.substring(0, comma); args = label.substring(comma + 1); } +} + +// ── errors (§9) ───────────────────────────────────────────────────────────── +void err(Print& out, const char* code, const char* k1 = nullptr, const String& v1 = String(), + const char* k2 = nullptr, const String& v2 = String()) { + out.print("{\"error\":\""); out.print(code); out.print('"'); + if (k1) { out.print(",\""); out.print(k1); out.print("\":\""); out.print(v1); out.print('"'); } + if (k2) { out.print(",\""); out.print(k2); out.print("\":\""); out.print(v2); out.print('"'); } + out.print("}\n"); +} + +// ── serialisers (§5.2 / §11) ──────────────────────────────────────────────── +bool bare_string_ok(const char* s) { + // A bare LINE string must not contain '\n' or edge whitespace (§8). + size_t n = strlen(s); + if (n == 0) return true; + if (s[0] == ' ' || s[n - 1] == ' ') return false; + for (size_t i = 0; i < n; i++) if (s[i] == '\n' || s[i] == '\r') return false; + return true; +} + +// LINE serialiser: scalar -> bare, container -> strict JSON. No trailing newline. +void render_line_node(JsonVariantConst node, Print& out) { + if (node.is() || node.is()) { + serializeJson(node, out); + } else if (node.is()) { + const char* s = node.as(); + if (bare_string_ok(s)) out.print(s); // bare (unquoted) + else serializeJson(node, out); // fall back to quoted JSON string + } else { + serializeJson(node, out); // number / bool / null -> bare + } +} + +// ── path resolver (§6) ────────────────────────────────────────────────────── +// Resolve `path` against `root`, render the result (LINE) or a §9 error. +// `full` is the whole request line (echoed in error "path"). +void resolve_and_render(JsonVariantConst root, const String& path, const String& full, Print& out) { + JsonVariantConst node = root; + int start = 0; + while (true) { + int dot = path.indexOf('.', start); + String seg = (dot < 0) ? path.substring(start) : path.substring(start, dot); + + // trailing method, e.g. keys() + if (seg.endsWith("()")) { + String method = seg.substring(0, seg.length() - 2); + if (method == "keys") { + if (!node.is()) { err(out, "not_a_container", "path", full, "at", seg); return; } + JsonDocument tmp; JsonArray arr = tmp.to(); + for (JsonPairConst kv : node.as()) arr.add(kv.key().c_str()); + serializeJson(tmp, out); out.print('\n'); return; + } + err(out, "bad_path", "path", full); return; + } + + if (node.is()) { + JsonObjectConst obj = node.as(); + JsonVariantConst child = obj[seg]; + if (child.isNull()) { // absent key (present-null is not used in payloads) + // build the available-keys list + String avail = "["; + bool f = true; + for (JsonPairConst kv : obj) { if (!f) avail += ','; avail += '"'; avail += kv.key().c_str(); avail += '"'; f = false; } + avail += ']'; + out.print("{\"error\":\"no_such_key\",\"path\":\""); out.print(full); + out.print("\",\"at\":\""); out.print(seg); + out.print("\",\"available\":"); out.print(avail); out.print("}\n"); + return; + } + node = child; + } else if (node.is()) { + // segment must be a non-negative index + bool numeric = seg.length() > 0; + for (size_t i = 0; i < seg.length(); i++) if (!isdigit((unsigned char)seg[i])) numeric = false; + JsonArrayConst arr = node.as(); + long idx = numeric ? seg.toInt() : -1; + if (!numeric || idx < 0 || (size_t)idx >= arr.size()) { err(out, "index_out_of_range", "path", full, "at", seg); return; } + node = arr[idx]; + } else { + // scalar — cannot descend + err(out, "not_a_container", "path", full, "at", seg); return; + } + + if (dot < 0) break; + start = dot + 1; + } + render_line_node(node, out); + out.print('\n'); +} + +// ── LINE dispatch (§5) ────────────────────────────────────────────────────── +void handle_line(const String& line, Print& out) { + int dot = line.indexOf('.'); + if (dot >= 0) { // query form: cmd.path + String cmd = line.substring(0, dot); + SnapshotFn fn = find(cmd); + if (!fn) { err(out, "unknown_command", "path", line); return; } + JsonDocument doc; JsonVariant root = doc.to(); + fn("", root); + resolve_and_render(doc.as(), line.substring(dot + 1), line, out); + } else { // action form: cmd[,args] + int comma = line.indexOf(','); + String cmd = (comma < 0) ? line : line.substring(0, comma); + String args = (comma < 0) ? String("") : line.substring(comma + 1); + StreamFn sfn = find_stream(cmd); + if (sfn) { sfn(args, out); out.print('\n'); return; } + SnapshotFn fn = find(cmd); + if (!fn) { err(out, "unknown_command", "path", line); return; } + JsonDocument doc; JsonVariant root = doc.to(); + fn(args, root); + render_line_node(doc.as(), out); out.print('\n'); + } +} + +// ── JSON envelope (§7) ────────────────────────────────────────────────────── +// Find the command list: _protocol_set_, an array of {set:[...]}, a bare array, +// or the first array-valued member of an object (§7.1, lenient). +JsonArrayConst extract_list(JsonVariantConst req) { + if (req["_protocol_set_"].is()) return req["_protocol_set_"].as(); + if (req.is()) { + JsonArrayConst arr = req.as(); + for (JsonObjectConst o : arr) if (o["set"].is()) return o["set"].as(); + return arr; // bare array used directly + } + if (req.is()) + for (JsonPairConst kv : req.as()) + if (kv.value().is()) return kv.value().as(); + return JsonArrayConst(); +} + +// Emit one command's payload as strict JSON (one flat `set` element, §7.2). +void emit_payload_json(const String& label, Print& out) { + String cmd, args; split_label(label, cmd, args); + StreamFn sfn = find_stream(cmd); + if (sfn) { sfn(args, out); return; } // measurement writes its own JSON value + SnapshotFn fn = find(cmd); + if (!fn) { out.print("{\"error\":\"unknown_command\",\"path\":\""); out.print(label); out.print("\"}"); return; } + JsonDocument doc; JsonVariant root = doc.to(); + fn(args, root); + serializeJson(doc, out); // strict JSON, all strings quoted +} + +void handle_json(const String& doc_str, Print& out) { + JsonDocument req; + DeserializationError e = deserializeJson(req, doc_str); + if (e) { err(out, "json_parse", "detail", e.c_str()); return; } + + JsonArrayConst list = extract_list(req); + + out.print("{\"device_name\":\""); out.print(s_id.device); + out.print("\",\"device_version\":\""); out.print(s_id.version); + out.print("\",\"device_battery\":0,\"device_firmware\":\""); out.print(s_id.firmware); + out.print("\",\"sample\":[{\"protocol_id\":\"NaN\",\"set\":["); + bool first = true; + for (JsonObjectConst entry : list) { + const char* label = entry["label"]; + if (!label) continue; // §7.1 entries without a label are skipped + uint16_t repeats = entry["protocol_repeats"] | 1; + if (repeats == 0) repeats = 1; + for (uint16_t r = 0; r < repeats; r++) { // flat: N consecutive elements (§7.2) + if (!first) out.print(','); + first = false; + emit_payload_json(String(label), out); + } + } + out.print("]}]}"); out.print(FRAME_TOKEN); out.print('\n'); +} + +} // namespace + +// ── public API ────────────────────────────────────────────────────────────── +void on(const char* name, SnapshotFn fn) { + if (s_ncmds < MAX_CMDS) s_cmds[s_ncmds++] = { name, fn }; +} + +void on_stream(const char* name, StreamFn fn) { + if (s_nstreams < MAX_CMDS) s_streams[s_nstreams++] = { name, fn }; +} + +void identity(const Identity& id) { s_id = id; } + +// Mid-frame == a mode is locked. Buffered bytes alone must NOT count: with no +// mode locked the buffer can only hold whitespace (anything else would have +// locked a mode), and treating that as busy stalled the caller's router forever. +bool busy(void) { return s_mode != Mode::UNKNOWN; } + +void reset(void) { reset_rx(); } + +void poll(Stream& io) { + while (io.available() > 0) { + char c = (char)io.read(); + if (c == '\r') continue; // §4: CR ignored + + if (s_mode == Mode::UNKNOWN) { + // drop leading framing/boot junk before a mode is locked. A '\n' is only + // meaningful once a request has started: buffering a leading one selects + // no mode (it is whitespace) yet leaves rx non-empty, which used to make + // busy() true forever and stall the caller's router. Clients terminate + // envelopes with '\n' (the openJII driver probes with + // '{"command":"INFO"}\n'), so this is the common path, not an edge case. + unsigned char uc = (unsigned char)c; + bool printable = (uc >= 0x20 && uc < 0x7F); + if (!printable && !(c == '\n' && s_rx.length() > 0)) continue; + } else if ((unsigned char)c > 0x7F) { + // Both dialects are 7-bit, so a high byte mid-frame means the binary host + // has taken the wire (an ambyte wake burst). Abandon the partial frame and + // hand the stream back instead of absorbing the burst: every absorbed byte + // refreshed the idle timer, so the companion's ~1.25 s retry window + // expired before the timeout could fire. Dropping this one byte is safe - + // the wake is sent in triplicate and retried. + reset_rx(); + return; + } + + s_rx += c; + s_last_byte_ms = millis(); + + if (s_mode == Mode::UNKNOWN) { // §4: first non-ws byte selects mode + int i = 0; + while (i < (int)s_rx.length() && isspace((unsigned char)s_rx[i])) i++; + if (i < (int)s_rx.length()) + s_mode = (s_rx[i] == '{' || s_rx[i] == '[') ? Mode::JSON : Mode::LINE; + } + + if (s_mode == Mode::LINE) { + if (c == '\n') { + s_rx.trim(); + if (s_rx.length() > 0) { handle_line(s_rx, io); io.flush(); } // empty line -> no response (§4) + reset_rx(); + } + } else if (s_mode == Mode::JSON) { // string-aware brace/bracket depth + if (c == '"' && s_prev != '\\') s_inStr = !s_inStr; + if (!s_inStr) { + if (c == '{') s_brace++; else if (c == '}') s_brace--; + else if (c == '[') s_bracket++; else if (c == ']') s_bracket--; + } + s_prev = c; + if (!s_inStr && s_brace == 0 && s_bracket == 0) { + s_rx.trim(); + if (s_rx.length() > 0) { handle_json(s_rx, io); io.flush(); } + reset_rx(); + } + } + + if (s_rx.length() > RX_MAX) { io.print("{\"error\":\"rx_overflow\"}\n"); reset_rx(); } + } + + // §4: incomplete request idle > 1 s. Applies to any locked mode, not just + // JSON: a stalled LINE frame would otherwise hold the stream indefinitely + // (there is no other LINE timeout). + if (s_mode != Mode::UNKNOWN && s_rx.length() > 0 && (millis() - s_last_byte_ms) > JSON_IDLE_MS) { + if (s_mode == Mode::JSON) io.print("{\"error\":\"json_timeout\"}\n"); + reset_rx(); + } +} + +} // namespace ojii diff --git a/lib/openjii_proto/openjii_proto.h b/lib/openjii_proto/openjii_proto.h new file mode 100644 index 0000000..65a08c9 --- /dev/null +++ b/lib/openjii_proto/openjii_proto.h @@ -0,0 +1,65 @@ +// openjii_proto — device-agnostic openJII serial protocol (LINE + JSON envelope). +// +// Implements CommunicationProtocolOpenJIISerial.md: first-byte mode detection, +// LINE mode (bare scalars / JSON containers, dot-path queries, keys()), and the +// JSON "openJII envelope" (flat `set`, frame token), with the §9 error taxonomy. +// +// A device registers per-command SNAPSHOT handlers that build a value tree +// (command-as-root); the framework owns the serializers, the path resolver, the +// envelope writer, and the receiver. Measurement/STREAM commands (one streamed +// JSON value per envelope position) are a separate API added once D6 is settled. +// +// Single-image note: the module no longer owns the whole read loop. The device's +// main loop routes by first byte and feeds poll() only printable traffic (the +// binary FSM path must never reach it — poll() consumes bytes and silently drops +// >127 junk while unlocked). busy()/reset() exist for that router: busy() says an +// envelope is mid-frame and still owns the stream; reset() drops any stale +// partial input before the stream is handed to another protocol. +#pragma once + +#include +#include + +namespace ojii { + +// Snapshot handler: populate `root` as this command's command-as-root payload +// (§2.1). `args` is the comma-separated remainder after the command name +// ("set_currents,1,2,3" -> "1,2,3"; "" if none). On error set root["error"] +// to a §9 code (the framework still serialises whatever the handler produced). +using SnapshotFn = void (*)(const String& args, JsonVariant root); + +// Stream/measurement handler: write ONE well-formed JSON value directly to `out` +// (for commands whose output is too large to buffer as a tree, e.g. a run). In an +// envelope it occupies one `set` slot; in LINE mode it is the bare response. +using StreamFn = void (*)(const String& args, Print& out); + +struct Identity { + const char* device; // e.g. "Ambit" -> hello.device, envelope device_name + const char* version; // e.g. "0.1.0" -> envelope device_version + const char* firmware; // e.g. "0.1.0" -> envelope device_firmware +}; + +// Register a snapshot command (name is matched case-sensitively). +void on(const char* name, SnapshotFn fn); + +// Register a stream/measurement command. +void on_stream(const char* name, StreamFn fn); + +// Set device identity used by the envelope header and the built-in hello. +void identity(const Identity& id); + +// Drive the protocol: consume available bytes from `io`, and when a full request +// is framed, dispatch it and write the response to `io`. Call every loop(). +void poll(Stream& io); + +// True while a request is mid-frame (mode locked or bytes buffered). While busy, +// the receiver owns the stream: keep calling poll() (it also fires the JSON idle +// timeout) and do not route bytes elsewhere. +bool busy(void); + +// Drop any partial input and unlock the mode. Call before handing the stream to +// another protocol (e.g. the binary FSM path) so a stale fragment can't prepend +// to the next text command. +void reset(void); + +} // namespace ojii diff --git a/nvs1.cpp b/nvs1.cpp deleted file mode 100644 index 5e0d744..0000000 --- a/nvs1.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "nvs1.h" -#include "src/mlx90632/u_mlx.h" -#include - -extern Preferences preferences; -struct ambit_calibration_info_t ambit_calibration_local, ambit_calibration_income; -struct ambit_FW_info_t ambit_FW_info; -struct metadata_t metadata_epprom, metadata_incoming; - - - -static void get_FW_info(void){ - ambit_FW_info.MAC = ESP.getEfuseMac(); - ambit_FW_info.Size = ESP.getSketchSize(); - strncpy(ambit_FW_info.FW_date, __DATE__, 12); - ambit_FW_info.Checksum = 0xFF & (MAJOR_VERSION << 4) & (MINOR_VERSION << 2) & (BATCH_VERSION << 0); - - return; -} - - -static void load_metadata(void){ - preferences.begin("metadata", true); - if (preferences.isKey("lon")) metadata_epprom.lon = preferences.getDouble("lon", 1.0); - if (preferences.isKey("lat")) metadata_epprom.lat = preferences.getDouble("lat", 1.0); - if (preferences.isKey("alt")) metadata_epprom.alt = preferences.getFloat("alt", 1.0); - if (preferences.isKey("time")) metadata_epprom.time = preferences.getUInt("time", 1); - if (preferences.isKey("acc")) metadata_epprom.acc = preferences.getFloat("acc", 1.0); - if (preferences.isKey("vacc")) metadata_epprom.vacc = preferences.getFloat("vacc", 1.0); - if (preferences.isKey("info1")) preferences.getString("info1", metadata_epprom.info1, 200); - if (preferences.isKey("x")) metadata_epprom.x = preferences.getFloat("x", 0.0); - if (preferences.isKey("y")) metadata_epprom.y = preferences.getFloat("y", 0.0); - if (preferences.isKey("z")) metadata_epprom.z = preferences.getFloat("z", 0.0); - preferences.end(); - memcpy(&metadata_incoming, &metadata_epprom, sizeof(metadata_t)); - return; -} - -void save_metadata(void){ - //QC - if (metadata_incoming.lon < -180.0 || metadata_incoming.lon > 360.0) return; - if (metadata_incoming.lat < -90.0 || metadata_incoming.lat > 90.0) return; - if (metadata_incoming.alt < -500.0 || metadata_incoming.alt > 30000.0) return; - // Save metadata to NVS - preferences.begin("metadata", false); - preferences.putDouble("lon", metadata_incoming.lon); - preferences.putDouble("lat", metadata_incoming.lat); - preferences.putFloat("alt", metadata_incoming.alt); - preferences.putUInt("time", metadata_incoming.time); - preferences.putFloat("acc", metadata_incoming.acc); - preferences.putFloat("vacc", metadata_incoming.vacc); - preferences.putString("info1", metadata_incoming.info1); - preferences.putFloat("x", metadata_incoming.x); - preferences.putFloat("y", metadata_incoming.y); - preferences.putFloat("z", metadata_incoming.z); - preferences.end(); - return; -} - - -static void load_calibration_info(){ - preferences.begin("config", true); - if (preferences.isKey("actinic")) ambit_calibration_local.actinic_coef = preferences.getFloat("actinic", 0.1); - if (preferences.isKey("spec")) ambit_calibration_local.spec_coef = preferences.getFloat("spec", 1.0); - if (preferences.isKey("emit")) ambit_calibration_local.mlx_emissivity = preferences.getDouble("emit", 1.0); - if (preferences.isKey("sun")) ambit_calibration_local.sun_coef = preferences.getFloat("sun", 1.0); - if (preferences.isKey("name")) preferences.getString("name", ambit_calibration_local.ambit_name, 20); - if (preferences.isKey("temp_offset")) ambit_calibration_local.temp_offset = preferences.getFloat("temp_offset", 0.0); - if (preferences.isKey("temp_slope")) ambit_calibration_local.temp_slope = preferences.getFloat("temp_slope", 1.0); - - if (preferences.isKey("act_50")) ambit_calibration_local.act_50 = preferences.getUShort("act_50", 1); - if (preferences.isKey("act_100")) ambit_calibration_local.act_100 = preferences.getUShort("act_100", 2); - if (preferences.isKey("act_150")) ambit_calibration_local.act_150 = preferences.getUShort("act_150", 3); - if (preferences.isKey("act_200")) ambit_calibration_local.act_200 = preferences.getUShort("act_200", 4); - if (preferences.isKey("act_250")) ambit_calibration_local.act_250 = preferences.getUShort("act_250", 5); - - if (preferences.isKey("adpd_lit")) ambit_calibration_local.adpd[1] = preferences.getUInt("adpd_lit", 0); - if (preferences.isKey("adpd_dark")) ambit_calibration_local.adpd[0] = preferences.getUInt("adpd_dark", 0); - if (preferences.isKey("adpd_sun")) ambit_calibration_local.adpd[2] = preferences.getUInt("adpd_sun", 0); - if (preferences.isKey("adpd_leaf")) ambit_calibration_local.adpd[3] = preferences.getUInt("adpd_leaf", 0); - if (preferences.isKey("adpd_730")) ambit_calibration_local.adpd[4] = preferences.getUInt("adpd_730", 0); - if (preferences.isKey("adpd_730r")) ambit_calibration_local.adpd[5] = preferences.getUInt("adpd_730r", 0); - preferences.end(); - mlx_read_coe(ambit_calibration_local.mlx_coef); - memcpy(&ambit_calibration_income, &ambit_calibration_local, sizeof(ambit_calibration_info_t)); -} - - - - - -void load_info_from_nvs(bool print){ - load_metadata(); - if (print){ - Serial.printf("Metadata: lon:%f\tlat:%f\talt:%f\ttime:%d\tacc:%f\tvacc:%f\tinfo1:%s\tx:%f\ty:%f\tz:%f\n", metadata_epprom.lon, metadata_epprom.lat, metadata_epprom.alt, metadata_epprom.time, metadata_epprom.acc, metadata_epprom.vacc, metadata_epprom.info1, metadata_epprom.x, metadata_epprom.y, metadata_epprom.z); - } - load_calibration_info(); - if (print){ - // print all ambit_calibration_local - Serial.printf("Calibration: Name:%s\tActinic:%f\tSpec:%f\tEmit:%f\tSun:%f\tTemp_offset:%f\tTemp_slope:%f\n", ambit_calibration_local.ambit_name, ambit_calibration_local.actinic_coef, ambit_calibration_local.spec_coef, ambit_calibration_local.mlx_emissivity, ambit_calibration_local.sun_coef, ambit_calibration_local.temp_offset, ambit_calibration_local.temp_slope); - Serial.printf("Calibration: Act_50:%d\tAct_100:%d\tAct_150:%d\tAct_200:%d\tAct_250:%d\n", ambit_calibration_local.act_50, ambit_calibration_local.act_100, ambit_calibration_local.act_150, ambit_calibration_local.act_200, ambit_calibration_local.act_250); - Serial.printf("Calibration: ADPD: %d\t%d\t%d\t%d\t%d\t%d\n", ambit_calibration_local.adpd[0], ambit_calibration_local.adpd[1], ambit_calibration_local.adpd[2], ambit_calibration_local.adpd[3], ambit_calibration_local.adpd[4], ambit_calibration_local.adpd[5]); - - Serial.printf("MLX: "); - for (int i = 0; i < 14; i++){ - Serial.printf("%d\t", ambit_calibration_local.mlx_coef[i]); - } - Serial.printf("\n"); - } - get_FW_info(); - if (print){ - Serial.printf("FW: MAC:%012llx\tSize:%d\tDate:%s\n", ambit_FW_info.MAC, ambit_FW_info.Size, ambit_FW_info.FW_date); - Serial.printf("FW: %d.%d.%d\n", ambit_FW_info.Major, ambit_FW_info.Minor, ambit_FW_info.Batch); - } - return; -} - diff --git a/nvs1.h b/nvs1.h deleted file mode 100644 index c2133f1..0000000 --- a/nvs1.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef _NVS1_H_ -#define _NVS1_H_ -#include -#include "nvs_flash.h" - -#define MAJOR_VERSION 0 -#define MINOR_VERSION 0 -#define BATCH_VERSION 7 - -struct ambit_calibration_info_t -{ - char ambit_name[20] = "AmbitV007"; - int32_t mlx_coef[14] = {0}; - uint32_t adpd[6] = {0}; - float_t temp_offset = 0.0; - float_t temp_slope = 1.0; - float_t actinic_coef = 0.1; - float_t spec_coef = 0.3; - uint16_t act_50 = 5; - uint16_t act_100 = 4; - uint16_t act_150 = 3; - uint16_t act_200 = 2; - uint16_t act_250 = 1; - float_t mlx_emissivity = 0.9; - float_t sun_coef = 1.0; -}; - -extern struct ambit_calibration_info_t ambit_calibration_local, ambit_calibration_income; - -struct ambit_FW_info_t -{ - uint8_t Major = MAJOR_VERSION; - uint8_t Minor = MINOR_VERSION; - uint8_t Batch = BATCH_VERSION; - uint32_t Size = 0; - uint64_t MAC = 0; - char FW_date[12]; - char reserved[12]; - uint8_t Checksum = 0; -}; - -extern struct ambit_FW_info_t ambit_FW_info; - -struct metadata_t -{ - double lon = 1.0; - double lat = 1.0; - float alt = 1.0; - float acc = 1.0; - float vacc = 1.0; - uint32_t time = 0; - float x = 0.0; - float y = 0.0; - float z = 0.0; - char info1[200] = "New_Ambit"; - uint16_t EOF_MARK = 2025; // end of file marker, used to check if the metadata is valid -}; - -extern struct metadata_t metadata_epprom, metadata_incoming; - -void load_info_from_nvs(bool print); -void save_metadata(void); - -#endif // _NVS1_H_ \ No newline at end of file diff --git a/old/do_E.cpp b/old/do_E.cpp deleted file mode 100644 index 2149021..0000000 --- a/old/do_E.cpp +++ /dev/null @@ -1,210 +0,0 @@ - -#include -#define MAX_E_COMMAND_LEN 20 - - -extern uint8_t pulsed_620_current, pulsed_720_current, dc_current; -extern uint8_t gain_fluor, gain_fluref, gain_720, gain_720ref, gain_sun, gain_leaf; -extern uint8_t status_run_config_set; - - -int MPF(uint16_t mode, uint16_t current, uint16_t dc_current,uint8_t,uint8_t); -int conf_slow_FR_1(uint8_t I620, uint8_t I730, uint8_t I_FR, uint8_t G_Fluor, uint8_t G_FluorRef, uint8_t G_Sun, uint8_t G_IR, uint8_t G_FR, uint8_t G_FRref); -int run_arr_type1(uint8_t length, uint8_t* arr, bool); - - - -static constexpr unsigned hash(const char *string) -{ - return *string == 0 ? 17325 : *string + (*string * hash(string+1)); -} - -static const char* TAG = "DO_E"; - - -static uint8_t get_cmd(const char* s, uint16_t* ptr, char *cmd){ - char n; - uint8_t counter = 0; - for (uint8_t i = 0; i < MAX_E_COMMAND_LEN; i++){ - n = s[i + *ptr]; - - if ((n == ',') || (n == '\0')){ - cmd[counter] = '\0'; - if (i > 0){ - if (n == ','){ - *ptr += i + 1; - return 1; - }else{ - *ptr += i; - return 2; - } - } - return 0; - }else if (s[i + *ptr] > 31){ - if ((s[i + *ptr] == 32) && (counter == 0)) continue; - cmd[counter] = s[i + *ptr]; - counter += 1; - } - } - return 0; -} - -static uint8_t get_int_from_string(const char* s, uint16_t* ptr, int* number){ - char n; - char integer[MAX_E_COMMAND_LEN]; - uint8_t counter = 0; - - for (uint8_t i = 0; i < MAX_E_COMMAND_LEN; i++){ - n = s[i + *ptr]; - - if (((n > 47) && (n < 58)) || (n == 45)){ - integer[counter] = n; - counter += 1; - }else if ((n == ',')||(n == '\0')){ - if (counter == 0) return 0; - integer[counter] = '\0'; - *number = atoi(integer); - if (n == ','){ - *ptr += i + 1; - return 1; - } - *ptr += i; - return 2; - }else if (n < 32) return 0; - } - return 0; -} - - -static uint16_t get_int_array(const char* s, const uint16_t ptr, uint8_t* arr){ - uint16_t data_counter = 0; - int ii = 0; - uint16_t ptr_used = ptr; - - uint8_t ret = get_int_from_string(s, &ptr_used, &ii); - while (ret > 0){ - if (arr != NULL) arr[data_counter] = ii; - data_counter += 1; - if (ret == 2) break; - ret = get_int_from_string(s, &ptr_used, &ii); - } - - return data_counter; -} - -void do_E(const char* c){ - char cmd[MAX_E_COMMAND_LEN]; - uint16_t ptr = 0; - int ii; - - if (get_cmd(c, &ptr, cmd) == 0){ - ESP_LOGE(TAG, "Command cannot parse: %s", c); - return; - } - ESP_LOGV(TAG, "Command is: %s", cmd); - - unsigned int val = hash(cmd); - - switch (val) - { - case hash("hello"): - Serial.println("Ready"); - break; - - case hash("SC"): - { - uint8_t arr[3]; - uint8_t counter = get_int_array(c, ptr, arr); - ESP_LOGV(TAG, "set currents cmd gets %d numbers:", counter); - if (counter == 3){ - pulsed_620_current = arr[0]; - pulsed_720_current = arr[1]; - dc_current = arr[2]; - }else if (counter == 0){ - Serial.printf("Currents are: %d,%d,%d", pulsed_620_current,pulsed_720_current,dc_current); - }else{ - ESP_LOGE(TAG, "set currents cmd gets %d numbers:", counter); - } - } - break; - - case hash("SG"): - { - uint8_t arr[6]; - uint8_t counter = get_int_array(c, ptr, arr); - ESP_LOGV(TAG, "set Gains cmd gets %d numbers:", counter); - if (counter == 6){ - gain_fluor = arr[0]; - gain_fluref = arr[1]; - gain_720 = arr[2]; - gain_720ref = arr[3]; - gain_sun = arr[4]; - gain_leaf = arr[5]; - }else if (counter == 0){ - Serial.printf("Gains are: %d,%d,%d,%d,%d,%d", gain_fluor,gain_fluref,gain_720,gain_720ref,gain_sun,gain_leaf); - } - - else - { - ESP_LOGE(TAG, "set Gains cmd gets %d numbers:", counter); - } - } - break; - - case hash("config1"): - if (status_run_config_set == 0){ - conf_slow_FR_1(pulsed_620_current, pulsed_720_current, dc_current, gain_fluor, gain_fluref, gain_sun, gain_leaf, gain_720, gain_720ref); - status_run_config_set = 1; - } - break; - - case hash("arr"): - { - uint8_t counter = get_int_array(c, ptr, NULL); - ESP_LOGV(TAG, "arr cmd gets %d numbers:", counter); - //expect: num lines, led_persist + N x 8 elements, checksum - if ((counter - 3)%8 > 0){ - ESP_LOGE(TAG, "arr not right"); - break; - } - uint8_t line_counts = (counter - 3)/8; - if (line_counts == 0){ - ESP_LOGE(TAG, "arr is zero"); - break; - } - - uint8_t arr[counter] = {0}; - uint8_t checksum = 0; - get_int_array(c, ptr, arr); - - if (line_counts != arr[0]){ - ESP_LOGE(TAG, "arr length doesn't match"); - break; - } - - for (uint8_t i = 0; i < line_counts; i++){ - for (uint8_t j = 0; j < 8; j++){ - Serial.printf("%d,", arr[i * 8 + j + 2]); - } - Serial.println(); - } - run_arr_type1(line_counts, arr + 2, arr[1]); - } - break; - - - - default: - Serial.printf("Bad command:%s", cmd); - } - - - - - - - - - - -} \ No newline at end of file diff --git a/old/mlx90632.c b/old/mlx90632.c deleted file mode 100644 index a5a084e..0000000 --- a/old/mlx90632.c +++ /dev/null @@ -1,710 +0,0 @@ -/** - * @file mlx90632.c - * @brief MLX90632 driver with virtual i2c communication - * @internal - * - * @copyright (C) 2017 Melexis N.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @endinternal - * - * @details - * - * @addtogroup mlx90632_private MLX90632 Internal library functions - * @{ - * - */ -#include -#include -#include - -#include "mlx90632.h" -#include "mlx90632_depends.h" - -#define POW10 10000000000LL - -#ifndef VERSION -#define VERSION "test" -#endif - -static const char mlx90632version[] __attribute__((used)) = { VERSION }; - -#ifndef STATIC -#define STATIC static -#endif - - -void msleep(int msecs); - -int mlx90632_start_measurement(void) -{ - int ret, tries = MLX90632_MAX_NUMBER_MESUREMENT_READ_TRIES; - uint16_t reg_status; - - ret = mlx90632_i2c_read(MLX90632_REG_STATUS, ®_status); - if (ret < 0) - return ret; - - ret = mlx90632_i2c_write(MLX90632_REG_STATUS, reg_status & (~MLX90632_STAT_DATA_RDY)); - if (ret < 0) - return ret; - - while (tries-- > 0) - { - ret = mlx90632_i2c_read(MLX90632_REG_STATUS, ®_status); - if (ret < 0) - return ret; - if (reg_status & MLX90632_STAT_DATA_RDY) - break; - /* minimum wait time to complete measurement - * should be calculated according to refresh rate - * atm 10ms - 11ms - */ - usleep(10000, 11000); - } - - if (tries < 0) - { - // data not ready - return -ETIMEDOUT; - } - - return (reg_status & MLX90632_STAT_CYCLE_POS) >> 2; -} - -/** Based on @link mlx90632_start_measurement @endlink return value fill channel_new and channel_old - * variables with proper values. This is to provide a bit more flexibility in case other channels are - * returned and need a bit more mingeling than usual maths can provide. - * - * So far there are just two use cases. If returned value is not 1 or 2, it will leave channel_new - * and channel_old unassigned. - * - * @param[in] ret @link mlx90632_start_measurement @endlink return value - * @param[out] *channel_new Pointer to memory location where new channel value is stored - * @param[out] *channel_old Pointer to memory location where old channel value is stored - * - * @retval 0 When both memory locations are updated as per ret - * @retval -EINVAL channel_new and channel_old were not updated - */ -STATIC int32_t mlx90632_channel_new_select(int32_t ret, uint8_t *channel_new, uint8_t *channel_old) -{ - switch (ret) - { - case 1: - *channel_new = 1; - *channel_old = 2; - break; - - case 2: - *channel_new = 2; - *channel_old = 1; - break; - - default: - return -EINVAL; - } - return 0; -} - -/** Read ambient raw old and new values based on @link mlx90632_start_measurement @endlink return value. - * - * Two i2c_reads are needed to obtain necessary raw ambient values from the sensor, as they are then - * preprocessed before going to calculation functions. Because one value is newer than other (see @link - * mlx90632_start_measurement @endlink) this function provides dynamics based on return value of the - * @link mlx90632_start_measurement @endlink. - * - * @param[out] *ambient_new_raw Pointer to memory location where new ambient value from sensor is stored - * @param[out] *ambient_old_raw Pointer to memory location where old ambient value from sensor is stored - * - * @retval 0 Successfully read both values - * @retval <0 Something went wrong. Check errno.h for more details. - */ -STATIC int32_t mlx90632_read_temp_ambient_raw(int16_t *ambient_new_raw, int16_t *ambient_old_raw) -{ - int32_t ret; - uint16_t read_tmp; - - ret = mlx90632_i2c_read(MLX90632_RAM_3(1), &read_tmp); - if (ret < 0) - return ret; - *ambient_new_raw = (int16_t)read_tmp; - - ret = mlx90632_i2c_read(MLX90632_RAM_3(2), &read_tmp); - if (ret < 0) - return ret; - *ambient_old_raw = (int16_t)read_tmp; - - return ret; -} - -/** Read object raw old and new values based on @link mlx90632_start_measurement @endlink return value. - * - * Four i2c_reads are needed to obtain necessary raw object values from the sensor. These values are grouped per new - * and old and then averaged before return of the function. After that they are then preprocessed before going to - * calculation functions. Because one value is newer than other (see @link mlx90632_start_measurement @endlink) this - * function provides dynamics based on return value of the @link mlx90632_start_measurement @endlink. - * - * @param[in] start_measurement_ret Return value of @link mlx90632_start_measurement @endlink - * @param[out] *object_new_raw Pointer to memory location where average of new object values from sensor is stored - * @param[out] *object_old_raw Pointer to memory location where average of old object values from sensor is stored - * - * @retval 0 Successfully read both values - * @retval <0 Something went wrong. Check errno.h for more details. - */ -STATIC int32_t mlx90632_read_temp_object_raw(int32_t start_measurement_ret, - int16_t *object_new_raw, int16_t *object_old_raw) -{ - int32_t ret; - uint16_t read_tmp; - int16_t read; - uint8_t channel, channel_old; - - ret = mlx90632_channel_new_select(start_measurement_ret, &channel, &channel_old); - if (ret != 0) - return -EINVAL; - - ret = mlx90632_i2c_read(MLX90632_RAM_2(channel), &read_tmp); - if (ret < 0) - return ret; - - read = (int16_t)read_tmp; - - ret = mlx90632_i2c_read(MLX90632_RAM_1(channel), &read_tmp); - if (ret < 0) - return ret; - *object_new_raw = (read + (int16_t)read_tmp) / 2; - - ret = mlx90632_i2c_read(MLX90632_RAM_2(channel_old), &read_tmp); - if (ret < 0) - return ret; - read = (int16_t)read_tmp; - - ret = mlx90632_i2c_read(MLX90632_RAM_1(channel_old), &read_tmp); - if (ret < 0) - return ret; - *object_old_raw = (read + (int16_t)read_tmp) / 2; - - return ret; -} - -int32_t mlx90632_read_temp_raw(int16_t *ambient_new_raw, int16_t *ambient_old_raw, - int16_t *object_new_raw, int16_t *object_old_raw) -{ - int32_t ret, start_measurement_ret; - - // trigger and wait for measurement to complete - start_measurement_ret = mlx90632_start_measurement(); - if (start_measurement_ret < 0) - return start_measurement_ret; - - /** Read new and old **ambient** values from sensor */ - ret = mlx90632_read_temp_ambient_raw(ambient_new_raw, ambient_old_raw); - if (ret < 0) - return ret; - - /** Read new and old **object** values from sensor */ - ret = mlx90632_read_temp_object_raw(start_measurement_ret, object_new_raw, object_old_raw); - - return ret; -} - -int32_t mlx90632_read_temp_raw_burst(int16_t *ambient_new_raw, int16_t *ambient_old_raw, - int16_t *object_new_raw, int16_t *object_old_raw) -{ - int32_t ret, start_measurement_ret; - - // trigger and wait for measurement to complete - start_measurement_ret = mlx90632_start_measurement_burst(); - if (start_measurement_ret < 0) - return start_measurement_ret; - - /** Read new and old **ambient** values from sensor */ - ret = mlx90632_read_temp_ambient_raw(ambient_new_raw, ambient_old_raw); - if (ret < 0) - return ret; - - /** Read new and old **object** values from sensor */ - ret = mlx90632_read_temp_object_raw(2, object_new_raw, object_old_raw); - - return ret; -} - - -/* DSPv5 */ -double mlx90632_preprocess_temp_ambient(int16_t ambient_new_raw, int16_t ambient_old_raw, int16_t Gb) -{ - double VR_Ta, kGb; - - kGb = ((double)Gb) / 1024.0; - - VR_Ta = ambient_old_raw + kGb * (ambient_new_raw / (MLX90632_REF_3)); - return ((ambient_new_raw / (MLX90632_REF_3)) / VR_Ta) * 524288.0; -} - -double mlx90632_preprocess_temp_object(int16_t object_new_raw, int16_t object_old_raw, - int16_t ambient_new_raw, int16_t ambient_old_raw, - int16_t Ka) -{ - double VR_IR, kKa; - - kKa = ((double)Ka) / 1024.0; - - VR_IR = ambient_old_raw + kKa * (ambient_new_raw / (MLX90632_REF_3)); - return ((((object_new_raw + object_old_raw) / 2) / (MLX90632_REF_12)) / VR_IR) * 524288.0; -} - -double mlx90632_calc_temp_ambient(int16_t ambient_new_raw, int16_t ambient_old_raw, int32_t P_T, - int32_t P_R, int32_t P_G, int32_t P_O, int16_t Gb) -{ - double Asub, Bsub, Ablock, Bblock, Cblock, AMB; - - AMB = mlx90632_preprocess_temp_ambient(ambient_new_raw, ambient_old_raw, Gb); - - Asub = ((double)P_T) / (double)17592186044416.0; - Bsub = (double)AMB - ((double)P_R / (double)256.0); - Ablock = Asub * (Bsub * Bsub); - Bblock = (Bsub / (double)P_G) * (double)1048576.0; - Cblock = (double)P_O / (double)256.0; - - return Bblock + Ablock + Cblock; -} - -/** Iterative calculation of object temperature - * - * DSPv5 requires 3 iterations to reduce noise for object temperature. Since - * each iteration requires same calculations this helper function is - * implemented. - * - * @param[in] prev_object_temp previously calculated object temperature. If - * there is no previously calculated temperature - * input 25.0 - * @param[in] object object temperature from @link mlx90632_preprocess_temp_object @endlink - * @param[in] TAdut ambient temperature coefficient - * @param[in] Ga Register value on @link MLX90632_EE_Ga @endlink - * @param[in] Fa Register value on @link MLX90632_EE_Fa @endlink - * @param[in] Fb Register value on @link MLX90632_EE_Fb @endlink - * @param[in] Ha Register value on @link MLX90632_EE_Ha @endlink - * @param[in] Hb Register value on @link MLX90632_EE_Hb @endlink - * @param[in] emissivity Value provided by user of the object emissivity - * - * @return Calculated object temperature for current iteration in milliCelsius - */ -STATIC double mlx90632_calc_temp_object_iteration(double prev_object_temp, int32_t object, double TAdut, - int32_t Ga, int32_t Fa, int32_t Fb, int16_t Ha, int16_t Hb, - double emissivity) -{ - double calcedGa, calcedGb, calcedFa, TAdut4, first_sqrt; - // temp variables - double KsTAtmp, Alpha_corr; - double Ha_customer, Hb_customer; - - - Ha_customer = Ha / ((double)16384.0); - Hb_customer = Hb / ((double)1024.0); - calcedGa = ((double)Ga * (prev_object_temp - 25)) / ((double)68719476736.0); - KsTAtmp = (double)Fb * (TAdut - 25); - calcedGb = KsTAtmp / ((double)68719476736.0); - Alpha_corr = (((double)(Fa * POW10)) * Ha_customer * (double)(1 + calcedGa + calcedGb)) / - ((double)70368744177664.0); - calcedFa = object / (emissivity * (Alpha_corr / POW10)); - TAdut4 = (TAdut + 273.15) * (TAdut + 273.15) * (TAdut + 273.15) * (TAdut + 273.15); - - first_sqrt = sqrt(calcedFa + TAdut4); - - return sqrt(first_sqrt) - 273.15 - Hb_customer; -} - -/** Iterative calculation of object temperature when the environment temperature differs from the sensor temperature - * - * DSPv5 requires 3 iterations to reduce noise for object temperature. Since - * each iteration requires same calculations this helper function is - * implemented. - * - * @param[in] prev_object_temp previously calculated object temperature. If - * there is no previously calculated temperature - * input 25.0 - * @param[in] object object temperature from @link mlx90632_preprocess_temp_object @endlink - * @param[in] TAdut ambient temperature coefficient - * @param[in] TaTr4 compensation coefficient for reflected (environment) temperature. The compensation - * coefficient is calculated from ambient temperature either from a sensor different than the MLX90632 or - * acquired by other means. - * @param[in] Ga Register value on @link MLX90632_EE_Ga @endlink - * @param[in] Fa Register value on @link MLX90632_EE_Fa @endlink - * @param[in] Fb Register value on @link MLX90632_EE_Fb @endlink - * @param[in] Ha Register value on @link MLX90632_EE_Ha @endlink - * @param[in] Hb Register value on @link MLX90632_EE_Hb @endlink - * @param[in] emissivity Value provided by user of the object emissivity - * - * @return Calculated object temperature for current iteration in milliCelsius - */ -STATIC double mlx90632_calc_temp_object_iteration_reflected(double prev_object_temp, int32_t object, double TAdut, double TaTr4, - int32_t Ga, int32_t Fa, int32_t Fb, int16_t Ha, int16_t Hb, - double emissivity) -{ - double calcedGa, calcedGb, calcedFa, first_sqrt; - // temp variables - double KsTAtmp, Alpha_corr; - double Ha_customer, Hb_customer; - - Ha_customer = Ha / ((double)16384.0); - Hb_customer = Hb / ((double)1024.0); - calcedGa = ((double)Ga * (prev_object_temp - 25)) / ((double)68719476736.0); - KsTAtmp = (double)Fb * (TAdut - 25); - calcedGb = KsTAtmp / ((double)68719476736.0); - Alpha_corr = (((double)(Fa * POW10)) * Ha_customer * (double)(1 + calcedGa + calcedGb)) / - ((double)70368744177664.0); - calcedFa = object / (emissivity * (Alpha_corr / POW10)); - - first_sqrt = sqrt(calcedFa + TaTr4); - - return sqrt(first_sqrt) - 273.15 - Hb_customer; -} - -static double emissivity = 0.0; -void mlx90632_set_emissivity(double value) -{ - emissivity = value; -} - -double mlx90632_get_emissivity(void) -{ - if (emissivity == 0.0) - { - return 1.0; - } - else - { - return emissivity; - } -} - -double mlx90632_calc_temp_object(int32_t object, int32_t ambient, - int32_t Ea, int32_t Eb, int32_t Ga, int32_t Fa, int32_t Fb, - int16_t Ha, int16_t Hb) -{ - double kEa, kEb, TAdut; - double temp = 25.0; - double tmp_emi = mlx90632_get_emissivity(); - int8_t i; - - kEa = ((double)Ea) / ((double)65536.0); - kEb = ((double)Eb) / ((double)256.0); - TAdut = (((double)ambient) - kEb) / kEa + 25; - - //iterate through calculations - for (i = 0; i < 5; ++i) - { - temp = mlx90632_calc_temp_object_iteration(temp, object, TAdut, Ga, Fa, Fb, Ha, Hb, tmp_emi); - } - return temp; -} - -double mlx90632_calc_temp_object_reflected(int32_t object, int32_t ambient, double reflected, - int32_t Ea, int32_t Eb, int32_t Ga, int32_t Fa, int32_t Fb, - int16_t Ha, int16_t Hb) -{ - double kEa, kEb, TAdut; - double temp = 25.0; - double tmp_emi = mlx90632_get_emissivity(); - double TaTr4; - double ta4; - int8_t i; - - kEa = ((double)Ea) / ((double)65536.0); - kEb = ((double)Eb) / ((double)256.0); - TAdut = (((double)ambient) - kEb) / kEa + 25; - - TaTr4 = reflected + 273.15; - TaTr4 = TaTr4 * TaTr4; - TaTr4 = TaTr4 * TaTr4; - ta4 = TAdut + 273.15; - ta4 = ta4 * ta4; - ta4 = ta4 * ta4; - TaTr4 = TaTr4 - (TaTr4 - ta4) / tmp_emi; - - //iterate through calculations - for (i = 0; i < 5; ++i) - { - temp = mlx90632_calc_temp_object_iteration_reflected(temp, object, TAdut, TaTr4, Ga, Fa, Fb, Ha, Hb, tmp_emi); - } - return temp; -} - -int32_t mlx90632_init(void) -{ - int32_t ret; - uint16_t eeprom_version, reg_status; - - ret = mlx90632_i2c_read(MLX90632_EE_VERSION, &eeprom_version); - if (ret < 0) - { - return ret; - } - - if ((eeprom_version & 0x00FF) != MLX90632_DSPv5) - { - // this here can fail because of big/little endian of cpu/i2c - return -EPROTONOSUPPORT; - } - - ret = mlx90632_i2c_read(MLX90632_REG_STATUS, ®_status); - if (ret < 0) - return ret; - - // Prepare a clean start with setting NEW_DATA to 0 - ret = mlx90632_i2c_write(MLX90632_REG_STATUS, reg_status & ~(MLX90632_STAT_DATA_RDY)); - if (ret < 0) - return ret; - - if ((eeprom_version & 0x7F00) == MLX90632_XTD_RNG_KEY) - { - return ERANGE; - } - - return 0; -} - -int32_t mlx90632_addressed_reset(void) -{ - int32_t ret; - uint16_t reg_ctrl; - uint16_t reg_value; - - ret = mlx90632_i2c_read(MLX90632_REG_CTRL, ®_value); - if (ret < 0) - return ret; - - reg_ctrl = reg_value & ~MLX90632_CFG_PWR_MASK; - reg_ctrl |= MLX90632_PWR_STATUS_STEP; - ret = mlx90632_i2c_write(MLX90632_REG_CTRL, reg_ctrl); - if (ret < 0) - return ret; - - ret = mlx90632_i2c_write(0x3005, MLX90632_RESET_CMD); - if (ret < 0) - return ret; - - usleep(150, 200); - - ret = mlx90632_i2c_write(MLX90632_REG_CTRL, reg_value); - - return ret; -} - -int32_t mlx90632_get_measurement_time(uint16_t meas) -{ - int32_t ret; - uint16_t reg; - - ret = mlx90632_i2c_read(meas, ®); - if (ret < 0) - return ret; - - reg &= MLX90632_EE_REFRESH_RATE_MASK; - reg = reg >> 8; - - return MLX90632_MEAS_MAX_TIME >> reg; -} - -int32_t mlx90632_calculate_dataset_ready_time(void) -{ - int32_t ret; - int32_t refresh_time; - - ret = mlx90632_get_meas_type(); - if (ret < 0) - return ret; - - if ((ret != MLX90632_MTYP_MEDICAL_BURST) && (ret != MLX90632_MTYP_EXTENDED_BURST)) - return -EINVAL; - - if (ret == MLX90632_MTYP_MEDICAL_BURST) - { - ret = mlx90632_get_measurement_time(MLX90632_EE_MEDICAL_MEAS1); - if (ret < 0) - return ret; - - refresh_time = ret; - - ret = mlx90632_get_measurement_time(MLX90632_EE_MEDICAL_MEAS2); - if (ret < 0) - return ret; - - refresh_time = refresh_time + ret; - } - else - { - ret = mlx90632_get_measurement_time(MLX90632_EE_EXTENDED_MEAS1); - if (ret < 0) - return ret; - - refresh_time = ret; - - ret = mlx90632_get_measurement_time(MLX90632_EE_EXTENDED_MEAS2); - if (ret < 0) - return ret; - - refresh_time = refresh_time + ret; - - ret = mlx90632_get_measurement_time(MLX90632_EE_EXTENDED_MEAS3); - if (ret < 0) - return ret; - - refresh_time = refresh_time + ret; - } - - return refresh_time; -} - -int32_t mlx90632_start_measurement_burst(void) -{ - int32_t ret; - int tries = MLX90632_MAX_NUMBER_MESUREMENT_READ_TRIES; - uint16_t reg; - - ret = mlx90632_i2c_read(MLX90632_REG_CTRL, ®); - if (ret < 0) - return ret; - - reg |= MLX90632_START_BURST_MEAS; - - ret = mlx90632_i2c_write(MLX90632_REG_CTRL, reg); - if (ret < 0) - return ret; - - ret = mlx90632_calculate_dataset_ready_time(); - if (ret < 0) - return ret; - delay(ret); /* Waiting for refresh of all the measurement tables */ - - while (tries-- > 0) - { - ret = mlx90632_i2c_read(MLX90632_REG_STATUS, ®); - if (ret < 0) - return ret; - if ((reg & MLX90632_STAT_BUSY) == 0) - break; - /* minimum wait time to complete measurement - * should be calculated according to refresh rate - * atm 10ms - 11ms - */ - usleep(10000, 11000); - } - - if (tries < 0) - { - // data not ready - return -ETIMEDOUT; - } - - return 0; -} - - -STATIC int32_t mlx90632_unlock_eeporm() -{ - return mlx90632_i2c_write(0x3005, MLX90632_EEPROM_WRITE_KEY); -} - -STATIC int32_t mlx90632_wait_for_eeprom_not_busy() -{ - uint16_t reg_status; - int32_t ret = mlx90632_i2c_read(MLX90632_REG_STATUS, ®_status); - - while (ret >= 0 && reg_status & MLX90632_STAT_EE_BUSY) - { - ret = mlx90632_i2c_read(MLX90632_REG_STATUS, ®_status); - } - - return ret; -} - -STATIC int32_t mlx90632_erase_eeprom(uint16_t address) -{ - int32_t ret = mlx90632_unlock_eeporm(); - - if (ret < 0) - return ret; - - ret = mlx90632_i2c_write(address, 0x00); - if (ret < 0) - return ret; - - ret = mlx90632_wait_for_eeprom_not_busy(); - return ret; -} - -STATIC int32_t mlx90632_write_eeprom(uint16_t address, uint16_t data) -{ - int32_t ret = mlx90632_erase_eeprom(address); - - if (ret < 0) - return ret; - - ret = mlx90632_unlock_eeporm(); - if (ret < 0) - return ret; - - ret = mlx90632_i2c_write(address, data); - if (ret < 0) - return ret; - - ret = mlx90632_wait_for_eeprom_not_busy(); - return ret; -} - -int32_t mlx90632_set_refresh_rate(mlx90632_meas_t measRate) -{ - uint16_t meas1, meas2; - - int32_t ret = mlx90632_i2c_read(MLX90632_EE_MEDICAL_MEAS1, &meas1); - - if (ret < 0) - return ret; - - uint16_t new_value = MLX90632_NEW_REG_VALUE(meas1, measRate, MLX90632_EE_REFRESH_RATE_START, MLX90632_EE_REFRESH_RATE_SHIFT); - - if (meas1 != new_value) - { - ret = mlx90632_write_eeprom(MLX90632_EE_MEDICAL_MEAS1, new_value); - if (ret < 0) - return ret; - } - - ret = mlx90632_i2c_read(MLX90632_EE_MEDICAL_MEAS2, &meas2); - if (ret < 0) - return ret; - - new_value = MLX90632_NEW_REG_VALUE(meas2, measRate, MLX90632_EE_REFRESH_RATE_START, MLX90632_EE_REFRESH_RATE_SHIFT); - if (meas2 != new_value) - { - ret = mlx90632_write_eeprom(MLX90632_EE_MEDICAL_MEAS2, new_value); - } - - return ret; -} - -mlx90632_meas_t mlx90632_get_refresh_rate(void) -{ - int32_t ret; - uint16_t meas1; - - ret = mlx90632_i2c_read(MLX90632_EE_MEDICAL_MEAS1, &meas1); - if (ret < 0) - return MLX90632_MEAS_HZ_ERROR; - - return (mlx90632_meas_t)MLX90632_REFRESH_RATE(meas1); -} - -///@} diff --git a/old/mlx90632.h b/old/mlx90632.h deleted file mode 100644 index 9d9f44c..0000000 --- a/old/mlx90632.h +++ /dev/null @@ -1,465 +0,0 @@ -/** - * @file mlx90632.h - * @brief MLX90632 driver with virtual i2c communication - * @internal - * - * @copyright (C) 2017 Melexis N.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @endinternal - * - * @addtogroup mlx90632_API MLX90632 Driver Library API - * Implementation of MLX90632 driver with virtual i2c read/write functions - * - * @details - * Copy of Kernel driver, except that it is stripped of Linux kernel specifics - * which are replaced by simple i2c read/write functions. There are some Linux - * kernel macros left behind as they make code more readable and easier to - * understand, but if you already have your own implementation then preprocessor - * should handle it just fine. - * - * Repository contains README.md for compilation and unit-test instructions. - * - * @{ - * - */ -#ifndef _MLX90632_LIB_ -#define _MLX90632_LIB_ - -/* Including CRC calculation functions */ -#include -#include "mlx90632_extended_meas.h" - -/* Solve errno not defined values */ -#ifndef ETIMEDOUT -#define ETIMEDOUT 110 /**< From linux errno.h */ -#endif -#ifndef EINVAL -#define EINVAL 22 /**< From linux errno.h */ -#endif -#ifndef EPROTONOSUPPORT -#define EPROTONOSUPPORT 93 /**< From linux errno.h */ -#endif -#ifndef ERANGE -#define ERANGE 34 /**< From linux errno.h */ -#endif -#ifndef ENOKEY -#define ENOKEY 126 /**< From linux errno.h */ -#endif - - -/* BIT, GENMASK and ARRAY_SIZE macros are imported from kernel */ -#ifndef BIT -#define BIT(x)(1UL << (x)) -#endif -#ifndef GENMASK -#ifndef BITS_PER_LONG -#warning "Using default BITS_PER_LONG value" -#define BITS_PER_LONG 32 /**< Define how many bits per long your CPU has */ -#endif -#define GENMASK(h, l) \ - (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) -#endif -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) /**< Return number of elements in array */ -#endif - -/* Memory sections addresses */ -#define MLX90632_ADDR_RAM 0x4000 /**< Start address of ram */ -#define MLX90632_ADDR_EEPROM 0x2480 /**< Start address of user eeprom */ - -/* EEPROM addresses - used at startup */ -#define MLX90632_EE_CTRL 0x24d4 /**< Control register initial value */ -#define MLX90632_EE_CONTROL MLX90632_EE_CTRL /**< More human readable for Control register */ - -#define MLX90632_EE_I2C_ADDRESS 0x24d5 /**< I2C address register initial value */ -#define MLX90632_EE_VERSION 0x240b /**< EEPROM version reg - assumed 0x101 */ - -#define MLX90632_EE_P_R 0x240c /**< Calibration constant ambient reference register 32bit */ -#define MLX90632_EE_P_G 0x240e /**< Calibration constant ambient gain register 32bit */ -#define MLX90632_EE_P_T 0x2410 /**< Calibration constant ambient tc2 register 32bit */ -#define MLX90632_EE_P_O 0x2412 /**< Calibration constant ambient offset register 32bit */ -#define MLX90632_EE_Aa 0x2414 /**< Aa calibration const register 32bit */ -#define MLX90632_EE_Ab 0x2416 /**< Ab calibration const register 32bit */ -#define MLX90632_EE_Ba 0x2418 /**< Ba calibration const register 32bit */ -#define MLX90632_EE_Bb 0x241a /**< Bb calibration const register 32bit */ -#define MLX90632_EE_Ca 0x241c /**< Ca calibration const register 32bit */ -#define MLX90632_EE_Cb 0x241e /**< Cb calibration const register 32bit */ -#define MLX90632_EE_Da 0x2420 /**< Da calibration const register 32bit */ -#define MLX90632_EE_Db 0x2422 /**< Db calibration const register 32bit */ -#define MLX90632_EE_Ea 0x2424 /**< Ea calibration constant register 32bit */ -#define MLX90632_EE_Eb 0x2426 /**< Eb calibration constant register 32bit */ -#define MLX90632_EE_Fa 0x2428 /**< Fa calibration constant register 32bit */ -#define MLX90632_EE_Fb 0x242a /**< Fb calibration constant register 32bit */ -#define MLX90632_EE_Ga 0x242c /**< Ga calibration constant register 32bit */ -#define MLX90632_EE_Gb 0x242e /**< Ambient Beta calibration constant 16bit */ -#define MLX90632_EE_Ka 0x242f /**< IR Beta calibration constant 16bit */ -#define MLX90632_EE_Ha 0x2481 /**< Ha customer calibration value register 16bit */ -#define MLX90632_EE_Hb 0x2482 /**< Hb customer calibration value register 16bit */ - -#define MLX90632_EE_MEDICAL_MEAS1 0x24E1 /**< Medical measurement 1 16bit */ -#define MLX90632_EE_MEDICAL_MEAS2 0x24E2 /**< Medical measurement 2 16bit */ -#define MLX90632_EE_EXTENDED_MEAS1 0x24F1 /**< Extended measurement 1 16bit */ -#define MLX90632_EE_EXTENDED_MEAS2 0x24F2 /**< Extended measurement 2 16bit */ -#define MLX90632_EE_EXTENDED_MEAS3 0x24F3 /**< Extended measurement 3 16bit */ - -/* Refresh Rate */ -typedef enum mlx90632_meas_e { - MLX90632_MEAS_HZ_ERROR = -1, - MLX90632_MEAS_HZ_HALF = 0, - MLX90632_MEAS_HZ_1 = 1, - MLX90632_MEAS_HZ_2 = 2, - MLX90632_MEAS_HZ_4 = 3, - MLX90632_MEAS_HZ_8 = 4, - MLX90632_MEAS_HZ_16 = 5, - MLX90632_MEAS_HZ_32 = 6, - MLX90632_MEAS_HZ_64 = 7, -} mlx90632_meas_t; -#define MLX90632_EE_REFRESH_RATE_START 10 /**< Refresh Rate Start bit */ -#define MLX90632_EE_REFRESH_RATE_SHIFT 8 /**< Refresh Rate shift */ -#define MLX90632_EE_REFRESH_RATE_MASK GENMASK(MLX90632_EE_REFRESH_RATE_START, MLX90632_EE_REFRESH_RATE_SHIFT) /**< Refresh Rate Mask */ -#define MLX90632_EE_REFRESH_RATE(ee_val) (ee_val & MLX90632_EE_REFRESH_RATE_MASK) /**< Extract the Refresh Rate bits*/ -#define MLX90632_REFRESH_RATE(ee_val) (MLX90632_EE_REFRESH_RATE(ee_val) >> MLX90632_EE_REFRESH_RATE_SHIFT) /**< Extract Refresh Rate from ee register */ -#define MLX90632_REFRESH_RATE_STATUS(mlx90632_meas) (mlx90632_meas << MLX90632_EE_REFRESH_RATE_SHIFT) /**< Extract the Refresh Rate bits */ - -/* Register addresses - volatile */ -#define MLX90632_REG_I2C_ADDR 0x3000 /**< Chip I2C address register */ - -/* Control register address - volatile */ -#define MLX90632_REG_CTRL 0x3001 /**< Control Register address */ -#define MLX90632_CFG_SOC_SHIFT 3 /**< Start measurement in step mode */ -#define MLX90632_CFG_SOC_MASK BIT(MLX90632_CFG_SOC_SHIFT) -#define MLX90632_CFG_PWR_MASK GENMASK(2, 1) /**< PowerMode Mask */ -#define MLX90632_CFG_PWR(ctrl_val) (ctrl_val & MLX90632_CFG_PWR_MASK) /**< Extract the PowerMode bits*/ -#define MLX90632_CFG_MTYP_SHIFT 4 /**< Meas select start shift */ -#define MLX90632_CFG_MTYP_MASK GENMASK(8, MLX90632_CFG_MTYP_SHIFT) /**< Meas select Mask */ -#define MLX90632_CFG_MTYP(ctrl_val) (ctrl_val & MLX90632_CFG_MTYP_MASK) /**< Extract the MeasType bits*/ -#define MLX90632_CFG_SOB_SHIFT 11 /**< Start burst measurement in step mode */ -#define MLX90632_CFG_SOB_MASK BIT(MLX90632_CFG_SOB_SHIFT) -#define MLX90632_CFG_SOB(ctrl_val) (ctrl_val << MLX90632_CFG_SOB_SHIFT) - -/* PowerModes statuses */ -#define MLX90632_PWR_STATUS(ctrl_val) (ctrl_val << 1) -#define MLX90632_PWR_STATUS_HALT MLX90632_PWR_STATUS(0) /**< Pwrmode hold */ -#define MLX90632_PWR_STATUS_SLEEP_STEP MLX90632_PWR_STATUS(1) /**< Pwrmode sleep step*/ -#define MLX90632_PWR_STATUS_STEP MLX90632_PWR_STATUS(2) /**< Pwrmode step */ -#define MLX90632_PWR_STATUS_CONTINUOUS MLX90632_PWR_STATUS(3) /**< Pwrmode continuous*/ -/* Measurement type select*/ -#define MLX90632_MTYP_STATUS(ctrl_val) (ctrl_val << MLX90632_CFG_MTYP_SHIFT) -#define MLX90632_MTYP_STATUS_MEDICAL MLX90632_MTYP_STATUS(0) /**< Medical measurement type */ -#define MLX90632_MTYP_STATUS_EXTENDED MLX90632_MTYP_STATUS(17) /**< Extended measurement type*/ -#define MLX90632_MTYP(reg_val) (MLX90632_CFG_MTYP(reg_val) >> MLX90632_CFG_MTYP_SHIFT) /**< Extract MTYP from Control register */ -/* Start of burst measurement options */ -#define MLX90632_START_BURST_MEAS MLX90632_CFG_SOB(1) -#define MLX90632_BURST_MEAS_NOT_PENDING MLX90632_CFG_SOB(0) - -/* Device status register - volatile */ -#define MLX90632_REG_STATUS 0x3fff /**< Device status register */ -#define MLX90632_STAT_BUSY BIT(10) /**< Device busy indicator */ -#define MLX90632_STAT_EE_BUSY BIT(9) /**< Device EEPROM busy indicator */ -#define MLX90632_STAT_BRST BIT(8) /**< Device brown out reset indicator */ -#define MLX90632_STAT_CYCLE_POS GENMASK(6, 2) /**< Data position in measurement table */ -#define MLX90632_STAT_DATA_RDY BIT(0) /**< Data ready indicator */ - -/* RAM_MEAS address-es for each channel */ -#define MLX90632_RAM_1(meas_num) (MLX90632_ADDR_RAM + 3 * meas_num) -#define MLX90632_RAM_2(meas_num) (MLX90632_ADDR_RAM + 3 * meas_num + 1) -#define MLX90632_RAM_3(meas_num) (MLX90632_ADDR_RAM + 3 * meas_num + 2) - -/* Timings (ms) */ -#define MLX90632_TIMING_EEPROM 100 /**< Time between EEPROM writes */ - -/* Magic constants */ -#define MLX90632_DSPv5 0x05 /* EEPROM DSP version */ -#define MLX90632_EEPROM_VERSION MLX90632_ID_MEDICAL /**< Legacy define - to be deprecated */ -#define MLX90632_EEPROM_WRITE_KEY 0x554C /**< EEPROM write key 0x55 and 0x4c */ -#define MLX90632_RESET_CMD 0x0006 /**< Reset sensor (address or global) */ -#define MLX90632_MAX_MEAS_NUM 31 /**< Maximum number of measurements in list */ -#define MLX90632_EE_SEED 0x3f6d /**< Seed for the CRC calculations */ -#define MLX90632_REF_12 12.0 /**< ResCtrlRef value of Channel 1 or Channel 2 */ -#define MLX90632_REF_3 12.0 /**< ResCtrlRef value of Channel 3 */ -#define MLX90632_XTD_RNG_KEY 0x0500 /**Extended range support indication key */ - -/* Measurement types - the MSBit is for software purposes only and has no hardware bit related to it. It indicates continuous '0' or sleeping step burst - '1' measurement mode*/ -#define MLX90632_MTYP_MEDICAL 0x00 -#define MLX90632_MTYP_EXTENDED 0x11 -#define MLX90632_MTYP_MEDICAL_BURST 0x80 -#define MLX90632_MTYP_EXTENDED_BURST 0x91 -#define MLX90632_BURST_MEASUREMENT_TYPE(meas_type) (meas_type + 0x80) /**< The MSBit is only used in the software to indicate burst type measurement. The 5 LS Bits define medical or extended measurement and are used to set the hardware */ - -#define MLX90632_MEASUREMENT_TYPE_STATUS(mtyp_type) (mtyp_type & 0x7F) /**< Extract the measurement type from MTYP */ -#define MLX90632_MEASUREMENT_BURST_STATUS(mtyp_type) (mtyp_type & 0x80) /**< Extract the measurement burst/continuous type from MTYP */ - -#define MLX90632_MEAS_MAX_TIME 2000 /**< Maximum measurement time in ms for the lowest possible refresh rate */ -#define MLX90632_MAX_NUMBER_MESUREMENT_READ_TRIES 100 /**< Maximum number of read tries before quiting with timeout error */ - -/* Gets a new register value based on the old register value - only writing the value based on the desired bits - * Masks the old register and shifts the new value in - */ -#define MLX90632_NEW_REG_VALUE(old_reg, new_value, h, l) \ - ((old_reg & (0xFFFF ^ GENMASK(h, l))) | (new_value << MLX90632_EE_REFRESH_RATE_SHIFT)) - -/** Read raw ambient and object temperature - * - * Trigger and read raw ambient and object temperatures. This values still need - * to be pre-processed via @link mlx90632_preprocess_temp_ambient @endlink and @link - * mlx90632_preprocess_temp_object @endlink functions and then processed via @link - * mlx90632_calc_temp_ambient @endlink and @link mlx90632_calc_temp_object @endlink - * to retrieve values in milliCelsius - * - * @param[out] ambient_new_raw Pointer to where new raw ambient temperature is written - * @param[out] object_new_raw Pointer to where new raw object temperature is written - * @param[out] ambient_old_raw Pointer to where old raw ambient temperature is written - * @param[out] object_old_raw Pointer to where old raw object temperature is written - * - * @retval 0 Successfully read both temperatures - * @retval <0 Something went wrong. Check errno.h for more details - */ -int32_t mlx90632_read_temp_raw(int16_t *ambient_new_raw, int16_t *ambient_old_raw, - int16_t *object_new_raw, int16_t *object_old_raw); - -/** Read raw ambient and object temperature in sleeping step mode - * - * Trigger and read raw ambient and object temperatures. This values still need - * to be pre-processed via @link mlx90632_preprocess_temp_ambient @endlink and @link - * mlx90632_preprocess_temp_object @endlink functions and then processed via @link - * mlx90632_calc_temp_ambient @endlink and @link mlx90632_calc_temp_object @endlink - * to retrieve values in milliCelsius - * - * @param[out] ambient_new_raw Pointer to where new raw ambient temperature is written - * @param[out] object_new_raw Pointer to where new raw object temperature is written - * @param[out] ambient_old_raw Pointer to where old raw ambient temperature is written - * @param[out] object_old_raw Pointer to where old raw object temperature is written - * - * @retval 0 Successfully read both temperatures - * @retval <0 Something went wrong. Check errno.h for more details - */ -int32_t mlx90632_read_temp_raw_burst(int16_t *ambient_new_raw, int16_t *ambient_old_raw, - int16_t *object_new_raw, int16_t *object_old_raw); - -/** Calculation of raw ambient output - * - * Preprocessing of the raw ambient value - * - * @param[in] ambient_new_raw ambient temperature from @link MLX90632_RAM_3 @endlink. The meas_num 1 or 2 is - * determined by value in @link MLX90632_STAT_CYCLE_POS @endlink - * @param[in] ambient_old_raw ambient temperature from @link MLX90632_RAM_3 @endlink. The meas_num 1 or 2 is - * determined by value not in @link MLX90632_STAT_CYCLE_POS @endlink - * @param[in] Gb Register value on @link MLX90632_EE_Gb @endlink - * - * @return Calculated ambient raw output - */ -double mlx90632_preprocess_temp_ambient(int16_t ambient_new_raw, int16_t ambient_old_raw, int16_t Gb); - -/** Calculation of raw object output - * - * Preprocessing of the raw object value - * - * @param[in] object_new_raw object temperature from @link MLX90632_RAM_1 @endlink or @link MLX90632_RAM_2 @endlink. - * The meas_number 1 or 2 is determined by value in @link MLX90632_STAT_CYCLE_POS @endlink - * @param[in] object_old_raw object temperature from @link MLX90632_RAM_1 @endlink or @link MLX90632_RAM_2 @endlink. - * The meas_number 1 or 2 is determined by value not in @link MLX90632_STAT_CYCLE_POS @endlink - * @param[in] ambient_new_raw ambient temperature from @link MLX90632_RAM_3 @endlink. The meas_number 1 or 2 is - * determined by value in @link MLX90632_STAT_CYCLE_POS @endlink - * @param[in] ambient_old_raw ambient temperature from @link MLX90632_RAM_3 @endlink. The meas_number 1 or 2 is - * determined by value not in @link MLX90632_STAT_CYCLE_POS @endlink - * @param[in] Ka Register value on @link MLX90632_EE_Ka @endlink - * - * @return Calculated object raw output - */ -double mlx90632_preprocess_temp_object(int16_t object_new_raw, int16_t object_old_raw, - int16_t ambient_new_raw, int16_t ambient_old_raw, - int16_t Ka); - -/** Calculation of ambient temperature - * - * DSPv5 implementation of ambient temperature calculation - * - * @param[in] ambient_new_raw ambient temperature from @link MLX90632_RAM_3 @endlink. The channel 1 or 2 is - * determined by value in @link MLX90632_STAT_CYCLE_POS @endlink - * @param[in] ambient_old_raw ambient temperature from @link MLX90632_RAM_3 @endlink. The channel 1 or 2 is - * determined by value not in @link MLX90632_STAT_CYCLE_POS @endlink - * @param[in] P_T Register value on @link MLX90632_EE_P_T @endlink - * @param[in] P_R Register value on @link MLX90632_EE_P_R @endlink - * @param[in] P_G Register value on @link MLX90632_EE_P_G @endlink - * @param[in] P_O Register value on @link MLX90632_EE_P_O @endlink - * @param[in] Gb Register value on @link MLX90632_EE_Gb @endlink - * - * @return Calculated ambient temperature degrees Celsius - */ -double mlx90632_calc_temp_ambient(int16_t ambient_new_raw, int16_t ambient_old_raw, int32_t P_T, - int32_t P_R, int32_t P_G, int32_t P_O, int16_t Gb); - -/** Calculation of object temperature - * - * DSPv5 implementation of object temperature calculation with customer - * calibration data - * - * @param[in] object object temperature from @link mlx90632_preprocess_temp_object @endlink - * @param[in] ambient ambient temperature from @link mlx90632_preprocess_temp_ambient @endlink - * @param[in] Ea Register value on @link MLX90632_EE_Ea @endlink - * @param[in] Eb Register value on @link MLX90632_EE_Eb @endlink - * @param[in] Ga Register value on @link MLX90632_EE_Ga @endlink - * @param[in] Fb Register value on @link MLX90632_EE_Fb @endlink - * @param[in] Fa Register value on @link MLX90632_EE_Fa @endlink - * @param[in] Ha Register value on @link MLX90632_EE_Ha @endlink - * @param[in] Hb Register value on @link MLX90632_EE_Hb @endlink - * - * @note emissivity Value provided by user of the object emissivity - * using @link mlx90632_set_emissivity @endlink function. - * - * @return Calculated object temperature in milliCelsius - */ -double mlx90632_calc_temp_object(int32_t object, int32_t ambient, - int32_t Ea, int32_t Eb, int32_t Ga, int32_t Fa, int32_t Fb, - int16_t Ha, int16_t Hb); - -/** Calculation of object temperature when the environment temperature differs from the sensor temperature - * - * when the object has emissivity lower than 1 then it does not just emit InfraRed light, but also reflects it. - * That is why measurement of the ambient temperature around object is important to help calculating more precise object temperature. - * This function makes it possible to add object environment temperature and offset it with sensor's ambient temperature to calculate more precise - * object temperature. DSPv5 implementation of object temperature calculation with customer - * calibration data - * - * @param[in] object object temperature from @link mlx90632_preprocess_temp_object @endlink - * @param[in] ambient sensor ambient temperature from @link mlx90632_preprocess_temp_ambient @endlink - * @param[in] reflected reflected (environment) temperature from a sensor different than the MLX90632 or acquired by other means - * @param[in] Ea Register value on @link MLX90632_EE_Ea @endlink - * @param[in] Eb Register value on @link MLX90632_EE_Eb @endlink - * @param[in] Ga Register value on @link MLX90632_EE_Ga @endlink - * @param[in] Fb Register value on @link MLX90632_EE_Fb @endlink - * @param[in] Fa Register value on @link MLX90632_EE_Fa @endlink - * @param[in] Ha Register value on @link MLX90632_EE_Ha @endlink - * @param[in] Hb Register value on @link MLX90632_EE_Hb @endlink - * - * @note emissivity Value provided by user of the object emissivity - * using @link mlx90632_set_emissivity @endlink function. - * - * @return Calculated object temperature in milliCelsius - */ -double mlx90632_calc_temp_object_reflected(int32_t object, int32_t ambient, double reflected, - int32_t Ea, int32_t Eb, int32_t Ga, int32_t Fa, int32_t Fb, - int16_t Ha, int16_t Hb); - -/** Initialize MLX90632 driver and confirm EEPROM version - * - * EEPROM version is important to match sensor EEPROM content and calculations. - * This is why this function checks for correct EEPROM version before it does - * checksum validation of the EEPROM content. - * - * @note EEPROM version can have swapped high and low bytes due to CPU or I2C. - * Please confirm that i2c read (16bit) is functioning as expected. - * - * @retval 0 Successfully initialized MLX90632 driver, extended range measurement not supported - * @retval @link ERANGE @endlink Successfully initialized MLX90632 driver, extended range measurement is supported - * @retval <0 Something went wrong. Consult errno.h for more details. - */ -int32_t mlx90632_init(void); - -/** Trigger start measurement for mlx90632 - * - * Trigger measurement cycle and wait for data to be ready. It does not read anything, just triggers and completes. - * - * @retval <0 Something failed. Check errno.h for more information - * @retval >=0 Channel position where new (recently updated) measurement can be found - * - * @note This function is using usleep so it is blocking! - */ -int mlx90632_start_measurement(void); - -/** Set emissivity which is retained in single variable. - * - * @param[in] value Value provided by user of object emissivity. Defaults to 1.0 and cannot be 0.0. - * - * @warning This is not suitable for multi-process calculations as we do not use instances - */ -void mlx90632_set_emissivity(double value); - -/** Read value of emissivity - */ -double mlx90632_get_emissivity(void); - -/** Trigger start of burst measurement for mlx90632 - * - * Trigger a single measurement cycle and wait for data to be ready. It does not read anything, just triggers and completes. - * The SOB bit is set so that the complete measurement table is re-freshed. - * - * @note The SOB bit is cleared internally by the mlx90632 immediately after the measurement has started. - * - * @retval <0 Something failed. Check errno.h for more information - * @retval 0 New data is available and waiting to be processed - * - * @note This function is using usleep and msleep. Because of usleep it is blocking, while msleep implementation can have a thread switch! - * In case both are blocking expect up to 2 second freeze of CPU in worse case scenario (depending on Refresh rate setting), so - * you might also need to take care of Watch Dog. - */ -int32_t mlx90632_start_measurement_burst(void); - -/** Reads the refresh rate and calculates the time needed for a single measurment from the EEPROM settings. - * - * @param[in] meas Measurement to read the frefresh rate for - * - * @retval >=0 Refresh time in ms - * @retval <0 Something went wrong. Check errno.h for more details. - */ -int32_t mlx90632_get_measurement_time(uint16_t meas); - -/** Reads the refresh rate and calculates the time needed for a whole measurment table from the EEPROM settings. - * - * The function is returning valid measurement time only for burst mode measurements. - * An error will be returned if it is called with a continuous measurement type parameter. - * - * @retval >=0 Refresh time in ms - * @retval <0 Something went wrong. Check errno.h for more details. - */ -int32_t mlx90632_calculate_dataset_ready_time(void); - -/** Trigger system reset for mlx90632 - * - * Perform full reset of mlx90632 using reset command. - * It also waits for at least 150us to ensure the mlx90632 device is properly reset and ready for further communications. - * - * @retval <0 Something failed. Check errno.h for more information - * @retval 0 The mlx90632 device was properly reset and is now ready for communication. - * - * @note This function is using usleep so it is blocking! - */ -int32_t mlx90632_addressed_reset(void); - -/** Sets the refresh rate of the sensor using the MLX90632_EE_MEAS_1 and MLX90632_EE_MEAS_2 registers - * - * @param[in] measRate refresh rate to set with #mlx90632_meas_e - * - * @retval <0 Something went wrong. Consult errno.h for more details. - */ -int32_t mlx90632_set_refresh_rate(mlx90632_meas_t measRate); - -/** Gets the value in MLX90632_EE_MEAS_1 and converts it to the appropriate MLX90632_MEAS enum - * - * @retval MLX90632_MEAS_HZ_ERROR if there is an error - * @retval refresh_rate as the #mlx90632_meas_e - */ -mlx90632_meas_t mlx90632_get_refresh_rate(void); - -///@} - -#ifdef TEST -int32_t mlx90632_read_temp_ambient_raw(int16_t *ambient_new_raw, int16_t *ambient_old_raw); -int32_t mlx90632_read_temp_object_raw(int32_t start_measurement_ret, - int16_t *object_new_raw, int16_t *object_old_raw); - -#endif - -#endif diff --git a/old/mlx90632_depends.h b/old/mlx90632_depends.h deleted file mode 100644 index 28af8ac..0000000 --- a/old/mlx90632_depends.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @file mlx90632_depends.h - * @brief MLX90632 driver dependencies - * @internal - * - * @copyright (C) 2017 Melexis N.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @endinternal - * - * @addtogroup mlx90632_API_depends MLX90632 Driver Library Externally defined - * @brief Functions to be defined externally as they are uC or OS dependent - * - * @details - * Some functions need to be declared externally as they are different for each - * uC or OS. Depends on platform implementation SMBus functions read 8 bytes, - * but MLX90632 has 16bit address registers (some are also 32bit). So reading - * at least 16bits at time is recommended. - * - * @{ - */ -#ifndef _MLX90632_DEPENDS_LIB_ -#define _MLX90632_DEPENDS_LIB_ - - -/** Read the register_address value from the mlx90632 - * - * i2c read is processor specific and this function expects to have address of mlx90632 known, as it operates purely on - * register addresses. - * - * @note Needs to be implemented externally - * @param[in] register_address Address of the register to be read from - * @param[out] *value pointer to where read data can be written - - * @retval 0 for success - * @retval <0 for failure - */ -extern int32_t mlx90632_i2c_read(int16_t register_address, uint16_t *value); - -/** Write value to register_address of the mlx90632 - * - * i2c write is processor specific and this function expects to have address of mlx90632 known, as it operates purely - * on register address and value to be written to. - * - * @note Needs to be implemented externally - * @param[in] register_address Address of the register to be read from - * @param[in] value value to be written to register address of mlx90632 - - * @retval 0 for success - * @retval <0 for failure - */ -extern int32_t mlx90632_i2c_write(int16_t register_address, uint16_t value); - -/** Blocking function for sleeping in microseconds - * - * Range of microseconds which are allowed for the thread to sleep. This is to avoid constant pinging of sensor if the - * data is ready. - * - * @note Needs to be implemented externally - * @param[in] min_range Minimum amount of microseconds to sleep - * @param[in] max_range Maximum amount of microseconds to sleep - */ -extern void usleep(int min_range, int max_range); - -/** Blocking function for sleeping in milliseconds - * - * milliseconds which are allowed for the thread to sleep. This is to avoid constant pinging of sensor - * while the measurement is ongoing in sleeping step mode. - * - * @note Needs to be implemented externally - * @param[in] msecs Amount of milliseconds to sleep - */ -extern void msleep(int msecs); - -///@} -#endif diff --git a/old/mlx90632_extended_meas.c b/old/mlx90632_extended_meas.c deleted file mode 100644 index 2561cb1..0000000 --- a/old/mlx90632_extended_meas.c +++ /dev/null @@ -1,366 +0,0 @@ -/** - * @Extended range measurements implementation for MLX90632 driver with virtual i2c communication - * @internal - * - * @copyright (C) 2017 Melexis N.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @endinternal - * - * @details - * - * @addtogroup mlx90632_private MLX90632 Internal library functions - * @{ - * - */ -#include -#include -#include - -#include "mlx90632.h" -#include "mlx90632_depends.h" - -#define POW10 10000000000LL - -#ifndef VERSION -#define VERSION "test" -#endif - -#ifndef STATIC -#define STATIC static -#endif - -/** Read ambient raw old and new values for the extended range based on @link mlx90632_start_measurement @endlink return value. - * - * Two i2c_reads are needed to obtain necessary raw ambient values from the sensor, as they are then - * preprocessed before going to calculation functions - * @param[out] *ambient_new_raw Pointer to memory location where new ambient value from sensor is stored - * @param[out] *ambient_old_raw Pointer to memory location where old ambient value from sensor is stored - * - * @retval 0 Successfully read both values - * @retval <0 Something went wrong. Check errno.h for more details. - */ -STATIC int32_t mlx90632_read_temp_ambient_raw_extended(int16_t *ambient_new_raw, int16_t *ambient_old_raw) -{ - int32_t ret; - uint16_t read_tmp; - - ret = mlx90632_i2c_read(MLX90632_RAM_3(17), &read_tmp); - if (ret < 0) - return ret; - *ambient_new_raw = (int16_t)read_tmp; - - ret = mlx90632_i2c_read(MLX90632_RAM_3(18), &read_tmp); - if (ret < 0) - return ret; - *ambient_old_raw = (int16_t)read_tmp; - - return ret; -} - -/** Read object raw values for the extended range based on @link mlx90632_start_measurement @endlink return value. - * - * Six i2c_reads are needed to obtain necessary raw object values from the sensor. These values are grouped and then - * averaged before return of the function. After that they are then preprocessed before going to - * calculation functions - * - * @param[out] *object_new_raw Pointer to memory location where average of new object values from sensor is stored - * - * @retval 0 Successfully read values - * @retval <0 Something went wrong. Check errno.h for more details. - */ -STATIC int32_t mlx90632_read_temp_object_raw_extended(int16_t *object_new_raw) -{ - int32_t ret; - uint16_t read_tmp; - int32_t read; - - ret = mlx90632_i2c_read(MLX90632_RAM_1(17), &read_tmp); - if (ret < 0) - return ret; - - read = (int16_t)read_tmp; - - ret = mlx90632_i2c_read(MLX90632_RAM_2(17), &read_tmp); - if (ret < 0) - return ret; - - read = read - (int16_t)read_tmp; - - ret = mlx90632_i2c_read(MLX90632_RAM_1(18), &read_tmp); - if (ret < 0) - return ret; - - read = read - (int16_t)read_tmp; - - ret = mlx90632_i2c_read(MLX90632_RAM_2(18), &read_tmp); - if (ret < 0) - return ret; - - read = (read + (int16_t)read_tmp) / 2; - - ret = mlx90632_i2c_read(MLX90632_RAM_1(19), &read_tmp); - if (ret < 0) - return ret; - - read = read + (int16_t)read_tmp; - - ret = mlx90632_i2c_read(MLX90632_RAM_2(19), &read_tmp); - if (ret < 0) - return ret; - - read = read + (int16_t)read_tmp; - - if (read > 32767 || read < -32768) - return -EINVAL; - - *object_new_raw = (int16_t)read; - - return ret; -} - -int32_t mlx90632_read_temp_raw_extended(int16_t *ambient_new_raw, int16_t *ambient_old_raw, int16_t *object_new_raw) -{ - int32_t ret, start_measurement_ret; - int tries = 3; - - // trigger and wait for measurement to complete - while (tries-- > 0) - { - start_measurement_ret = mlx90632_start_measurement(); - if (start_measurement_ret < 0) - return start_measurement_ret; - - if (start_measurement_ret == 19) - break; - } - - if (tries < 0) - { - // data not ready - return -ETIMEDOUT; - } - - /** Read new and old **ambient** values from sensor */ - ret = mlx90632_read_temp_ambient_raw_extended(ambient_new_raw, ambient_old_raw); - if (ret < 0) - return ret; - - /** Read new **object** value from sensor */ - ret = mlx90632_read_temp_object_raw_extended(object_new_raw); - - return ret; -} - -int32_t mlx90632_read_temp_raw_extended_burst(int16_t *ambient_new_raw, int16_t *ambient_old_raw, int16_t *object_new_raw) -{ - int32_t ret, start_measurement_ret; - - // trigger and wait for measurement to complete - start_measurement_ret = mlx90632_start_measurement_burst(); - if (start_measurement_ret < 0) - return start_measurement_ret; - - /** Read new and old **ambient** values from sensor */ - ret = mlx90632_read_temp_ambient_raw_extended(ambient_new_raw, ambient_old_raw); - if (ret < 0) - return ret; - - /** Read new **object** value from sensor */ - ret = mlx90632_read_temp_object_raw_extended(object_new_raw); - - return ret; -} - -double mlx90632_preprocess_temp_ambient_extended(int16_t ambient_new_raw, int16_t ambient_old_raw, int16_t Gb) -{ - double VR_Ta, kGb; - - kGb = ((double)Gb) / 1024.0; - - VR_Ta = ambient_old_raw + kGb * (ambient_new_raw / (MLX90632_REF_3)); - return ((ambient_new_raw / (MLX90632_REF_3)) / VR_Ta) * 524288.0; -} - -double mlx90632_preprocess_temp_object_extended(int16_t object_new_raw, int16_t ambient_new_raw, - int16_t ambient_old_raw, int16_t Ka) -{ - double VR_IR, kKa; - - kKa = ((double)Ka) / 1024.0; - - VR_IR = ambient_old_raw + kKa * (ambient_new_raw / (MLX90632_REF_3)); - return ((object_new_raw / (MLX90632_REF_12)) / VR_IR) * 524288.0; -} - -double mlx90632_calc_temp_ambient_extended(int16_t ambient_new_raw, int16_t ambient_old_raw, int32_t P_T, - int32_t P_R, int32_t P_G, int32_t P_O, int16_t Gb) -{ - double Asub, Bsub, Ablock, Bblock, Cblock, AMB; - - AMB = mlx90632_preprocess_temp_ambient_extended(ambient_new_raw, ambient_old_raw, Gb); - - Asub = ((double)P_T) / (double)17592186044416.0; - Bsub = AMB - ((double)P_R / (double)256.0); - Ablock = Asub * (Bsub * Bsub); - Bblock = (Bsub / (double)P_G) * (double)1048576.0; - Cblock = (double)P_O / (double)256.0; - - return Bblock + Ablock + Cblock; -} - -/** Iterative calculation of object temperature - * - * The DSP requires few iterations to reduce noise for object temperature. Since - * each iteration requires same calculations this helper function is - * implemented. - * - * @param[in] prev_object_temp previously calculated object temperature. If - * there is no previously calculated temperature - * input 25.0 - * @param[in] object object temperature from @link mlx90632_preprocess_temp_object_extended @endlink - * @param[in] TAdut ambient temperature coefficient - * @param[in] TaTr4 compensation coefficient for reflected (environment) temperature - * @param[in] Ga Register value on @link MLX90632_EE_Ga @endlink - * @param[in] Fa Register value on @link MLX90632_EE_Fa @endlink - * @param[in] Fb Register value on @link MLX90632_EE_Fb @endlink - * @param[in] Ha Register value on @link MLX90632_EE_Ha @endlink - * @param[in] Hb Register value on @link MLX90632_EE_Hb @endlink - * @param[in] emissivity Value provided by user of the object emissivity - * - * @return Calculated object temperature for current iteration in milliCelsius - */ -static double mlx90632_calc_temp_object_iteration_extended(double prev_object_temp, int32_t object, double TAdut, double TaTr4, - int32_t Ga, int32_t Fa, int32_t Fb, int16_t Ha, int16_t Hb, - double emissivity) -{ - double calcedGa, calcedGb, calcedFa, first_sqrt; - // temp variables - double KsTAtmp, Alpha_corr; - double Ha_customer, Hb_customer; - - Ha_customer = Ha / ((double)16384.0); - Hb_customer = Hb / ((double)1024.0); - calcedGa = ((double)Ga * (prev_object_temp - 25)) / ((double)68719476736.0); - KsTAtmp = (double)Fb * (TAdut - 25); - calcedGb = KsTAtmp / ((double)68719476736.0); - Alpha_corr = (((double)(Fa * POW10)) * Ha_customer * (double)(1.0 + calcedGa + calcedGb)) / - ((double)70368744177664.0); - calcedFa = object / (emissivity * (Alpha_corr / POW10)); - - first_sqrt = sqrt(calcedFa + TaTr4); - - return sqrt(first_sqrt) - 273.15 - Hb_customer; -} - -double mlx90632_calc_temp_object_extended(int32_t object, int32_t ambient, double reflected, - int32_t Ea, int32_t Eb, int32_t Ga, int32_t Fa, int32_t Fb, - int16_t Ha, int16_t Hb) -{ - double kEa, kEb, TAdut; - double temp = 25.0; - double tmp_emi = mlx90632_get_emissivity(); - double TaTr4; - double ta4; - int8_t i; - - kEa = ((double)Ea) / ((double)65536.0); - kEb = ((double)Eb) / ((double)256.0); - TAdut = (((double)ambient) - kEb) / kEa + 25; - - TaTr4 = reflected + 273.15; - TaTr4 = TaTr4 * TaTr4; - TaTr4 = TaTr4 * TaTr4; - ta4 = TAdut + 273.15; - ta4 = ta4 * ta4; - ta4 = ta4 * ta4; - TaTr4 = TaTr4 - (TaTr4 - ta4) / tmp_emi; - - //iterate through calculations - for (i = 0; i < 5; ++i) - { - temp = mlx90632_calc_temp_object_iteration_extended(temp, object, TAdut, TaTr4, Ga, Fa / 2, Fb, Ha, Hb, tmp_emi); - } - - return temp; -} - -int32_t mlx90632_set_meas_type(uint8_t type) -{ - int32_t ret; - uint16_t reg_ctrl; - - if ((type != MLX90632_MTYP_MEDICAL) & (type != MLX90632_MTYP_EXTENDED) & (type != MLX90632_MTYP_MEDICAL_BURST) & (type != MLX90632_MTYP_EXTENDED_BURST)) - return -EINVAL; - - ret = mlx90632_addressed_reset(); - if (ret < 0) - return ret; - - ret = mlx90632_i2c_read(MLX90632_REG_CTRL, ®_ctrl); - if (ret < 0) - return ret; - - reg_ctrl = reg_ctrl & (~MLX90632_CFG_MTYP_MASK & ~MLX90632_CFG_PWR_MASK); - reg_ctrl |= (MLX90632_MTYP_STATUS(MLX90632_MEASUREMENT_TYPE_STATUS(type)) | MLX90632_PWR_STATUS_HALT); - - ret = mlx90632_i2c_write(MLX90632_REG_CTRL, reg_ctrl); - if (ret < 0) - return ret; - - ret = mlx90632_i2c_read(MLX90632_REG_CTRL, ®_ctrl); - if (ret < 0) - return ret; - - reg_ctrl = reg_ctrl & ~MLX90632_CFG_PWR_MASK; - if (MLX90632_MEASUREMENT_BURST_STATUS(type)) - { - reg_ctrl |= MLX90632_PWR_STATUS_SLEEP_STEP; - } - else - { - reg_ctrl |= MLX90632_PWR_STATUS_CONTINUOUS; - } - - ret = mlx90632_i2c_write(MLX90632_REG_CTRL, reg_ctrl); - - return ret; -} - -int32_t mlx90632_get_meas_type(void) -{ - int32_t ret; - uint16_t reg_ctrl; - uint16_t reg_temp; - - ret = mlx90632_i2c_read(MLX90632_REG_CTRL, ®_temp); - if (ret < 0) - return ret; - - reg_ctrl = MLX90632_MTYP(reg_temp); - - if ((reg_ctrl != MLX90632_MTYP_MEDICAL) & (reg_ctrl != MLX90632_MTYP_EXTENDED)) - return -EINVAL; - - reg_temp = MLX90632_CFG_PWR(reg_temp); - - if (reg_temp == MLX90632_PWR_STATUS_SLEEP_STEP) - return MLX90632_BURST_MEASUREMENT_TYPE(reg_ctrl); - - if (reg_temp != MLX90632_PWR_STATUS_CONTINUOUS) - return -EINVAL; - - return reg_ctrl; -} -///@} diff --git a/old/mlx90632_extended_meas.h b/old/mlx90632_extended_meas.h deleted file mode 100644 index fd71ae9..0000000 --- a/old/mlx90632_extended_meas.h +++ /dev/null @@ -1,156 +0,0 @@ -/** - * @file mlx90632_extended_meas.h - * @brief MLX90632 extended range measurement support functions - * @internal - * - * @copyright (C) 2017 Melexis N.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * @endinternal - * - * @addtogroup mlx90632_API MLX90632 Driver Library API - */ -#ifndef _MLX90632_EXTENDED_MEAS_LIB_ -#define _MLX90632_EXTENDED_MEAS_LIB_ - -/** Read raw ambient and object temperature for extended range - * - * Trigger and read raw ambient and object temperatures. This values still need - * to be pre-processed via @link mlx90632_preprocess_temp_ambient_extended @endlink and @link - * mlx90632_preprocess_temp_object_extended @endlink functions and then processed via @link - * mlx90632_calc_temp_ambient_extended @endlink and @link mlx90632_calc_temp_object_extended @endlink - * to retrieve values in milliCelsius - * - * @param[out] ambient_new_raw Pointer to where new raw ambient temperature is written - * @param[out] object_new_raw Pointer to where new raw object temperature is written - * @param[out] ambient_old_raw Pointer to where old raw ambient temperature is written - * @param[out] object_old_raw Pointer to where old raw object temperature is written - * - * @retval 0 Successfully read both temperatures - * @retval <0 Something went wrong. Check errno.h for more details - */ -int32_t mlx90632_read_temp_raw_extended(int16_t *ambient_new_raw, int16_t *ambient_old_raw, int16_t *object_new_raw); - -/** Read raw ambient and object temperature for extended range sleeping step mode - * - * Trigger and read raw ambient and object temperatures. This values still need - * to be pre-processed via @link mlx90632_preprocess_temp_ambient_extended @endlink and @link - * mlx90632_preprocess_temp_object_extended @endlink functions and then processed via @link - * mlx90632_calc_temp_ambient_extended @endlink and @link mlx90632_calc_temp_object_extended @endlink - * to retrieve values in milliCelsius - * - * @param[out] ambient_new_raw Pointer to where new raw ambient temperature is written - * @param[out] object_new_raw Pointer to where new raw object temperature is written - * @param[out] ambient_old_raw Pointer to where old raw ambient temperature is written - * @param[out] object_old_raw Pointer to where old raw object temperature is written - * - * @retval 0 Successfully read both temperatures - * @retval <0 Something went wrong. Check errno.h for more details - */ -int32_t mlx90632_read_temp_raw_extended_burst(int16_t *ambient_new_raw, int16_t *ambient_old_raw, int16_t *object_new_raw); - -/** Calculation of raw ambient output for the extended range - * - * Preprocessing of the raw ambient value - * - * @param[in] ambient_new_raw ambient temperature from @link MLX90632_RAM_3 @endlink from meas num 17 - * @param[in] ambient_old_raw ambient temperature from @link MLX90632_RAM_3 @endlink from meas num 18 - * @param[in] Gb Register value on @link MLX90632_EE_Gb @endlink - * - * @return Calculated ambient raw output - */ -double mlx90632_preprocess_temp_ambient_extended(int16_t ambient_new_raw, int16_t ambient_old_raw, int16_t Gb); - -/** Calculation of raw object output for the extended range - * - * Preprocessing of the raw object value - * - * @param[in] object_new_raw object temperature from @link MLX90632_RAM_1 @endlink and @link MLX90632_RAM_2 @endlink - * from meas_number 17, 18 and 19 - * @param[in] ambient_new_raw ambient temperature from @link MLX90632_RAM_3 @endlink from meas num 17 - * @param[in] ambient_old_raw ambient temperature from @link MLX90632_RAM_3 @endlink from meas num 18 - * @param[in] Ka Register value on @link MLX90632_EE_Ka @endlink - * - * @return Calculated object raw output - */ -double mlx90632_preprocess_temp_object_extended(int16_t object_new_raw, int16_t ambient_new_raw, - int16_t ambient_old_raw, int16_t Ka); - -/** Calculation of ambient temperature for the extended range - * - * @param[in] ambient_new_raw ambient temperature from @link MLX90632_RAM_3 @endlink from meas num 17 - * @param[in] ambient_old_raw ambient temperature from @link MLX90632_RAM_3 @endlink from meas num 18 - * @param[in] P_T Register value on @link MLX90632_EE_P_T @endlink - * @param[in] P_R Register value on @link MLX90632_EE_P_R @endlink - * @param[in] P_G Register value on @link MLX90632_EE_P_G @endlink - * @param[in] P_O Register value on @link MLX90632_EE_P_O @endlink - * @param[in] Gb Register value on @link MLX90632_EE_Gb @endlink - * - * @return Calculated ambient temperature degrees Celsius - */ -double mlx90632_calc_temp_ambient_extended(int16_t ambient_new_raw, int16_t ambient_old_raw, int32_t P_T, - int32_t P_R, int32_t P_G, int32_t P_O, int16_t Gb); - -/** Calculation of object temperature for the extended range - * - * @param[in] object object temperature from @link mlx90632_preprocess_temp_object @endlink - * @param[in] ambient sensor ambient temperature from @link mlx90632_preprocess_temp_ambient @endlink - * @param[in] reflected reflected (environment) temperature from a sensor different than the MLX90632 or acquired by other means - * @param[in] Ea Register value on @link MLX90632_EE_Ea @endlink - * @param[in] Eb Register value on @link MLX90632_EE_Eb @endlink - * @param[in] Ga Register value on @link MLX90632_EE_Ga @endlink - * @param[in] Fb Register value on @link MLX90632_EE_Fb @endlink - * @param[in] Fa Register value on @link MLX90632_EE_Fa @endlink - * @param[in] Ha Register value on @link MLX90632_EE_Ha @endlink - * @param[in] Hb Register value on @link MLX90632_EE_Hb @endlink - * - * @note emissivity Value provided by user of the object emissivity - * using @link mlx90632_set_emissivity @endlink function. - * - * @return Calculated object temperature in milliCelsius - */ -double mlx90632_calc_temp_object_extended(int32_t object, int32_t ambient, double reflected, - int32_t Ea, int32_t Eb, int32_t Ga, int32_t Fa, int32_t Fb, - int16_t Ha, int16_t Hb); - -/** Switch the measurement type of the MLX90632 - * - * @param[in] type measurement type to switch to - * - * @note the available types are defined as @link MLX90632_MTYP_MEDICAL @endlink - * and @link MLX90632_MTYP_EXTENDED @endlink - * - * @retval 0 Successfully swithed the meausurement mode - * @retval <0 Something went wrong. Check errno.h for more details - */ -int32_t mlx90632_set_meas_type(uint8_t type); - -/** Get the current measurement type set in the MLX90632 - * - * @note the available types are defined as @link MLX90632_MTYP_MEDICAL @endlink - * and @link MLX90632_MTYP_EXTENDED @endlink - * - * @retval = @link MLX90632_MTYP_MEDICAL @endlink - medical range measurement type - * @retval = @link MLX90632_MTYP_EXTENDED @endlink - extended range measurement type - * @retval <0 Something went wrong. Consult errno.h for more details. - */ -int32_t mlx90632_get_meas_type(void); - -#ifdef TEST -int32_t mlx90632_read_temp_ambient_raw_extended(int16_t *ambient_new_raw, int16_t *ambient_old_raw); -int32_t mlx90632_read_temp_object_raw_extended(int16_t *object_new_raw); - -#endif - -#endif diff --git a/package.json b/package.json new file mode 100644 index 0000000..064a61d --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "ambit-release-automation", + "private": true, + "version": "1.0.0", + "description": "Minimal semantic-release tooling for Ambit firmware", + "repository": { + "type": "git", + "url": "https://github.com/Jan-IngenHousz-Institute/ambit.git" + }, + "devDependencies": { + "@semantic-release/github": "11.0.6", + "conventional-changelog-conventionalcommits": "8.0.0", + "semantic-release": "25.0.5" + } +} diff --git a/par_calibrator.py b/par_calibrator.py deleted file mode 100644 index 316eae5..0000000 --- a/par_calibrator.py +++ /dev/null @@ -1,122 +0,0 @@ -import serial -import time -import sys -def main(input_str = "", output = "", hello = False): - with serial.Serial("COM31", 115200) as ser: - readable = "" - timer0 = time.perf_counter() - ser.write(("\r\n").encode()) - while time.perf_counter() - timer0 < 5: - time.sleep(0.01) - try: - pass - except KeyboardInterrupt: - break - if input_str != "": - ser.write((input_str + "\r\n").encode()) - print("write", input_str) - time.sleep(0.1) - if output == "": return 0 - - while ser.in_waiting > 0: - _c = ser.read() - if (_c > bytes([128]) or _c < bytes([32])) and not (_c == b"\r" or _c == b"\n"or _c == b"\t"): - print(int.from_bytes(_c)) - if hello: - if int.from_bytes(_c) == 133: - ser.write("hello\r\n".encode()) - print("write hello") - else: - readable += _c.decode() - if _c == b"\n": - print(readable, end="") - if output != "": - if readable.startswith(output): - return 1 - readable = "" - - -def cali_par(ground_truth = 1000): - with serial.Serial("COM31", 115200) as ser: - readable = "" - - - ser.write("get_par".encode()) - print("write get_par") - timer0 = time.perf_counter() - while time.perf_counter() - timer0 < 1: - time.sleep(0.01) - if ser.in_waiting > 0: - break - - output = "" - line_count = 0 - while ser.in_waiting > 0: - _c = ser.read() - if (_c > bytes([128]) or _c < bytes([32])) and not (_c == b"\r" or _c == b"\n"or _c == b"\t"): - print(int.from_bytes(_c)) - - else: - readable += _c.decode() - if _c == b"\n": - print(line_count, readable, end="") - if (line_count == 0): output = readable.strip() - line_count += 1 - readable = "" - - if line_count == 2: - try: - meas = float(output) - print("get readings") - - if (meas > 0): - print(f'{ground_truth / meas:.6f}') - if (ground_truth / meas > 0.65 or ground_truth / meas < 0.1): - print("calibration failed") - return 0 - ser.write(f"set_spec,{ground_truth / meas:.6f}".encode()) - - - except: - print("get readings failed") - return 0 - - - - - - - - - - -if __name__ == "__main__": - par = 0 - if len(sys.argv) == 3: - print("PAR:", sys.argv[1]) - par = int(sys.argv[1]) - ambit_name = sys.argv[2] - else: - print("PAR: 0") - - - - if main("hello,", "NEW Name Here", True) == 1: - print("hello") - main("set_name,"+ambit_name, "") - time.sleep(0.25) - main("set_emit,0.9", "") - time.sleep(0.25) - main("set_act,0.1", "") - time.sleep(0.25) - if par == 0: - cali_par(ground_truth = 0) - else: - cali_par(ground_truth = par) - - input("Press Enter to do baseline") - main("baseline,1", "") - time.sleep(2) - main("hello,", "NEW Name Here") - - \ No newline at end of file diff --git a/plans/AMBIT_COMMAND35_SPECPAR.md b/plans/AMBIT_COMMAND35_SPECPAR.md new file mode 100644 index 0000000..2fa2dcc --- /dev/null +++ b/plans/AMBIT_COMMAND35_SPECPAR.md @@ -0,0 +1,885 @@ +# Command 35 — three-tier AS7341 calibration: spectral characteristics + PAR + +Intended reader: an agent or developer implementing this in the **ambit** repo +(`Documents/Git-repo/ambit`). The analysis behind §1–§3 was done in the **miniPar** repo; +everything about ambit was read directly from its source and verified against the commit on +disk. Line references were re-checked on 2026-08-17 against `fix/spec_overflow` @ `a1ff230`. + +**The architecture ports. The numbers are seeds, not calibration.** ambit has different +optics, window and diffuser, so every coefficient vector must eventually be re-measured on +ambit hardware against an LR1-B (goal A) and a Li-Cor Li-250A (goal B). Earlier revisions of +this plan therefore shipped `spec_sens` at 1.0 and `par_weight` at 0.0 — but an all-zero +weight vector makes the computed PAR *identically zero*, which is a worse starting point than +a vector fitted on a different unit of the same sensor. So miniPar's fitted vectors are now +carried in as **defaults** (§7), under conditions spelled out there: they encode miniPar's +optical stack, they are provisional, and they must not be mistaken for an ambit calibration. +§11 tracks what replaces them, and the `flags` calibration byte (§5a) exists to keep the +distinction visible on the wire. + +**Status fact that shapes the whole plan: cmd 35 has never been released.** `git tag +--contains a1ff230` is empty, the commit is not an ancestor of `origin/main`, `git show +v1.1.4:src/run_esp.cpp` has no `case 35`, and the Ambyte's `ambit_protocol.h` defines no +opcode 35. No fielded device answers it and no shipped host asks for it. Its 32-byte payload +is therefore **free to redefine**, which is why this plan rewrites cmd 35 in place instead of +adding a cmd 36 alongside it. The only consumer that exists anywhere is one notebook in the +Calibratron repo (§9). + +--- + +## 1. Two goals, one sensor + +| | goal | output | fitted against | +|---|---|---|---| +| **A** | spectral characteristics of the light | per-channel irradiance-like values (optionally a 1 nm spectrum, host-side) | LR1-B spectrometer | +| **B** | PAR | one scalar, µmol m⁻² s⁻¹ | tier 2: Li-Cor Li-250A. tier 3: a Li-250A **or** a transfer standard — §1a | + +They share the exposure normalisation and diverge immediately after. Goal B is **not** +computed from goal A's output — see §8. + +### 1a. Tier 3 does not require a Li-250A. Tier 2 does. + +The two tiers have different reference requirements, and conflating them has been costing +this plan clarity. + +**Tier 2 needs an absolute PAR reference across many source families.** It is where the +µmol scale enters and where spectral weighting is decided, so nothing but a proper quantum +sensor will do. That stays a Li-250A, and it stays a TODO (§11). + +**Tier 3 does not.** It is two per-device parameters fitted at one spectrum, and what it +actually measures is the ratio of this device's optical throughput to whatever the tier-2 +vector was fitted on. A **transfer standard** serves that: any instrument that computes PAR +with *the same* `par_weight`. A miniPAR is exactly that today, because §7c's seed *is* +miniPar's fleet vector — so at one spectrum both devices compute `w · s` over identical +weights, the spectral bias is common-mode, and it divides out of the ratio. The fit's +*linearity* is unaffected, which is what makes the host's quality gates still mean something. + +The cost is a pure multiplicative bias equal to the reference's own PAR error at the +calibration spectrum: ~2 % in-domain (§7c) but **4.4 % median / 9.8 % worst** by §2's +cross-source figures, which are the honest ones for a halogen bench lamp that is nothing +like miniPar's daylight-dominated set. Against the ~5 % absolute accuracy of a Li-250A that +is ~7 % combined rather than ~5 %. + +It is also **recoverable without repeating any bench work**, provided the host stores +`par_tier2` per sweep point: one later Li-250A comparison on a single device yields a scale +correction applicable to every stored sweep. That storage requirement is on the host, and the +Calibratron does it. + +So: **tier 3 against a Li-250A is an optional TODO, not a precondition.** A transfer-standard +fit is a conforming tier-3 calibration as long as the record names the reference and carries +the sweep. Two caveats to record with it: the fitted `par_slope` is specific to the +calibration lamp's spectrum, and a halogen lamp shifts colour temperature across an intensity +sweep, so the record should say how far the spectrum moved. + +## 2. The three tiers + +``` +tier 1 x = raw / (gain × tint) per sample, no calibration, pure arithmetic +tier 2 PAR_DEF = w · x fleet default, 10 floats, fitted once +tier 3 PAR = a · PAR_DEF + b per device, 2 floats, from an intensity sweep +``` + +Why this split, measured on miniPar (462 samples, 6 devices, daylight + canopy + gel +filters + office + LED panels): + +| PAR model | R² | median \|err\| | +|---|---|---| +| sum of the 8 visible channels, 1 fitted slope | 0.949 | 17.2% | +| fixed physics weights from the ams matrix, 1 fitted slope | 0.990 | 6.3% | +| 10 channels, freely fitted coefficients | 0.998 | ~2% | + +**Tier 2 must be fleet-level, not per-device.** An intensity sweep on one light source +moves the sample along a single ray in 10-space — it identifies a scale, not 10 weights. +Leave-one-device-out, fitting 10 coefficients from one device's own single-source +calibration rows and testing on a different source family: + +| | R² | median \|err\| | +|---|---|---| +| 10 coefficients fitted per device | −31 … −7443 | 434 … 779% | +| fleet `w` + 2 per-device params | 0.996 … 0.998 | 2.5 … 9.8% | + +The per-device 10-coefficient fit does not degrade gracefully; it produces arbitrary +signs and detonates on any spectrum outside its calibration set. + +The second row was **re-measured on 2026-08-17** while porting the vectors (§7c), over all +six devices in `miniPar/data/*.csv`: fleet `w` from the other devices, tier 3 from the +held-out device's LED sweep only, scored on daylight rows it never saw. Median across +devices **4.37 %**, worst device 9.82 %. Earlier revisions of this table quoted 0.5–3.1 % +from a narrower run; the wider figure is the one to plan against. + +**Tier 3 source choice.** Both work; daylight calibrates better, LEDs are repeatable on a +bench: daylight-calibrated → 2.4% median on LEDs; LED-calibrated → 4.4% median on +daylight (worst device 9.8%). + +## 3. What ambit does today + +`get_PAR_raw()` — [src/src/as7341/spec_meas.cpp:207-236](src/src/as7341/spec_meas.cpp): + +```cpp +const uint16_t counts = spec[SPEC_RAW_INDEX[n]]; // RAW ADC counts +if (n < 8) weighted_sum += (uint32_t) counts * SPEC_WEIGHT[n]; +else if (n == 8) nir_weighted = (uint32_t) counts * SPEC_WEIGHT[n]; +calc_par = weighted_sum * 0.006 - nir_weighted * 0.0075; +return calc_par * PAR_OFFSET; // PAR_OFFSET = 4 +``` + +- **No tier 1 at all.** Nothing in the live firmware divides by gain or by integration time + anywhere — the only occurrences of the 2.78 µs tick or a gain multiplier in `src/` are + inside dead driver functions and one commented-out line. +- It survives that only by **pinning the exposure**: `SPEC_ATIME 99`, `SPEC_ASTEP 499`, + `AS7341_GAIN_2X`, reprogrammed on every read by `dual_exposure()` + ([spec_meas.cpp:133](src/src/as7341/spec_meas.cpp)). The coefficients are silently + bound to those settings. +- **Tier 2 is integer weights**, `Spec_COE1..9` = `{12, 10, 11, 10, 10, 9, 7, 4, 1}` in + [spec_meas.h:19-27](src/src/as7341/spec_meas.h). Normalised to F1 that is + `1.00, 0.83, 0.92, 0.83, 0.83, 0.75, 0.58, 0.33` — much flatter than the ams matrix's + PAR weights (`1.00, 0.66, 0.57, 0.52, 0.43, 0.36, 0.46, 0.34`), i.e. close to the + uniform-sum regime that cost 17% median error on miniPar. F2 and F4 get identical + weights purely from integer quantisation. The scalars `0.006`, `0.0075` and + `PAR_OFFSET = 4` have no derivation anywhere in the source. +- **Clear is unused in PAR**; NIR is subtracted with its own scalar. No Clear-based + stray-light term, which is where most of the miniPar accuracy gain came from. +- **Tier 3 is half-present**: `ambit_calibration_local.spec_coef` (a single scalar, + validated 0.05–100) multiplies PAR. That is `a` with no `b`. +- No dark-offset vector, no per-channel spectral sensitivity, no storage for either. +- `Adafruit_AS7341::toBasicCounts()` exists at + [Adafruit_AS7341.cpp:920](src/src/as7341/Adafruit_AS7341.cpp) and uses `2.78 / 1000` + (**ms**), but it is **dead code** — no call site anywhere. It would also be unsafe here + as written: it issues live `getGain()`/`getATIME()`/`getASTEP()` reads, so after + `dual_exposure()` it sees the *high*-bank gain for every channel including F1–F4. + +### Cmd 35 is already the right acquisition command + +[run_esp.cpp:270](src/run_esp.cpp) returns 32 bytes: `format`, `atime`, `gain_low`, +`gain_high`, `astep`, `flags` (bit0 = full scale hit), `raw[10]`, `par`. Everything a host +needs for tier 1 is already on the wire. Its `par` field is still the legacy path, hence +the TODO at [run_esp.cpp:296](src/run_esp.cpp). + +Two defects in what it currently promises, both fixed by this plan: + +- The prescribed normalisation `raw / (gain × (atime+1) × (astep+1))` omits the tick + entirely **and treats the gain byte as a multiplier**. It is an `as7341_gain_t` + *ordinal* (n → 0.5·2ⁿ): ordinal 0 is 0.5×, so using it literally divides by **zero**; + ordinals 3, 4, 10 mean 4×, 8×, 512×. Ordinal 2 = multiplier 2 is the enum's only fixed + point, which is why the formula appears to work at the pinned exposure. +- The payload comment attributes Clear and NIR to the `gain_low` bank. They come from the + **high** bank — see §8. + +--- + +## 4. Decisions taken + +Recorded here so the implementation does not relitigate them. + +| # | decision | rationale | +|---|---|---| +| 1 | **Rewrite cmd 35; do not add cmd 36.** | 35 is unreleased and unconsumed; a second near-identical opcode would be permanent duplication bought with nothing. | +| 2 | **The five calibration vectors live *outside* `ambit_calibration_info_t`**, in their own NVS-backed struct with its own byte-explicit read-back. | That struct is blitted raw onto the wire by cmd 33/1 at its full `sizeof` ([run_esp.cpp:231](src/run_esp.cpp#L231)) and is mirrored field-for-field in the Ambyte's `ambit_protocol.h` under an explicit "MUST match ambit-1 nvs1.h — omitting it desyncs the framed read" warning. See §6 for why appending is worse than a clean break. | +| 3 | **Integration time in milliseconds**, tick `2.78e-3`. | The ams constants (dark offsets, reconstruction matrix) are published in ms. Anchor: ATIME 99 / ASTEP 499 → **139 ms**. | +| 4 | **The tier math lives only in the cmd 35 handler.** cmd 31, text `get_par`/`PAR`, and JSON `par`/`par_raw` keep the legacy path untouched. | Cmd 31 is frozen against a shipped Ambyte. Cmd 35 is the future primary spectral command and will supersede the rest once hardware-validated; until then two definitions coexist deliberately, not accidentally. | +| 5 | **The wire carries the gain *ordinal*; the firmware converts internally** via a `gain_multiplier()` helper in `spec_meas`, applied per bank. | The ordinal is compact, already documented as `as7341_gain_t 0-10`, and the host already derives a multiplier (the Calibratron CSV has a `gain_x` column). The helper goes in `spec_meas`, not the vendored Adafruit driver. | +| 6 | **Per-channel saturation and clip masks**, plus board-level status bits. | A payload that divides, offset-corrects and weights must say *which* channels are trustworthy. §5 has the layout. | +| 7 | **`spec_coef` is untouched and is not applied to the new `par`.** | Cmd 31 and deployed devices depend on it. `par_slope` is its successor, not a rename — a scalar on integer-weighted raw counts is a different quantity. | +| 8 | **`spec_sens` and `par_weight` ship seeded from the miniPar campaign; `par_slope`/`par_intercept` stay identity.** (Revised 2026-08-17 — these were originally 1.0 / 0.0.) | An all-zero weight vector makes PAR identically zero, which is less useful than a fit from another unit of the same sensor. Tier 3 is per-optics and is not seeded. The seeds are miniPar's optical stack and are not an ambit calibration — §7. | +| 9 | **`flags` is split into a negative-polarity condition byte and a positive-polarity calibration byte.** Provisional PAR is reported by *two* bits — bit8 `par_weight` is an ambit fleet fit, bit9 tier 3 stored for this device — not by one. | Decision 8 makes the old `par_weight_is_unset()` test permanently false, which would silently retire the only signal distinguishing a seeded device from a calibrated one. One bit could not express it either: the two halves of the PAR chain are calibrated by different people at different times. Positive polarity makes an all-zero word read as "nothing confirmed". §5a. | + +--- + +## 5. The rewritten cmd 35 payload + +Request unchanged: standard `0xA0` + 8-byte frame with `cmd_arr[0] = 35`, remaining seven +bytes unused. Response becomes `0xA1` + **80 B** + `0xF0`. + +Byte-explicit and naturally aligned (u16 on even offsets, f32 on multiples of 4), so it is +padding-free without `__attribute__((packed))` and without inheriting the ESP32-default- +alignment coupling that froze the cmd 33 structs at 140/48/248. All fields little-endian. + +``` + 0 u8 format = 1 bump only for a layout change + 1 u8 atime + 2 u8 gain_low as7341_gain_t ORDINAL, F1-F4 bank + 3 u8 gain_high as7341_gain_t ORDINAL, F5-F8 + NIR + Clear bank + 4 u16 astep + 6 u16 flags TWO ZONES WITH OPPOSITE POLARITY, one per byte — see §5a. + low byte = conditions, 1 means attention: + bit0 = any channel at digital full scale (== sat_mask != 0) + bit1 = any channel clipped at its dark offset (== clip_mask != 0) + bit2 = analog saturation reported by the AS7341 (ASAT, STATUS2) + bit3 = acquisition fault (I2C read failed or sensor absent) + bits 4-7 reserved, sent as 0 + high byte = calibration, 1 means confirmed: + bit8 = par_weight is an ambit fleet fit (0 = borrowed seed) + bit9 = tier-3 slope/intercept stored for this device + bits 10-15 reserved, sent as 0 + 8 u16 sat_mask bit i = channel i reached digital full scale +10 u16 clip_mask bit i = channel i clipped at its dark offset +12 u16 raw[10] unscaled counts: F1..F8, NIR, Clear +32 f32 chan[10] goal A: normalised, offset-corrected, spectrally scaled +72 f32 par goal B: par_slope * t2 + par_intercept +76 f32 par_tier2 goal B: t2, before slope/intercept +80 end +``` + +**`format` stays 1.** No host has ever observed the 32-byte form, so value 1 is not burned; +bumping to 2 would permanently reserve a number for a layout that never existed. + +Computation, in this order: + +``` +tint_ms = (atime + 1) * (astep + 1) * 2.78e-3 // MILLISECONDS — decision 3 +g(i) = gain_multiplier(bank(i)) // ordinal n -> 0.5 * 2^n +x[i] = raw[i] / (g(i) * tint_ms) // tier 1, basic counts +s[i] = max(0, x[i] - spec_offset[i]) // dark offset, clipped -> clip_mask +A[i] = s[i] * spec_sens[i] // goal A -> chan[i] +t2 = SUM_i par_weight[i] * s[i] // goal B tier 2 -> par_tier2 +PAR = par_slope * t2 + par_intercept // goal B tier 3 -> par +``` + +`bank(i)`: slots 0–3 (F1–F4) divide by `gain_low`; slots 4–9 (F5–F8, **NIR, Clear**) divide +by `gain_high`. See §8. + +- **`raw[]` stays on the wire** so a host can recompute everything and *verify* the firmware + rather than trust it. That property only holds with the calibration read-back of §6.4. +- **Basic counts get no slot**: the host derives them exactly from `raw[]` plus the four + exposure fields. +- **`par_tier2`** is exactly the quantity an onboarding intensity sweep regresses against the + Li-250A, so tier 3 can be fitted without inverting the stored slope/intercept. +- **`clip_mask` matters** because the offset clip is the only nonlinearity in the chain. +- **Full scale is computed from the reported `atime`/`astep`**, not from the compile-time + `SPEC_FULL_SCALE` macro. The macro is correct only while the exposure stays pinned, and + reporting the exposure exists precisely so it need not stay pinned. +- **`par` no longer equals cmd 31's `par`.** That equality is currently an explicit + conformance check at [HW_CONFORMANCE.md:120-123](plans/HW_CONFORMANCE.md) and must be + replaced with per-tier checks (§9). + +### 5a. Why `flags` has two zones with opposite polarity + +*Implemented.* As first written, bit2 was `par_weight_is_unset()` — literally "the tier-2 +vector is all zero". That was a sound signal while the vector shipped as zeros, because an +all-zero weight collapsed PAR to `par_intercept` and the flag was the only thing separating +"uncalibrated" from "dark". Seeding `par_weight` (§7c) makes that predicate permanently +false, so left alone it would retire the only warning a host ever gets — and a fresh device +would report a plausible PAR computed on another sensor's optics with nothing saying so. + +The replacement inverts the sense: **1 means confirmed calibrated**, and it lives in the high +byte, away from the condition bits. + +**Why inverted.** The payload is zero-initialised and reserved bits go out as 0, so a +"provisional = 1" bit reads as *calibrated* in every degenerate case — a truncated frame, a +misparsed offset, a field the firmware failed to set. Doubt should resolve to "not +calibrated": a spurious warning costs a question, while publishing seed-derived PAR as +science data costs a dataset. + +**Why zoned rather than mixed.** Bits 0–3 all mean "something is wrong". A single inverted +bit dropped among them is how a host ends up writing `if (flags) warn` and treating +*calibrated* as a fault. Splitting by byte makes the two polarities structural instead of a +comment someone has to notice, and both halves then fail safe in the same direction: an +all-zero `flags` word means "no fault reported, nothing confirmed calibrated". + +**Why two bits rather than one.** The two facts are independent and have different lifetimes. +`bit8` is a build property — is the compiled-in tier-2 vector an ambit fleet fit or the +miniPar seed — and is the same on every device running an image. `bit9` is per device: has +this unit been through a tier-3 sweep. Onboarding cares about `bit9` alone; a data consumer +wants both. OR-ing them into one bit would have thrown that away. + +```c +// nvs1.h — flip in the commit that lands an ambit-fitted fleet vector (§11). +static constexpr bool AMBIT_PAR_WEIGHT_IS_AMBIT_FIT = false; +extern bool ambit_spec_tier3_stored; // set from NVS key presence, and by the setters + +flags |= (AMBIT_PAR_WEIGHT_IS_AMBIT_FIT && + !par_weight_is_unset(...)) << 8; +flags |= ambit_spec_tier3_stored << 9; +``` + +`bit9` is keyed on **NVS key presence**, not on comparing floats to the defaults: a fitted +slope of exactly 1.0 is legitimate, and float equality against a constant is a poor test. +Either tier-3 key counts — a sweep through the origin has no intercept to store. `bit8` +additionally requires the live vector not be all-zero, so a build that claims an ambit fit +cannot vouch for a zero vector a host wrote over the setter; that is what keeps +`par_weight_is_unset()` load-bearing. + +Today both high bits are **0** on every device: the seed is not an ambit fit, and no unit has +been swept. That is the honest state, and §9's conformance expectation matches it. + +--- + +## 6. Calibration storage + +### 6.1 The state + +A new struct, deliberately **not** blitted onto any wire: + +```c +/* Spectral/PAR calibration. Kept out of ambit_calibration_info_t on purpose: that + * struct is memcpy'd onto the wire by cmd 33/1 at its full sizeof and is mirrored + * byte-for-byte in the ambyte's ambit_protocol.h, so it cannot grow without a + * coordinated reflash of every deployed logger. This one is read back through a + * byte-explicit, versioned layout (§6.4) and can be extended freely. */ +struct ambit_spec_calibration_t { + float spec_offset[10]; // goal A, dark offset; basic-count units, ms convention + float spec_sens[10]; // goal A, per-channel sensitivity; miniPar seed + float par_weight[10]; // goal B tier 2; miniPar fleet seed + float par_slope = 1.0f; // goal B tier 3, per device + float par_intercept = 0.0f; // goal B tier 3, per device +}; +``` + +Defaults: `spec_offset` from the ams workbook, `spec_sens` and `par_weight` from the miniPar +campaign — all three in §7. `par_slope`/`par_intercept` stay identity because they are +per-device by definition and nothing about miniPar's units transfers. Loaded by a new +`load_spec_calibration()` called from `load_info_from_nvs()`. NVS keys (namespace `config`, +≤15 chars): `spec_off`, `spec_sens`, `par_w`, `par_slope`, `par_icept`. + +### 6.2 NVS I/O — generalise, do not invent + +`save_calibration_float()` at [nvs1.cpp:61-85](src/nvs1.cpp) is already a verified blob +writer: `nvs_open` → `nvs_set_blob` → `nvs_commit` → `nvs_get_blob` → size check → `memcmp` +→ `esp_err_t`. It is scalar only because the length is hardcoded. Generalise it to +`save_calibration_blob(const char *key, const void *data, size_t len)` and make the float +version a one-line wrapper, so nothing existing changes. A 40-byte blob is a single NVS +entry, so it is strictly more atomic than the existing six-key `save_adpd_baseline()`. + +### 6.3 The loader guard this codebase does not have yet + +`Preferences::getBytes` copies a **shorter-than-expected** stored blob and returns the short +length, leaving the tail of the destination untouched. Reading straight into the live struct +would silently blend stored elements with defaults, undetectably afterwards. So: + +```c +if (preferences.isKey("spec_off")) { + float scratch[10]; + if (preferences.getBytes("spec_off", scratch, sizeof scratch) == sizeof scratch && + ambit_calibration::valid_spec_offset(scratch)) { + memcpy(spec_cal.spec_offset, scratch, sizeof scratch); + } +} +``` + +Read into scratch → check the returned length → run the predicate → only then adopt. Note +this is also where the handoff's original "appending fields is migration-free" property +stops: it holds for `isKey()`-guarded scalars, but a blob whose element count ever changes +is exactly the short-read case. + +### 6.4 Read-back — mandatory, not optional + +Because the vectors are off cmd 33/1, nothing else puts them on the wire, and without them +the "host can verify the firmware from `raw[]`" property of §5 does not hold. Add **cmd 33 +subtype 4**. This is purely additive: the existing `else` at +[run_esp.cpp:241-243](src/run_esp.cpp) answers a bare `ESP_CMD_END`, so an older image +degrades gracefully instead of going silent. + +``` + 0 u8 format = 1 + 1 u8 reserved + 2 u16 reserved + 4 f32 spec_offset[10] + 44 f32 spec_sens[10] + 84 f32 par_weight[10] +124 f32 par_slope +128 f32 par_intercept +132 end +``` + +Mirror it as a text verb (`get_spec_cal`) so the Calibratron can confirm a write landed +without speaking binary. *Implemented* in [src/do_command.h](src/do_command.h) as **five +labelled lines**, `:`, in the same order as the binary layout: + +``` +spec_offset:0.00196979,0.00724927,0.00319381,... 10 values +spec_sens:34.9506626,65.2894836,72.6979971,... 10 values +par_weight:333.463542,206.427139,-30.6130743,... 10 values +par_slope:1 +par_intercept:0 +``` + +Labelled lines rather than one flat row of 32 floats, for a specific reason: a host parsing +by *position* silently shifts every subsequent value by one when a field is added, removed or +interleaved with a log line — and for these vectors a one-slot shift is undetectable in the +numbers, because every element is a plausible magnitude for its neighbour. Keying on the label +makes that failure impossible instead of merely unlikely. `%.9g` is chosen to round-trip an +IEEE-754 `float` exactly, so the text mirror and cmd 33/4 agree bit for bit and a host may use +either as the read-back of record. + +Hosts should read a few more lines than the five and match on labels, since the console can +interleave `ESP_LOG` output at any point. + +### 6.5 Predicates + +Add to [src/calibration_math.h](src/calibration_math.h), which requires every text, binary +and NVS-load path to share one predicate. Note the file's live exception: the adpd baselines +are validated on save and in the text setter but **not** on load +([nvs1.cpp:180-185](src/nvs1.cpp)) — do not copy that. Follow the +`valid_adpd_baseline` / `save_adpd_baseline` pattern instead: validate inside the save +helper, so one call site covers every transport, and validate again on load. + +| predicate | bound | +|---|---| +| `valid_spec_offset(const float[10])` | every element finite, `[0, 1)` | +| `valid_spec_sens(const float[10])` | every element finite, `(0, 1000]` | +| `valid_par_weight(const float[10])` | every element finite, `\|e\| <= 1e4` | +| `valid_par_slope(float)` | finite, `(0, 100]` | +| `valid_par_intercept(float)` | finite, `\|b\| <= 500` | + +The **element-wise `isfinite` half is the load-bearing part today** — it is what keeps a NaN +out of the tier-1 divide — and it needs no measurements. The numeric ranges are provisional +and get tightened when the campaign produces real vectors (§11). Boundary cases go in +[test/calibration/test_main.cpp](test/calibration/test_main.cpp), which CI compiles and runs +with `-Wall -Wextra -Werror` ([pr.yml:30-36](.github/workflows/pr.yml)). + +### 6.6 Setters — text console, not binary 17/18 + +The earlier draft of this plan routed the vectors through new subtypes on binary cmds 17/18. +Use the text console instead: + +- It is the dialect the Calibratron actually speaks (AGENTS.md), and the Calibratron is what + runs the calibration. +- It already reports accept/reject explicitly (`"PAR coefficient rejected"`), and + [set_baseline](src/do_command.h#L182-L199) is a working comma-parsed vector setter to copy. +- Binary cmds 17 and 18 write **nothing** on an unrecognised subtype — no `ESP_CMD_DONE`, no + `ESP_CMD_END`, both replies live entirely inside `if (type == 1)` — so an old image costs + the host a full read timeout. Worse for cmd 18: `Serial.readBytes` is inside the `if`, so + the vector payload is never consumed and the stray bytes desync the next header scan. + +Verbs, following the existing `set_*` naming: `set_spec_offset`, `set_spec_sens`, +`set_par_weight` (10 comma-separated floats each), `set_par_slope`, `set_par_icept`. +The loose-parse + strict-predicate idiom already used by `set_act`/`set_spec` is fine — +`isfinite` catches `Serial_Input_Double`'s NAN-on-empty. + +**Reply contract — three outcomes, and hosts must test for the good one.** As implemented +(`report_spec_save` in [src/do_command.h](src/do_command.h)) every setter answers with exactly +one line: + +| reply | meaning | state | +|---|---|---| +| ` saved and verified` | committed to NVS and read back | **written** | +| ` rejected` | failed the predicate | NVS untouched | +| ` save failed: ` | predicate passed, the NVS write did not | NVS untouched | + +`` is `Spectral offset`, `Spectral sensitivity`, `PAR weight`, `PAR slope`, +`PAR intercept`. + +The third row is the trap, and it is worth stating rather than leaving to be discovered: it +contains no negative keyword at all. A host that tests for `"rejected"` — the obvious reading +of "it already reports accept/reject explicitly" above — treats an NVS failure as a success +and walks away believing it wrote a coefficient it did not write. **So acceptance must be +tested positively, on `saved and verified`.** That single test covers all three outcomes and +any future rewording, because only the confirmation passes it. Recommended for anything +matching these replies, host or firmware test. + +Two smaller notes for hosts: + +- Validate the value you are about to *transmit*, not the one you fitted. The wire is + fixed-point, so a value that satisfies §6.5 at full precision can violate it after + rounding — a `par_slope` below the format's resolution prints as `0.000000`, which + `valid_par_slope`'s `(0, 100]` correctly refuses. `%.6f` is ample for a slope near 1.0. +- These are five independent NVS commits, with no cross-key atomicity. Writing `par_slope` + then `par_icept` can therefore be interrupted between them, leaving `a` new and `b` old. + That is bounded and self-consistent rather than torn, but a host should read both back + together after writing both, and be able to restore the pair it replaced. + +**If binary subtypes are added later, fix this first:** [run_esp.cpp:423](src/run_esp.cpp) +discards the `readBytes` return value, and the integrity check is a plain u16 sum compared +against the trailing word — so a truncated, zero-filled buffer is self-consistent (`0 == 0`) +and is persisted as zeros. This is live today for the 12-byte actinic vector. +`crc16_ccitt` already exists in the same file at [run_esp.cpp:39-48](src/run_esp.cpp) and is +used by cmd 26; over an all-zero payload it gives 0x84F9 (12 B) / 0x85D9 (40 B), so a zero +payload provably cannot satisfy it. + +--- + +## 7. The shipped default vectors + +Three of the five come from measurement; two stay identity. **All are listed in ambit's +channel order — F1..F8, NIR, Clear — which is *not* miniPar's order. See §8.** + +### 7a. `spec_offset` — ams, genuinely device-independent + +Workbook `AS7341_AD000198_3-00.xlsx`, sheet `used Correction Values`, row 15, in +basic-count units under the **ms** convention. This is a property of the silicon, so it +ports without qualification. + +```c +// basic-count dark offsets, ams convention (tint in ms) +static const float kDefaultSpecOffset[10] = { + 0.00196979f, // F1 415 + 0.00724927f, // F2 445 + 0.00319381f, // F3 480 + 0.001314659f, // F4 515 + 0.001468153f, // F5 555 + 0.001858105f, // F6 590 + 0.001762778f, // F7 630 + 0.00521704f, // F8 680 + 0.001f, // NIR 910 + 0.003f, // Clear (broadband) +}; +``` + +**Channel centres corrected 2026-08-17.** Earlier revisions of this plan labelled these +410/440/470/510/550/583/620/670 and "Clear 750". Those numbers are not the AS7341's: the +datasheet centres are 415/445/480/515/555/590/630/680 with NIR ≈ 910 and Clear broadband, +as ambit's own vendored driver enum states +([Adafruit_AS7341.h:195-206](src/src/as7341/Adafruit_AS7341.h)) and as miniPar's column +names (`f1_415` … `f8_680`) use. The values were always right; only the comments were wrong. +[src/nvs1.h](src/nvs1.h) still carries the old labels and should be corrected with the +seeding change (§9). + +### 7b. `spec_sens` — miniPar §2.4, ports cleanly + +From `Scripts/spectralCalibration_miniPAR_LR1-B.ipynb`: the median per-channel ratio +`reference / sensor`, where `reference` is the LR1-B irradiance projected onto the channels +by least-squares inversion of the ams reconstruction matrix, and `sensor` is basic counts +with the ams offsets subtracted. Averaged over 3 devices; per-channel device spread is +**1.4 % – 9.7 %** (Clear worst), which is what justifies one fleet vector instead of a +per-serial lookup. + +```c +// miniPar LR1-B campaign, 3 devices, mean of the per-device factors +static const float kDefaultSpecSens[10] = { + 34.950663f, // F1 415 + 65.289484f, // F2 445 + 72.697997f, // F3 480 + 63.273264f, // F4 515 + 56.737110f, // F5 555 + 52.958660f, // F6 590 + 48.706781f, // F7 630 + 42.671670f, // F8 680 + 5.781618f, // NIR 910 <- miniPar index 9 + 31.565986f, // Clear <- miniPar index 8 +}; +``` + +This one ports **exactly**, because miniPar derived it under the same chain ambit computes: +basic counts with the ms tick, ams offsets subtracted, then scale. `s[i] × spec_sens[i]` in +ambit is the same expression that produced the constant. + +### 7c. `par_weight` — miniPar tier 2, ports with one caveat + +From `Scripts/regression_PAR_miniPAR.ipynb`. The notebook's export cell refits on **all** +samples and discards the tier-2 intercept; that cell had never been run, so this vector was +regenerated by re-executing its exact code over `miniPar/data/*.csv` (462 samples, 6 devices, +PAR 3.7 – 2033.7 µmol m⁻² s⁻¹). The notebook's stored 80/20 split reproduces bit-for-bit +(R² 0.9988, `f1 = 381.049293`, intercept `5.3691892`), which is the check that the +replication is faithful. + +```c +// miniPar Li-250A campaign, OLS on basic counts, all 462 samples, +// tier-2 intercept discarded (tier 3 absorbs it) +static const float kDefaultParWeight[10] = { + 333.463542f, // F1 415 + 206.427134f, // F2 445 + -30.6130744f, // F3 480 + 283.778061f, // F4 515 + -144.07319f, // F5 555 + 73.852848f, // F6 590 + 38.9285016f, // F7 630 + -6.43585093f,// F8 680 + -37.6580353f, // NIR 910 <- miniPar index 9 + 16.9097651f, // Clear <- miniPar index 8 +}; +``` + +**The caveat.** miniPar fitted `w` against `basic_counts()`, which is §2.1 only — it does +**not** subtract the dark offset. ambit applies `s = max(0, x − spec_offset)`. The difference +is `Σ wᵢ·offsetᵢ` = **2.40 µmol m⁻² s⁻¹**, a *constant*, so tier 3's intercept absorbs it +exactly. Verified by running ambit's chain over miniPar's data: R² 0.9983, median |err| +1.96 %, and a subsequent tier-3 fit returns `a = 1.0000, b = 7.983` — i.e. the whole +discrepancy (that 2.40 plus miniPar's discarded 5.61 tier-2 intercept) reappears as a pure +intercept, exactly as the tier composition predicts. Nothing needs rescaling. + +**But the constant is only constant while nothing clips — so a clipped point must never be +fitted.** The argument above is what licenses reusing this vector at all, and it rests on +`Σ wᵢ·offsetᵢ` being a fixed offset. Once any channel hits the `max(0, …)` floor, that term +stops depending on the light and the relation stops being affine. Since the clip is the +chain's only nonlinearity (§5), a clipped reading is off-model by construction. + +The consequence is sharp and easy to walk into, because the natural place to put a low anchor +in a tier-3 sweep is with the lamp off — and that is exactly where every channel clips. The +ams offsets are **0.28 – 2.02 raw counts** at the pinned 2× / 139 ms (F2 the largest), so the +clip bites only in a light-tight fixture, but there it bites on all ten. A fully clipped +reading has `par_tier2` identically 0 against a reference of ~0, so it sits on the line only +if `par_intercept` is 0. Folding one into the fit drags the intercept from 7.983 to **4.78 — +40 % low — while R² stays above 0.99, so no ordinary fit-quality gate catches it.** + +So a tier-3 sweep must exclude any point whose `clip_mask` is non-zero, at *any* lamp drive, +and must include at least one genuinely lit low point to constrain the intercept. Keying the +rule on `clip_mask` rather than on the drive setting handles both cases automatically: a dark +reading that comes back unclipped (ambient light present) is perfectly usable. This is also +the second reason `clip_mask` is on the wire per-channel rather than as one bit — the first +being §5's. + +**Read the signs before trusting this vector.** The notebook's own sanity check expects +F1–F8 to lift PAR and Clear/NIR to subtract stray light. This fit violates that on four +channels — F3, F5 and F8 are negative, and Clear is positive: + +``` +w normalised to f1: f1 1.000 f2 0.619 f3 -0.092 ← f4 0.851 + f5 -0.432 ← f6 0.221 f7 0.117 f8 -0.019 ← + clear 0.051 ← nir -0.113 +``` + +That is collinearity, not physics: the design matrix has condition number ≈ 451 and the +dataset is daylight-dominated, so OLS distributes weight almost arbitrarily among correlated +visible channels. The instability is visible directly — refitting on 80 % of the samples +moves `f1` from 333 to 381, `f3` from −30.6 to −9.2 and `f8` from −6.4 to −12.6, while the +*prediction* barely changes (R² 0.9983 vs 0.9988). The vector predicts well in-domain and +should not be read as a spectral response curve, nor extrapolated to spectra unlike the +calibration set. Fixing it needs a constrained fit (visible ≥ 0, Clear/NIR free) or shrinkage +toward the ams PAR weights — §11. + +### 7d. `par_slope` = 1.0, `par_intercept` = 0.0 — deliberately not seeded + +Tier 3 is per device and per optical stack. Seeding it from miniPar would import that +sensor's window and diffuser as if they were ambit's. + +Be precise about what this leaves: a fresh ambit reports `par = par_tier2` unscaled. It +tracks light correctly and has roughly the right spectral weighting, but its **magnitude is +not meaningful** — `par_slope` is exactly the term that carries optical throughput, and +ambit's aperture and diffuser are not miniPar's, so the scale can be off by a large factor +in either direction. Do not read the number as µmol m⁻² s⁻¹ before a tier-3 sweep. `flags` +bit9 is what says so on the wire. + +### 7e. What was checked before porting + +| | miniPar | ambit | action | +|---|---|---|---| +| tick | `ASTEP_TICK_MS = 2.78e-3` | `SPEC_TICK_MS = 2.78e-3` | none — both ms | +| gain map | `0.5 if reg==0 else 1<<(reg-1)` | `0.5 × 2ⁿ` | none — identical for all 11 ordinals | +| channel order | `..., clear, nir` | `..., NIR, Clear` | **last two swapped** | +| offset in the fit | not subtracted (`basic_counts`) | subtracted (`s`) | none — constant, absorbed by tier 3 | +| magnitudes | — | `valid_spec_sens` ≤ 1000, `valid_par_weight` ≤ 1e4 | both pass (max 72.7 and 333.5) | + +One thing to be aware of when reading the miniPar source: `Scripts/as7341_calibrate.py` +currently has `OFFSET_BASIC` **zeroed out**, with the real ams vector commented out just +above it. So `calibrated_raw()` in that module does not subtract the offset its +`SPECTRAL_COEF` was derived with. That is a miniPar-side inconsistency, worth a look there; +it does not affect anything ported here, because `spec_sens` was taken from the notebook +(which loads the offsets straight from the workbook) and `par_weight` never touches +`OFFSET_BASIC` at all. + +## 8. Footguns + +**Channel order differs from miniPar.** `SPEC_RAW_INDEX = {0,1,2,3,6,7,8,9,11,10}` +([spec_meas.cpp:193](src/src/as7341/spec_meas.cpp)) gives slot 8 = **NIR**, slot 9 = +**Clear**. miniPar's `CHANNELS` (both notebooks and `as7341_calibrate.py`) ends +`..., 'clear', 'nir'`. Any vector taken from miniPar tooling needs its last two elements +swapped — the §7 listings are **already swapped**; the miniPar sources are not. + +This one is unusually easy to get away with and unusually expensive when you don't: NIR and +Clear have the most dissimilar coefficients in every vector (`spec_sens` 5.78 vs 31.57, +`par_weight` −37.7 vs +16.9), so a missed swap does not fail loudly — it just produces a +confidently wrong PAR under any spectrum with appreciable NIR content. + +**Gain is per bank, and three comments in this repo state it backwards.** Both SMUX +configurations route Clear to ADC4 and NIR to ADC5, so the buffer holds two copies of each: +`spec[4]`/`spec[5]` from the low read and `spec[10]`/`spec[11]` from the high read. +`SPEC_RAW_INDEX` reports slots 8 and 9 from `spec[11]` and `spec[10]` — the **high** read, +taken after `setGain(gain2)` at [spec_meas.cpp:151](src/src/as7341/spec_meas.cpp). So +**F5–F8 *and* NIR *and* Clear divide by `gain_high`.** These three say otherwise and must be +corrected (they echo the SMUX function's *name*, `setup_F1F4_Clear_NIR`, not the data +reported): + +- [run_esp.cpp:285-286](src/run_esp.cpp) — cmd 35 payload comment +- [spec_meas.h:50-51](src/src/as7341/spec_meas.h) — `spec_raw_t` field comments +- [HW_CONFORMANCE.md:181-182](plans/HW_CONFORMANCE.md) — §6 reference table + +Leave [spec_meas.cpp:189-192](src/src/as7341/spec_meas.cpp) alone — it describes the +*buffer*, where both halves genuinely do contain Clear and NIR, and it is accurate. + +Both gains are `AS7341_GAIN_2X` today, so getting this wrong is latent rather than visible — +the payload carries both fields precisely because they may diverge. + +**The gain byte is an ordinal, not a multiplier.** `as7341_gain_t` n → 0.5·2ⁿ. Ordinal 0 is +0.5×, so dividing by the raw byte is a division by zero; ordinals 3/4/10 are 4×/8×/512×. +Ordinal 2 = 2× is the enum's only fixed point, which is exactly the pinned value — so a bug +here is invisible in every test at the current exposure. + +**Use the ms tick (`2.78e-3`), not seconds.** Three conventions are in circulation: +AN000633 / the workbook / ambit's own `toBasicCounts()` use **ms**; miniPar's *firmware* +`spectrometerGetBasicCountDivisor()` uses **seconds**; cmd 35's old comment prescribed no +tick at all. Decision 3 picks ms, because the published ams constants are expressed in it — +otherwise every vendor constant needs a ×1000 or ÷1000 fixup, in opposite directions for +offsets and weights. Sanity check: ATIME 99 / ASTEP 499 must give `tint = 139 ms`. + +**Compute PAR from `s`, never from `A`.** If PAR were `par_weight · A`, then `spec_sens` +would be baked into PAR, and any future improvement to the goal-A spectral calibration would +silently shift PAR on every deployed device with no PAR measurement having changed. Keeping +them separate is the whole reason tier 2 is fitted directly against the Li-250A. +(Mathematically the two are equivalent for *prediction* — a diagonal rescale followed by a +free fit spans the same space — so this is a coupling and maintenance requirement, not an +accuracy one.) + +--- + +## 9. What else must change + +**Firmware work from the seeding decision (§7) — complete.** Landed in this order, which is +the safe one: the flags change first (on its own it just reports the truth about a +still-unseeded build, both high bits 0), then the vectors. Reversing it would have left a +window where a device asserted a confident PAR with no provisional signal. + +- ~~Redefine `flags` bit2~~ — done; became the two-zone field of §5a. +- ~~Seed `spec_sens` and `par_weight`~~ — done, in `ambit_spec_calibration_t` + ([src/nvs1.h](src/nvs1.h)), in ambit channel order with the provenance, the sign caveat and + the offset-convention note recorded at the definition. No predicate changes were needed: + the largest magnitudes are 72.7 against `valid_spec_sens`'s 1000 and 333.5 against + `valid_par_weight`'s 1e4. +- ~~Fix the channel-centre comments~~ — done; they now read 415/445/480/515/555/590/630/680, + NIR 910, Clear broadband (§7a). + +Verified against miniPar's 462 samples after the port: the ambit-order vectors applied to +ambit-order columns reproduce the miniPar-order computation to 4.4e-16, and the chain scores +R² 0.9983 / median 1.96 %. A deliberately incorrect NIR/Clear swap scores R² 0.795 / 25.8 % +on the same data — worth knowing as the signature to look for if a future import goes wrong. + +**Acquisition** ([spec_meas.cpp](src/src/as7341/spec_meas.cpp)): + +- Add `gain_multiplier(as7341_gain_t)` in `spec_meas` — **not** in the vendored + `Adafruit_AS7341` (AGENTS.md: do not reformat vendored driver libs). The only existing + ordinal→multiplier switch is inside the dead `toBasicCounts()`. +- Per-channel saturation: replace the single `out->saturated` bool with a 10-bit mask, and + compare against the reported `(atime+1)*(astep+1)` rather than the compile-time constant. +- Read the AS7341's own ASAT bits from STATUS2 for `flags` bit2 (condition byte). Today the only STATUS2 + access is the AVALID bit in `getIsDataReady()`, so analog saturation below 50000 digital + counts is invisible. +- Give `dual_exposure()` a return value for `flags` bit3 (condition byte). It is currently `void` and + discards both I²C results — `low_success` is assigned at + [spec_meas.cpp:149](src/src/as7341/spec_meas.cpp) then overwritten at :156 and never + tested — and `check_AS7341()` failure does not abort. A dead I²C bus is currently + indistinguishable from a dark reading, and the tier-1 divide amplifies whatever comes back. +- Bound `delayForData(0)`, which waits forever. This was previously filed as an unrelated + out-of-scope observation; it is in scope now that cmd 35 is the primary spectral command. + +**[plans/HW_CONFORMANCE.md](plans/HW_CONFORMANCE.md)** — AGENTS.md makes it the regression +gate for any change to `run_esp.cpp`, so it must be rewritten *and re-run*: + +- `:70` Test A framing row: 32 B → 80 B. +- `:111-128` Test B checklist: the `format == 1` / `32 B` / `raw[i] <= 50000` assertions, + plus **`cmd 35's par equals cmd 31's par`, which stops being true** — replace with + per-tier checks (`par_tier2` reproducible from `raw[]` + the read-back vectors) and + `sat_mask`/`clip_mask` coverage. +- `:167-199` the whole §6 reference layout, including the wrong `gain_low` bank description + at `:181` and the `>= 1.2.0` gating text at `:196-199`. +- A new §7 log row. Note the existing v1.2.0 row is still `☐ pending HW` — **cmd 35 has + never been hardware-verified at all**, so this is a first verification, not a + re-verification, and there is no passing baseline to preserve. The load-bearing part of + Test A here is that cmd 31 stays byte-identical. + +**`Calibratron/multi_ambit_lr1b_capture.ipynb`** (different repo) — the only consumer of +cmd 35 that exists, and the acceptance harness for this feature, so the firmware change is +untestable until it is updated: `RESP_SPEC_RAW_SIZE` 32 → 80, new `<10f`@32 `chan[]`, +`= 1.2`, but no 1.2.0 exists and a build of this branch +reports **1.1.4** — `tools/version.py` keeps the leading `X.Y.Z` of `git describe`, so every +dev build reports the tag it descends from. The real consumer already abandoned version +gating for exactly this reason and probes the opcode instead. Restate the gate against +whatever version actually ships. Note also that `release.yml` is hard-pinned to +`EXPECTED_VERSION: 1.1.4` with four `vars.AMBIT_V114_*` repository variables asserted by +`test`, so cutting 1.2.0 needs a reviewed release-process change. + +**[plans/MERGE_PLAN.md](plans/MERGE_PLAN.md)** — its frozen-element table has no row for +cmd 35 and its command-ID list at `:146` is already stale against `ambit_protocol.h` +(missing 22/23/24 and the OTA opcodes 25–29). AGENTS.md directs readers there before +structural changes. + +--- + +## 10. Non-goals + +- **Do not put the 1 nm reconstruction matrix in firmware.** It is 721 × 10 floats + (~29 kB flash) and a full spectrum is 2.9 kB per reading. The 10 corrected channels in + `chan[]` are sufficient; the reconstruction is a fixed linear map the host applies. +- **Do not change cmd 31 or `spec_coef`.** Deployed devices cannot negotiate. Cmd 31 is in + the shipped Ambyte's `ambit_protocol.h`; the rewrite must leave it byte-identical. +- **Do not change the pinned exposure in this work.** Cmd 35 dividing by the *reported* + exposure is what makes auto-ranging possible later; actually adding auto-ranging is + separate work. Consequence: `gain_low` and `gain_high` cannot differ today + (`dual_exposure(AS7341_GAIN_2X, AS7341_GAIN_2X, ...)`, and `dual_exposure` has no + prototype in the header), so the per-bank divisor cannot be exercised with unequal gains + on hardware. Get the mapping right by reading §8, not by testing. +- **Do not extend `ambit_calibration_info_t`.** See decision 2 and §6. The struct is not + sacred — both repos are in development and a coordinated reflash is available — but an + un-updated Ambyte handed a longer response reads its 140 bytes correctly and then *scans* + for the `0xF0` terminator (`uart_read_exact` then `uart_scan_byte` in `uart_sensors.c`), + so it recovers **unless one of the extra bytes happens to equal 0xF0**. For arbitrary + fitted floats that is ~39% per 128-byte block, and the *defaults* (`0.0f` = `00 00 00 00`, + `1.0f` = `00 00 80 3F`) contain none — so a bench test passes and the corruption appears + later, in the field, only on calibrated devices. If the struct is ever changed, the change + worth making is not appending: it is replacing the raw `memcpy` with a byte-explicit + versioned layout so that every subsequent extension is free. That is a separate decision, + on its own merits, and it does not block this work. + +## 11. Open TODOs — the measurement campaign + +Nothing here is fitted on **ambit** hardware. Three of the five now ship a seed rather than +an identity value (§7), which changes the failure mode from "obviously zero" to "plausible +but wrong" — the `flags` calibration byte (§5a) is what keeps that visible: bit8 stays 0 +while `par_weight` is miniPar's, bit9 stays 0 until this device has been swept. + +| quantity | shipped default | still needs | ambit data today | +|---|---|---|---| +| `spec_offset[10]` | ams workbook — device-independent | nothing | n/a | +| `spec_sens[10]` | miniPar LR1-B, 3 devices | LR1-B across ambit optics | 1 session | +| `par_weight[10]` | miniPar Li-250A fleet OLS | Li-250A across **many source families** — required, §1a | none | +| `par_slope` | 1.0 | per-device intensity sweep against a Li-250A **or a transfer standard** (§1a); a Li-250A anchor is an optional TODO worth ~4 % | none | +| `par_intercept` | 0.0 | same sweep | none | + +What exists today: [data/multi_ambit_spec_lr1b.csv](data/multi_ambit_spec_lr1b.csv) — 3 rows, +3 devices, one light condition (`office`), one timestamp, one LR1-B trace. There is no +Li-Cor reference data anywhere in the ambit repo, so tiers 2 and 3 have no ambit fitting +basis at all. The seeds make the command *useful* before that campaign; they do not make it +*calibrated*, and the two must not be confused in anything reported to a user. + +**How to know the seeds have been earned out.** The miniPar port is finished when: an ambit +LR1-B session across several source families replaces §7b; an ambit Li-250A campaign replaces +§7c with a constrained or shrunk fit that has physical signs; `AMBIT_PAR_WEIGHT_IS_AMBIT_FIT` +flips to true (§5a); and each production device gets a tier-3 sweep. Until the third of those, +bit8 stays 0 on every device however well it has been swept — which is correct, and is why a +host must test bit8 and bit9 separately rather than collapsing them. + +Two constraints on the campaign, both learned the hard way on miniPar: + +- **Tier 2 must be fitted fleet-wide, from many source families.** §2's leave-one-device-out + table shows what a per-device 10-coefficient fit from a single source does. +- **Expect collinearity.** miniPar's plain-OLS fit has unphysical signs (negative on F3, F5, + F8; positive on Clear) because the design matrix is collinear (condition number ≈ 451) and + the dataset is daylight-dominated. A constrained fit (visible channels ≥ 0, Clear/NIR free) + or shrinkage toward the ams matrix's PAR weights is the recommended fix. Plan for enough + narrowband LED and gel-filter measurements to break it. §7c shows how unstable the + unconstrained fit is — coefficients move by a factor of 3 between an 80 % subsample and the + full set while R² barely changes — so do not treat a high R² as evidence the vector is + right. The ambit campaign is the chance to do this properly the first time rather than + inheriting the same problem. + +Once real vectors exist, tighten the §6.5 bounds and decide whether `par_weight` should be +seeded fleet-wide in firmware rather than written per device. + +## 12. Timing budget — do not micro-optimise the payload + +Serial is 115200 8N1 ([src/ambit-1.ino:74](src/ambit-1.ino)) → 86.8 µs per byte. + +| component | time | +|---|---| +| 2 × integration, ATIME 99 / ASTEP 499 | **278 ms** | +| SMUX reconfig + 2 × 12-byte I²C reads | ~1–5 ms | +| tier 1 + goal A + goal B math (~50 float ops, 240 MHz FPU) | **< 10 µs** | +| 80-byte payload | 6.9 ms | + +Integration is ~98% of the measurement and is inherent to the AS7341's SMUX (the part +cannot present all 10 channels in one integration). The added computation is free, and the +payload growth from 32 to 80 bytes costs ~1.5% (48 extra bytes = 4.2 ms on a ~284 ms +measurement). Include whatever fields are useful. + +## 13. Source material in the miniPar repo + +| path | contents | +|---|---| +| `CALIBRATION.md` | full write-up of goals A/B, the three tiers, and the evidence | +| `Scripts/regression_PAR_miniPAR.ipynb` | PAR model comparison; source of §7c's `par_weight`. Its tier-2 export cell has never been run and `par_coeffs_fleet.json` does not exist, so §7c was regenerated by re-executing that cell's code; the stored 80/20 cell reproduces exactly, which is the check that it was done faithfully. | +| `Scripts/spectralCalibration_miniPAR_LR1-B.ipynb` | goal A; source of §7b's `spec_sens`. Note its printed offset output is stale — the label says "same units" while the numbers shown are the old ×1000 seconds-convention ones. The *code* is ms and is what §7b relies on. | +| `Scripts/as7341_calibrate.py` | host implementation of tier 1 + goal A. **`OFFSET_BASIC` is currently zeroed**, with the real ams vector commented out above it, so `calibrated_raw()` skips the offset its own `SPECTRAL_COEF` was fitted with. A miniPar-side inconsistency; it does not affect what was ported here (§7e). | +| `Firmware/docs/AS7341_AD000198_3-00.xlsx` | ams workbook: dark offsets, 721×10 reconstruction matrix | +| `Firmware/docs/AS7341_AN000633_2-00.pdf` | ams app note: §2.1, §2.2, §2.4 | +| `Firmware/src/app/spectrometer_api.cpp:578-584` | miniPar firmware, all three tiers in 3 lines | diff --git a/plans/HW_CONFORMANCE.md b/plans/HW_CONFORMANCE.md new file mode 100644 index 0000000..d7a611a --- /dev/null +++ b/plans/HW_CONFORMANCE.md @@ -0,0 +1,318 @@ +# amBIT HW Conformance Test — Step 4b (binary frontend → core) + +**Status: PASSED 2026-06-05.** Step 4b's binary path is HW-verified byte-conformant +against a real ambyte; the dirty-mark change (§4) was accepted. Keep this procedure as the +**reusable conformance gate** for any future change touching `run_esp.cpp` or the +`data_utils.cpp` FSM (Step 5 onward) — re-run it after each such change. + +It is the verification gate for the binary/ambyte wire contract (MERGE_PLAN §2, §13). + +--- + +## 0. What Step 4b changed (what's at risk) + +`run_esp.cpp` cmd `1/2/10/20/21` now call shared `core_*` functions instead of inline +logic. The intent is **zero change on the wire**: + +- Every `Serial.write(ESP_CMD_DONE/END)` and `Serial.readBytes(...)` is byte-for-byte + unchanged; only the internal config/run calls were factored out. +- Wire normalization still lives in the adapter: gains 1-indexed (1..6, stored −1), + current byte `≥127` = "no change", MPF length base-128 (`cmd[1]<<7 | cmd[2]`). + +**One intentional internal change** (not a wire change): binary `set_gains`/`set_currents` +(cmd 1/2) now mark the config *dirty* (they didn't before). Consequences: +- Normal flow `cmd1 → cmd2 → cmd10 → cmd21`: **identical** (cmd 10 re-applies config + regardless of the dirty flag). +- Degenerate flow `set gains/currents → cmd21 without cmd10`: now correctly re-applies + the just-set values (previously could run with stale config). This is the only + observable behavioural delta, and it is a fix — confirm no ambyte workflow relied on + the old stale-config behaviour. + +--- + +## 1. Setup + +- **DUT:** an amBIT flashed with the current `fw_new` (post-Step-4b): + ``` + cd fw_new + "$HOME/.platformio/penv/Scripts/pio.exe" run -t upload # add -t upload to flash + ``` +- **Host (one of):** + - **A real ambyte** (preferred) on the FFC/UART0 link, driving the amBIT via its + `device.ambit_*` Lua commands / CLI (`ambit_*`). This exercises the genuine binary + protocol end-to-end. + - **A logic analyzer / UART sniffer** on the FFC TX & RX (115200 8N1) to capture raw + bytes for the strict byte-diff (Test A). Best combined with the ambyte. +- **Reference build (for the strict diff):** a pre-Step-4 amBIT image — e.g. `git stash` + the working tree, or check out the commit before `core.{h,cpp}` was added, build, and + keep `firmware.bin` aside as `firmware_preStep4.bin`. + +--- + +## 2. Test A — byte-level framing conformance (gold standard) + +Goal: the **protocol framing** is identical pre- vs post-Step-4b. (Measurement *payload* +values vary run-to-run with sensor noise — only the framing/lengths/checksum validity are +deterministic; do not expect data bytes to match.) + +1. Flash `firmware_preStep4.bin`. With the sniffer capturing, drive the standard sequence + from the ambyte (see §3 for the sequence). Save the trace as `pre.bin`. +2. Flash the post-Step-4b image. Repeat the **identical** host sequence. Save `post.bin`. +3. Compare. For each command, the following must match exactly: + +| Command | Deterministic bytes that MUST match | +|---|---| +| wake | host `0xAA 0xAA 0xAA` → amBIT `0x80` | +| set_gains (1) | frame `0xA0`+8B cmd → `0xA1` (CMD_DONE). No data. | +| set_currents (2) | frame → `0xA1` | +| config (10) | frame → `0xA1` | +| get_spec (31) | `0xA1` + **24 B** + `0xF0` | +| get_spec_raw (35) | `0xA1` + **80 B** + `0xF0`. Additive command — absent from any released build, so it has no `pre.bin` counterpart; verify the length and layout against §6 instead of diffing. | +| get_temp (32) | `0xA1` + **4 B** + `0xF0` | +| get_info (33,2) | `0xA1` + **`sizeof(ambit_fw_info_t)` B** + `0xF0` | +| get_temp_raw (34) | `0xA1` + **14 B** + `0xF0` | +| run (21) / mpf (20) | `0xA1` … FSM arrays (`0xD3` wake, `0xD2` ack, `0xD4 0x96` len-hdr w/ checksum byte, data, `0xD4 .. checksum`, `0xB4` pass) … `0xF0`. **Array count, each array length, header checksum, and data byte-sum checksum must verify.** Data values may differ. | + +**Pass:** all framing/handshake/length/checksum bytes identical; only measurement payload +values differ. **Fail:** any framing byte, length field, command-ID echo, or checksum +*scheme* differs → a contract break; do not ship, diff the offending command. + +> **Expected value delta from the spectral clamp (v1.2.0).** cmd 31's ten channel words +> are `counts × Spec_COE*` in a `uint16`, which used to wrap; they now saturate at 65535, +> and its PAR float is computed from the unclamped counts. Under bright illumination the +> post image therefore reports *different values* for cmd 31 — larger words and a much +> larger PAR (a wrapped F1 at 5462 counts read 8 and PAR 0.19; it now reads 65535 and PAR +> 1573). This is the intended fix, not a regression. **Framing, length and byte count for +> cmd 31 are unchanged and must still diff clean.** To compare values, take the reference +> and post traces under illumination dim enough that no channel exceeds its wrap threshold +> (F1 ≤ 5461 counts is the tightest); there the two images agree bit-for-bit. + +> If no sniffer is available, Test A reduces to "the ambyte completes every command in §3 +> with no timeout / no `checksum mismatch` / no `CMD_DONE not received` log" — a weaker but +> still useful gate (`uart_sensors.c` logs all three on failure). + +--- + +## 3. Test B — functional regression (per MERGE_PLAN §14) + +Drive each command class from the ambyte and confirm it completes and the numbers are +sane. Minimum sequence: + +- [ ] **wake / ping** — amBIT acks `0x80`; ambyte marks channel CONNECTED. +- [ ] **set_gains (cmd 1)** then **set_currents (cmd 2)** — each returns `CMD_DONE`. +- [ ] **config (cmd 10)** — `CMD_DONE`. +- [ ] **run (cmd 21)** with a small array — FSM round-trip completes, **all array + checksums pass**, array count = expected (env, fluor, fluoref, sun, leaf, 730, + 730ref), values in a plausible range. +- [ ] **run_mpf (cmd 20)** — completes; length decodes correctly (test a length >127 to + exercise the base-128 split, e.g. 200 → `cmd[1]=1, cmd[2]=72`). +- [ ] **get_spec (cmd 31)** / **get_temp (cmd 32)** / **get_temp_raw (cmd 34)** — correct + byte counts (24 / 4 / 14), values sane (temp ≈ ambient °C). +- [ ] **get_spec_raw (cmd 35)** — 80 B, layout per §6. Check, under both dim and bright + illumination: + - `format == 1`; `atime == 99`, `astep == 499`, `gain_low == gain_high == 2` + (`AS7341_GAIN_2X`) — these echo what the firmware programs, so a mismatch means + `dual_exposure()` and the reported metadata have drifted apart. + - **every `raw[i] ≤ (atime+1) × (astep+1)` = 50000.** A count above full scale means + the channel remap or the read length is wrong. `raw[i]` must never wrap — that is + the original purpose of the command. + - `sat_mask` names exactly the channels at 50000, and `flags` bit0 == (`sat_mask` != + 0). Both clear in shade. + - **Recompute the whole chain host-side from `raw[]` plus cmd 33/4 and compare** — + this is the load-bearing check, and the reason `raw[]` stays on the wire: + + tint_ms = (atime+1) × (astep+1) × 2.78e-3 → 139.0 at the pinned exposure + gain(i) = 0.5 × 2^ordinal → 2.0 at AS7341_GAIN_2X + (gain_low for i = 0..3; gain_high for i = 4..9, NIR and Clear included) + s[i] = max(0, raw[i]/(gain(i)×tint_ms) − spec_offset[i]) + chan[i] == s[i] × spec_sens[i] + par_tier2 == Σ par_weight[i] × s[i] + par == par_slope × par_tier2 + par_intercept + + Every field must agree to float precision. A mismatch confined to slots 8–9 (NIR, + Clear) means the gain bank mapping is wrong — those come from the **high** read. + - `clip_mask` bit i set exactly where `raw[i]/(gain×tint_ms) < spec_offset[i]`, and + `flags` bit1 == (`clip_mask` != 0). **Expect `clip_mask == 0x03FF` on a dark frame** + (all ten floor at their offset); that is correct, not a fault. + - `flags` high byte — **both bits expected 0** until the ambit campaign lands: bit8 + (tier-2 is an ambit fleet fit) is 0 while the compiled-in `par_weight` is the borrowed + miniPar seed, and bit9 (tier-3 stored) is 0 on any device that has not been swept. + Confirm bit9 flips to 1 after `set_par_slope` and stays 1 across a reboot — that is + the round-trip through NVS key presence. See `plans/AMBIT_COMMAND35_SPECPAR.md` §5a. + - `flags` bit3 (acquisition fault) clear on every read. If it is ever set, an I²C + channel read failed and the counts are junk. + - cmd 35's `par` no longer equals cmd 31's `par`, and must not be compared to it. + cmd 31 keeps `spec_coef` × the legacy integer-weight sum; cmd 35 reports the + three-tier value. Cross-check the *counts* instead: `raw[i] × Spec_COE_i` + reproduces cmd 31's word wherever that word is below 65535 (weights + 12,10,11,10,10,9,7,4,1,1 for F1..F8,NIR,Clear). +- [ ] **cmd 35 against an image that predates it** — an older AMBIT must simply not answer + (unknown opcodes fall into `default:` and write nothing), so the host takes its full + read timeout. This is why a host must not probe blind. +- [ ] **get_info (cmd 33)** subtypes 1/2/3 — struct sizes match `ambit_protocol.h` + (calibration **140 B**, fw_info 48 B, metadata 248 B). The calibration struct is 140 B, + not the ~136 B earlier revisions of this document and MERGE_PLAN quote: `tick_factor` + was appended in `ec99a27` and the ambyte header carries it too. +- [ ] **get_info (cmd 33) subtype 4** — spectral/PAR calibration read-back, `0xA1` + **132 B** + + `0xF0`, layout per §6. `format == 1`; the vectors match what `get_spec_cal` prints on + the text console; a freshly provisioned device shows the ams dark offsets, unity + sensitivities, an all-zero `par_weight`, `par_slope == 1.0` and `par_intercept == 0.0`. +- [ ] **cmd 33 with an unknown subtype** (e.g. 9) — must answer a bare `0xF0` with no data, + not silence. Subtype 4 is additive precisely because this `else` branch already exists. +- [ ] **Calibration write round-trip** — over the text console, `set_par_slope 2.0` then + `get_spec_cal` and cmd 33/4 both report 2.0; reboot and both still report 2.0 (the NVS + load path re-validates). `set_par_slope 0` and `set_spec_sens` with a zero element must + both be rejected with a message, and must leave the stored values unchanged. +- [ ] **baseline (cmd 6)**, **blink (cmd 5)**, **actinic (cmd 4)** — complete without error. +- [ ] **set_metadata (cmd 37)** with `eof_mark = 2025` — accepted and persisted. + +**Compare measurement values** from cmd 21/20 against the pre-Step-4 reference build on the +same target/sample: they should match within sensor noise (Step 4b does not change the +measurement math). + +--- + +## 4. Test C — the dirty-mark behavioural delta (the only intended change) + +Run this exact non-standard sequence **without a cmd 10 between set and run**: + +1. `cmd 10` (config) then `cmd 21` (run) with gains G1 — note the values. +2. `cmd 1` (set gains to a clearly different G2) **then `cmd 21` directly** (no cmd 10). + +- **Pre-Step-4b:** the run in step 2 used **G1** (stale — the bug). +- **Post-Step-4b:** the run in step 2 uses **G2** (re-applied — the fix). + +**Expected pass:** step-2 readings reflect G2. If any ambyte workflow *depended* on the old +stale-config behaviour, flag it — the dirty-mark can be removed from `core_set_gains` / +`core_set_currents` (drop the `adpd_mode = MPF_MODE` line) to restore the exact old binary +behaviour, at the cost of re-diverging from the text path. + +--- + +## 5. Result handling + +- **All pass** → mark Step 4b verified: update memory `merge-plan-ambit` ("4b verified on + HW ") and tick MERGE_PLAN §14's ambyte rows. +- **Framing diff (Test A fail)** → contract break; capture the offending command's pre/post + bytes and fix the adapter so the wire matches. +- **Test C unwanted** → remove the dirty-mark as noted in §4. + +--- + +## 6. Reference — `get_spec_raw` (cmd 35) payload layout + +Request is the standard `0xA0` + 8-byte frame with `cmd_arr[0] = 35`; the remaining seven +bytes are unused. Response is `0xA1` + **80 B** + `0xF0`. + +Unlike the cmd 33 structs 1/2/3 — which are frozen at 140/48/248 bytes because they are +`memcpy`'d straight out of ESP32-default-aligned C structs — this payload is assembled +byte-explicitly. Every field sits at a naturally aligned offset, so it is padding-free on +both sides without `__attribute__((packed))`. All multi-byte fields are little-endian. + +| Off | Size | Field | Notes | +|---|---|---|---| +| 0 | 1 | `format` | `= 1`. Bump only on a layout change; a host must reject unknown values rather than guess. The 32-byte layout this replaced was never released, so value 1 was never observed meaning anything else. | +| 1 | 1 | `atime` | As programmed (99). | +| 2 | 1 | `gain_low` | `as7341_gain_t` **ordinal** 0-10 for the **F1-F4** bank. `2` = `AS7341_GAIN_2X`. | +| 3 | 1 | `gain_high` | Same enum, the **F5-F8 + NIR + Clear** bank. Equal to `gain_low` today; the two diverge if autoranging is added. | +| 4 | 2 | `astep` | u16, as programmed (499). | +| 6 | 2 | `flags` | **Two zones with opposite polarity, one per byte.** Low byte = conditions, 1 means attention: bit0 = a channel hit digital full scale; bit1 = a channel clipped at its dark offset; bit2 = AS7341 analog saturation (ASAT); bit3 = acquisition fault (an I²C channel read failed); bits 4-7 reserved. High byte = calibration, 1 means confirmed: bit8 = `par_weight` is an ambit fleet fit (0 = borrowed miniPar seed); bit9 = tier-3 slope/intercept stored for this device (0 = never swept); bits 10-15 reserved. An all-zero word means "no fault, nothing confirmed" — the safe reading. Never treat an unset high bit as trustworthy. | +| 8 | 2 | `sat_mask` | u16, bit i = channel i reached digital full scale. | +| 10 | 2 | `clip_mask` | u16, bit i = channel i clipped at its dark offset. `0x03FF` on a dark frame is normal. | +| 12 | 20 | `raw[10]` | u16 × 10, **unscaled** counts in wavelength order: F1, F2, F3, F4, F5, F6, F7, F8, NIR, Clear. | +| 32 | 40 | `chan[10]` | f32 × 10, goal A: normalised, dark-offset-corrected, spectrally scaled. | +| 72 | 4 | `par` | f32, goal B: `par_slope × par_tier2 + par_intercept`. | +| 76 | 4 | `par_tier2` | f32, goal B before slope/intercept — the quantity a Li-250A intensity sweep regresses against. | + +**Normalisation.** Full scale is `(atime + 1) × (astep + 1)` = 50000, which is why `raw[]` +cannot overflow its `uint16`. The firmware's own chain, which a host must be able to +reproduce exactly from `raw[]` plus cmd 33/4: + +``` +tint_ms = (atime + 1) × (astep + 1) × 2.78e-3 MILLISECONDS — 139.0 at the pinned exposure +gain(i) = 0.5 × 2^ordinal gain_low for i = 0..3, gain_high for i = 4..9 +x[i] = raw[i] / (gain(i) × tint_ms) tier 1, basic counts +s[i] = max(0, x[i] − spec_offset[i]) clip -> clip_mask bit i +chan[i] = s[i] × spec_sens[i] goal A +par_tier2 = Σ par_weight[i] × s[i] goal B tier 2 +par = par_slope × par_tier2 + par_intercept goal B tier 3 +``` + +Three things this gets right that are easy to get wrong: + +- **The gain bytes are enum ordinals, not multipliers.** `n` means `0.5 × 2ⁿ`, so ordinal 0 + is 0.5× — using the byte directly would divide by *zero*. Ordinal 2 = multiplier 2 is the + enum's only fixed point, which is exactly the pinned value, so the mistake is invisible + until autoranging. +- **NIR and Clear divide by `gain_high`.** Both SMUX configurations sample Clear and NIR, so + the driver buffer holds two copies; the reported slots 8 and 9 come from the **high** read. +- **The tick is milliseconds.** Every published ams constant — the dark offsets and the + 721×10 reconstruction matrix — is expressed in ms, so the stored vectors are meaningless + under any other convention. + +**Relation to cmd 31.** cmd 31 reports `raw[i] × Spec_COE_i` saturated into a `uint16` +(weights 12, 10, 11, 10, 10, 9, 7, 4, 1, 1), scaled by `spec_coef`. It remains byte-frozen +at 24 B and is the only spectral command an image without cmd 35 answers. **cmd 35's `par` +is a different quantity from cmd 31's and the two must not be compared** — `spec_coef` stays +with cmd 31, while `par_slope`/`par_intercept` are its tier-3 successors here. + +## 6b. Reference — spectral/PAR calibration read-back (cmd 33 subtype 4) + +Response is `0xA1` + **132 B** + `0xF0`. Byte-explicit and versioned, unlike subtypes 1/2/3, +so it can be extended without a coordinated ambyte reflash. + +| Off | Size | Field | +|---|---|---| +| 0 | 1 | `format` = 1 | +| 1 | 1 | reserved, 0 | +| 2 | 2 | reserved, 0 | +| 4 | 40 | `spec_offset[10]` f32 — dark offset, basic-count units under the ms convention | +| 44 | 40 | `spec_sens[10]` f32 — goal A per-channel sensitivity | +| 84 | 40 | `par_weight[10]` f32 — goal B tier 2 | +| 124 | 4 | `par_slope` f32 | +| 128 | 4 | `par_intercept` f32 | + +Same channel order as `raw[]`/`chan[]`: F1..F8, NIR, Clear. An unknown cmd 33 subtype +answers a bare `0xF0`, so this is additive against older images. `get_spec_cal` on the text +console prints the same five values. + +**Capability gating.** There is no negotiation on this link, and an unknown opcode gets no +reply at all (`default:` in `do_esp_cmd()` writes nothing), so a host that probes pays a +full read timeout. Gate on the firmware version from cmd 33/2 — but note the version a +build reports is derived from `git describe`, so a development image several commits past a +tag reports that *tag's* version while already serving cmd 35. Fix the minimum version in +host code to whatever release actually carries this payload; do not copy a number from this +document before that release exists. + +This same harness (Tests A/B) is the reusable conformance gate for any future change that +touches `run_esp.cpp` or `data_utils.cpp`'s FSM (Step 5 onward). + +--- + +## 7. Re-verification log (changes that require re-running this gate) + +| Date | Change | Re-run | Status | +|------|--------|--------|--------| +| 2026-06-05 | Step 4b — binary frontend → core (`run_esp.cpp` cmd 1/2/10/20/21) | A + B + C | ✅ PASSED (incl. run(21)/mpf(20) FSM round-trip) | +| 2026-06-05 | Sweep #4 inc 1 — extracted `pam_send_results` from `run_arr_type1`; verbatim relocation of the `fsm_send_esp`/`send_serial` calls | A + B | ✅ PASSED — AMBYTE: run(21) = 5 arrays (idx0 len1 ENV + idx1-4 len30; 730/730ref correctly absent for no-IR), mpf(20) = 7 arrays. (COMPUTER `arrun` half is the same function's other branch — optional spot-check.) | +| 2026-06-05 | **#16 fix** — `serial_read_until`/`flush_serial` no longer echo non-target bytes (the echo injected the ambyte's 2nd `200` as ASCII `'2''0''0'` into the FSM **data phase** → checksum mismatch every run). **Not** a checksum-scheme bug: the amBIT byte-sums correctly (`u32_byte_sum`, data_utils.cpp:200/201/210, via `fsm_send_data`:432). | A + B | ✅ PASSED — array checksums clean, run/mpf complete | +| 2026-06-05 | Sweep #4 inc 2 — routed `run_trigger_spacer` (the mpf / cmd-20 path) through `pam_send_results` (`subsampling=1, has_730=true` → unconditional 7 channels; AMBYTE branch identical to the former inline block, COMPUTER branch inert as this path is never COMPUTER). `MPF` (2-channel idx 0/1), `external_trigger_run`/`_Flash`/`fluor_offset` (no FSM send) don't share this block. | B — the `mpf(20)` row of the Lua | ✅ PASSED — mpf(20) = 7 arrays, no checksum errors | +| 2026-06-05 | Cheap correctness sweep — **#10** init the comma-decl locals in `run_arr_type1`/`run_trigger_spacer`/`fluor_offset` (defensive; each was already assigned before use); **#8** `w`-length `uint8_t`→`uint16_t` + drop pointless `(uint16_t)` casts; **#9** delete 2 uncalled FSM fns (`fsm_wake_up_calls(void)`, `fsm_send_waitesp` — the latter had missing-return UB). | #10 → A+B (measurement path, behavior-neutral, very low risk). #8 text path (`w` over USB), #9 dead-code → no ambyte HW. | ☐ #10 prudent Lua re-run; #8/#9 done | +| 2026-06-08 | **Wire v2 (ambyte variant)** — (1) self-describing length header: the 2 spare bytes now carry `elem_width` (2\|4) + `dtype` (0 unsigned\|1 signed); csum formula unchanged (sum of bytes [0..6], now covers the new bytes); host defaults 4/0 when a byte is 0, so v1 still decodes. (2) ADC channels (Fluo/Fluoref/Sun/Leaf/730/730ref) sent as **uint16** (elem_width 2, dtype 0, each clamped to 0xFFFF); ENV as **int16** centi-°C (elem_width 2, dtype 1). Data-trailer csum = byte-sum of exactly the bytes sent. (3) new **TIMING** block idx 7 = `uint32[tick_begin, tick_end]` (µs from `esp_timer_get_time()`), emitted in `pam_send_results`. (4) **one wake per run**: `data_utils.cpp` FSM split into free `ambyte_wake()` + `dataclass::fsm_send_array(idx,elem_width,dtype)`; `pam_send_results` wakes once then streams each array back-to-back; host loops on the 212 marker, stops at the run's trailing 240. `fsm_send_esp` kept as a legacy wake+one-array(4/0) wrapper (MPF path). Host (ambyte) side updated to match. Both `pio run -e ambyte` and `-e cloud` build clean. | A + B | ☐ pending HW — one wake yields ENV(int16×N), Fluo/Fluoref/Sun/Leaf/730/730ref(uint16×N), TIMING(uint32×2), then 240; every header csum + data byte-sum must verify and each block decodes per `elem_width`/`dtype` | +| 2026-08-05 | **v1.1.1-rc4 host-discovery latch** (`30aaf6f`): UNKNOWN/TEXT remain awake and quiet; only a matched `0xAA` wake latches BINARY and enables light sleep/`0x85`. Binary parser/FSM code was unchanged. Exact rc4 app image (427,264 B, SHA-256 `a3c5e3f6bc9f8b55422db7f618b45e3ae1cfcef14b9512144e30f495f7c51e04`) OTA-flashed to recovered AB48. | Weak A + non-destructive B | ✅ PASSED — cold legacy/JSON first contact 10/10; cold binary wake 5/5; wake after 1.2 s idle 5/5; cmd 33/2 identity, temp/spec/temp-raw, run(21), and mpf(20) length 200 all completed with no timeout/framing/checksum error. MPF returned ENV + six 200-element arrays + TIMING. JSON arrun and malformed-extra-token rejection also passed, as did binary→JSON→binary→text→binary switching on one boot. Mutating baseline/set-metadata tests were deliberately omitted to preserve NVS calibration; no logic analyzer was available, so the strict pre/post byte trace remains unrun. | +| 2026-08-12 | **Spectral overflow fix + `get_spec_raw` (v1.2.0, branch `fix/spec_overflow`).** (1) `get_PAR()`'s `calc_spec[n] = counts × Spec_COE_n` assignment into a `uint16` **wrapped** from ~11 % of ADC full scale (F1 ×12 wraps above 5461 of 50000 counts), so a bright reading looked like a dark one; the ten words now **saturate at 65535** instead. Affects cmd 31 and the text/JSON `PAR`/`get_par` verbs — framing and 24 B length unchanged, *values* change under bright light (see the note in §2). (2) PAR is now summed from the unclamped counts in a `uint32` accumulator, so it no longer collapses when a channel pegs; verified bit-identical to the old result across 100 000 non-overflowing vectors. (3) New additive **cmd 35 `get_spec_raw`** → `0xA1` + 32 B + `0xF0`: unscaled counts plus `format`/`atime`/`astep`/`gain_low`/`gain_high`/`flags` (§6). (4) `spec_meas.h` given an include guard (it now defines a type). Ambyte side deliberately **not** updated — cmd 31 is untouched, so deployed loggers are unaffected. | A + B — A must show cmd 31 framing byte-identical; B adds the cmd 35 rows | ☐ superseded before it was ever run — see the 2026-08-17 row | +| 2026-08-17 | **cmd 35 redefined as the three-tier spectral/PAR command (branch `fix/spec_overflow`).** Its 32-byte payload was never released (no tag contains it, it is not on `main`, and the ambyte's `ambit_protocol.h` has no opcode 35), so it was rewritten in place rather than adding a cmd 36. Now `0xA1` + **80 B** + `0xF0`: adds `sat_mask`/`clip_mask`, `chan[10]` (goal A) and `par`/`par_tier2` (goal B), and `par` becomes `par_slope × tier2 + par_intercept` instead of the legacy `spec_coef` product — so the old "cmd 35 par == cmd 31 par" check is gone. Firmware now normalises by gain **multiplier** (the wire byte is an ordinal, `0.5 × 2ⁿ`) and by integration time in **ms**; NIR/Clear divide by `gain_high`, and three in-repo comments that said `gain_low` were corrected. New **cmd 33 subtype 4** returns the five calibration vectors (132 B) so a host can recompute and verify the chain from `raw[]`. Five text setters (`set_spec_offset`/`set_spec_sens`/`set_par_weight`/`set_par_slope`/`set_par_icept`) and `get_spec_cal` added; the vectors live outside `ambit_calibration_info_t`, so **cmd 33/1 stays 140 B**. cmd 31, `spec_coef` and the pinned exposure are untouched. | A + B — A must show cmd 31 framing byte-identical; B runs the rewritten cmd 35 rows, cmd 33/4, and the calibration write round-trip. **First hardware verification of cmd 35 in any form** — the 32-byte layout never passed this gate, so there is no baseline to preserve. | ☐ pending HW | + +**Focused check for sweep-#4 inc 1** (the send block is shared by both transports, so verify +both halves): + +- **AMBYTE path** — drive a `run` (cmd 21) and an `mpf` (cmd 20) from the ambyte: the FSM + round-trip (array count, each array length, header + byte-sum checksums) must be identical + to the pre-change build; measurement values within sensor noise. +- **COMPUTER path** — drive `arrun` over the USB/text console: the 7 ASCII blocks + (`Data:ENV…`, `Fluo`, `Fluoref`, `SUN`, `leaf`, `730`, `730ref`) followed by `Data sent` + must be byte-identical to pre-change (this exercises the `send_serial` half). + +Passing both confirms the extracted `pam_send_results` reproduces the inline block exactly. +The next sweep-#4 increments (`PamBuffers` RAII / `#11` leak fix, then `read_one_frame`) +each get their own row here and must re-run A + B before being trusted. diff --git a/plans/MERGE_PLAN.md b/plans/MERGE_PLAN.md new file mode 100644 index 0000000..32c9c6e --- /dev/null +++ b/plans/MERGE_PLAN.md @@ -0,0 +1,615 @@ +# Ambit Firmware Merge Plan (v2) — built around dialect + config unification + +Collapse the two diverged Ambit ESP32-C3 forks into **one** source tree that builds +both product variants from one codebase: + +- **`ambyte`** variant — binary FSM protocol over UART0/FFC, idle light-sleep, the + field/companion-board use case (today = `fw_new`, was `ambitPIO`). +- **`cloud`/`usb`** variant — text + JSON over native USB-CDC, feeds the OpenJII + platform / phone app (today = `fw_appCompatible`, was `openJII`). + +Sources: +- `T` = **`fw_new`** (ambyte/binary line, this repo) — the **trunk**. +- `A` = **`fw_appCompatible`** (cloud/JSON line) — a *capability fork*: it added the + JSON/line frontend but **deleted** the binary frontend, Wrench, and power + management. Donor of the text/USB frontend only. +- `H` = **`ambyte-iot-ludo`** (the datalogger) — **not** ambit FW. It is the + *authoritative spec* of the binary wire contract ([uart_sensors.c], [ambit_protocol.h]). + Treated here as the frozen reference, never modified. + +Back-compat of the **text** path is not a constraint; the **binary** path is a frozen +wire contract (§2). Git holds history. Reviewed 2026-06-05. + +--- + +## 0. Revision history + +### v3 (current) — openJII comms layer + +- **Core returns typed data, not a JSON tree.** The shared core is JSON-free; the JSON + tree is built only in the app-side (cloud) adapter, so the `ambyte`/binary image links + no JSON (§5, §7c). +- **The openJII text/JSON layer is a device-agnostic shared module** (§7d), built **fresh** + to [CommunicationProtocolOpenJIISerial.md] — CO2Dot is the doc's named reference but is + too old to lift (D5). +- **Envelope `set` is flat** (one element per executed command/repeat). **Snapshot → + tree; measurement → one streamed JSON value** per `set` position (D6). +- **Comms-layer workflow:** implement to the spec doc → HW-test against the real app (the + oracle) → revise the spec doc. The binary path is *not* iterated this way — it's frozen. + +### v2 — unification spine + +This revision is built around the **command-dialect + config unification** as the +headline task, not as one step among many. Specifically: + +- **D1 (Wrench): DELETE, not gate.** Tracing shows Wrench is reachable from exactly + one text-console entry point and exposes zero unique commands (§8). It goes away + entirely, behind a single stakeholder check. +- **The spine is now: one CORE + one CONFIG + three thin transport adapters** (§4–§7). + Every other task (JSON re-graft, PAM sink merge, framing fix, power mgmt) hangs off + that spine. +- **Constraint (c) is promoted to its own section (§2)** with an explicit, verified + list of frozen wire elements and a conformance gate, because the deployed ambyte + (`H`) already pins several of them — including the data checksum, which makes + `fw_new`'s "checksum fix" **mandatory for compatibility**, not optional. + +--- + +## Where we are — 2026-06-05 (resume here) + +Two build variants from one tree (`platformio.ini`): **`ambyte`** (binary FSM over +UART0/FFC, HW-verified field path) and **`cloud`** (openJII LINE+JSON over UART0 — the +test HW has no USB-C; the native-USB-CDC flags are kept in a comment for a future PCB). + +**Done + HW-verified** (commits through `51a7ffc`): +- Wrench deleted; the 3 config copies unified; transport-agnostic CORE (`core.*`); text + + binary frontends thin over it; `pam_send_results` shared; echo (#16) fixed; nvs checksum + (#6); dead-code purge (#3); cheap correctness (#8/#9/#10). Binary path byte-conformant vs + a real ambyte (`HW_CONFORMANCE.md`). +- **openJII module `lib/openjii_proto/`** (device-agnostic): first-byte mode detect, LINE + (bare scalar / JSON container + dot-path + `keys()`), JSON envelope (flat `set` + frame + token), §9 errors, receiver. Snapshot API `on()` + stream/measurement API `on_stream()`. +- **Cloud variant** (`src/frontend_cloud.cpp`): snapshots hello/temp/get_par/PAR (verified) + and **`arrun` measurement-in-envelope** (verified — runs `run_arr_type1`'s new + `json_output` path, emits `{"ENV":[..],"Fluo":[..],"Fluoref":[..],"SUN":[..],"leaf":[..]}`). + ENV channels byte-identical to fork A's `send_json`; ENV decoded from fw_new centi-°C. + +**Next (pick up here):** +1. **Expand command coverage.** Snapshots: `set_currents`/`set_gains`/`set_pd_gain`/`baseline`/ + `battery`/`set_act`/`set_name`/`set_emit`/`set_spec`/`clean_nvs`/`reboot`/`check` — each a + one `on()` tree-builder calling the CORE. Measurements: `q`/`w`/`mpf`/`ww`/`ff` — give + `run_trigger_spacer` (and `MPF`) the same `json_output` sink `run_arr_type1` has, register + via `on_stream`. +2. **Confirm formats against the app (D6/D2).** ENV is `{"temp_c":<°C>}` per point now (fork A + had `{"t_ms":..,"temp_c":..}` + step marks, dropped in fw_new). Adjust `send_env_json` if the + app needs the old shape. Check ArduinoJson float precision (trims trailing zeros vs the + spec's fixed 4 dp) is tolerated. +3. **Test the rest of LINE mode on HW:** dot-path queries, `keys()`, the error taxonomy. +4. **Open decisions:** D2/D3 (env centi-°C + ambient — consumer sign-off), D6 (measurement + shape — partly resolved by the working `arrun`). + +--- + +## 1. Headline: the disease and the cure + +**Disease.** The same ~25 logical operations are implemented as **four parallel +command parsers**, each with its own argument decoding *and* its own copy of the +device config: + +| Parser | File | Config it mutates | +|---|---|---| +| text/computer | [do_command.h] `hash()` switch | global `adpd_*_config` | +| binary/ambyte | [run_esp.cpp] `cmd_arr[0]` switch | `adpd_*_config_local` (file-static) | +| Wrench bridge | `do_c.cpp` native fns | `do_c.cpp` statics (3rd copy) | +| JSON/line (fork `A`) | [commands.cpp] `startsWith` chain | global `adpd_*_config` | + +Adding one command means editing up to four places; the same setting lives in three +structs that can silently diverge (set currents via text, run via binary → different +values applied). **This duplication is the root cause of the forks themselves.** + +**Cure.** Split every operation into: + +``` + wire bytes ─► [thin transport adapter] ─► normalized args ─► CORE op ─► result ─► [sink] + │ │ + (decodes that transport's one implementation, + own wire convention) one CONFIG struct +``` + +- **One CORE** ([core.cpp]): one function per logical operation, taking *normalized* + typed args and writing to a *sink*, never touching `Serial`/`printf` directly (§5). +- **One CONFIG** ([config.h]): a single `AmbitConfig` struct every path reads/writes (§6). +- **Three thin adapters**: binary (frozen wire), text-line, JSON (§7). Each adapter's + only job is wire⇄normalized-args translation + choosing the sink. No measurement or + config logic lives in an adapter. + +Note the realistic scope: because the binary wire is frozen (§2), we **do not** merge +the three *parsers* into one parser. We collapse the three *cores* and three *configs* +into one each, and make all three parsers thin shims over them. That is the achievable, +high-value unification. + +--- + +## 2. The frozen binary contract (constraint c) + +The binary path is a wire contract with **shipped ambyte firmware** (`H`). The contract +is defined by what [uart_sensors.c] / [ambit_protocol.h] already implement. Refactor +*behind* it freely; do **not** change the bytes. De-obscuring is allowed only on the +text/USB path. + +**Frozen elements (verified against `H`):** + +| Element | Value / encoding | Source of truth | +|---|---|---| +| Wake / ack | host `0xAA ×3` → ambit `0x80`; boot-idle `0x85` | [uart_sensors.c:32-37] | +| Cmd frame | header `0xA0` + 8-byte `cmd[8]` (+ optional extra) → `0xA1` done → … → `0xF0` end | [uart_sensors.c], [run_esp.cpp] | +| Command IDs | 1,2,10,20,21,31,32,33,34,37,4,5,6,17,18 | [ambit_protocol.h:15-31] | +| Info subtypes | 33 → 1=calib, 2=fw, 3=metadata | [ambit_protocol.h:33-37] | +| Response structs | calibration ~136 B, fw_info ~48 B, metadata ~248 B (ESP32 default alignment, `eof_mark==2025` to accept) | [ambit_protocol.h:39-87] | +| Raw responses | spec = 24 B (12×u16, last 4 = float PAR); temp = 4 B (2×i16 ×10); temp_raw = 14 B (7×i16) | [run_esp.cpp], [ambit_protocol.h:83-87] | +| FSM data handshake | wake 211, awake 210, data-hdr 212, len-marker 150, ready 200, data-pass 180, reset 222 | [data_utils.h:9-16], [uart_sensors.c:39-46] | +| FSM header checksum | sum of header bytes `[0..6]` == byte `[7]` | [uart_sensors.c:265-269] | +| **FSM data checksum** | **byte-sum of every payload byte mod 256** (`u32_byte_sum`) | [uart_sensors.c:323-330] | +| MPF length encoding | **base-128 split**: `cmd[1]=len>>7`, `cmd[2]=len&0x7F` | [device_commands.c:1027-1028], [run_esp.cpp:92] | +| Gain wire convention | cmd 1 gains are **1-indexed** on the wire (1..6), stored `−1` | [run_esp.cpp:58-63] | + +**Consequences for the merge:** + +1. **The byte-sum checksum is required, not optional.** `H` verifies it; `fw_original`'s + low-byte-only checksum would be rejected. `fw_new` already byte-sums — keep it. This + alone confirms `fw_new` (not `fw_original`) is the correct trunk for the binary path. +2. **Obscure-but-frozen encodings stay** (base-128 MPF length, 1-indexed gains, struct + alignment). We *document* them at the adapter boundary; we do not "clean them up." +3. **Per-transport wire conventions are decoded in the adapter, not the core.** The CORE + `set_gains` takes 0-indexed normalized gains; the binary adapter subtracts 1, the + text/JSON adapters pass through. Same for the base-128 length. +4. **Semantic (non-wire) contracts are a separate freeze (D2/D3).** `H`'s C layer + forwards env/data arrays **raw** (it does not decode env temperature), so changing + the env-temp *encoding* won't break the wire — but it changes what the *consumer* + (Lua/cloud) must parse. Freeze those by **decision + consumer sign-off**, not by the + conformance test. + +--- + +## 3. Decisions + +| # | Decision | Resolution | +|---|---|---| +| **D1** | Wrench scripting VM (`wrench.*` 17.4k LOC + `do_c.cpp`) | **DELETE entirely** (§8). One text-only entry point, zero unique commands. **Stakeholder check (the only gate):** confirm nobody sends multi-line `C…?` *scripts* (loops/control-flow) from the bench console today. Single primitive calls → pure win. Real scripting → that workflow moves host-side (the ambyte and app already drive primitive sequences themselves). | +| **D2** | Env-temperature wire/format | **Keep `fw_new` centi-°C** plain `int16` (de-obscured from the old `time<<16\|type<<12\|(mlx+20)*20`). Step-mark events are dropped. **Consumer sign-off required:** verify (a) the cloud/Lua consumer tolerates a plain centi-°C series, and (b) the dropped step-marks did not feed `H`'s `mark_event_*` subsystem ([device_commands.c:78]). | +| **D3** | Ambient (sun/leaf) correction | **Keep `fw_new`** offset-corrected (`>65000 ? x−65000 : 0`). If the cloud backend needs *raw*, branch at the `put()` on `CONNECTION_TYPE==CLOUD` only. **Verify with the data consumer before locking.** | +| **D4** | `SunRint`/`LeafRint` gain fields + `read_light_env()` (fork `A` only) | Keep **iff** the cloud/app variant needs the `set_pd_gain` / `read_light_env` snapshot. Otherwise drop the two fields and the function. Default: **keep** (the app uses `set_pd_gain`). | +| **D5** | openJII text/JSON implementation source | **Build the comms layer fresh** to [CommunicationProtocolOpenJIISerial.md] as a **device-agnostic shared module** (§7d). CO2Dot is the doc's named reference but is **too old to reuse** — do not lift its code. Reuse fork `A`'s parser/framing only as a starting skeleton, never its hand-printed command bodies. | +| **D6** | openJII measurement (`arrun`) in-envelope JSON shape | **Resolve by HW test, not now.** Observed app expectation is `arrun → [{…},{…}]` (one element per produced array). It appears to conflict with **command-as-root** (§2.1 of the spec): a bare array `[{…},{…}]` is command-as-root-compliant, a `{"arrun":[…]}` wrapper is not. The app is the oracle — confirm the exact shape on hardware, then revise the doc. | + +**Comms-layer workflow (D5/D6).** For the openJII text/JSON layer **only**: implement to the +spec doc → HW-test against the real app → revise the spec doc to match what the app +actually accepts. The binary/ambyte path is **not** iterated this way — it is frozen (§2). + +--- + +## 4. Target architecture + +``` + ┌─────────────────── one CORE (core.cpp) ───────────────────┐ + │ set_currents · set_gains · set_pd_gain · config_detector │ + host bytes │ run_array · run_mpf · external_trigger · flash_trigger │ + │ │ get_par · get_temp · get_temp_raw · baseline │ + ├─ binary adapter ───►│ set_actinic · set_name · set_metadata · set_emit/spec │ + │ (run_esp.cpp, │ blink · nvs_clean · nvs_update · info · check · reboot │ + │ FROZEN wire §2) │ read_light_env (D4) │ + │ └───────────────┬──────────────────────────┬────────────────┘ + ├─ text-line adapter ─────────────────┤ │ + │ (frontend_text.cpp) ▼ ▼ + │ one CONFIG (AmbitConfig) output via sink: + └─ json adapter ─────────► currents + gains + BINARY → fsm_send_esp / raw writes + (frontend_json.cpp, pd_gain + mode TEXT → send_serial / CSV + cloud/usb only) JSON → send_json + PLOT → CSV printf +``` + +- **Sink selector = `CONNECTION_TYPE`** (already the right seam in [config.h]). Extend + the enum with `CLOUD`; fork `A`'s `json_output` bool and `T`'s per-call branching both + collapse onto it. +- **Core functions take an explicit sink/connection arg or read the global + `CONNECTION_TYPE`** — but never hardcode a transport. + +--- + +## 5. The unified CORE (`core.cpp` / `core.h`) + +Extract each command body (currently duplicated across the four parsers) into **one** +function. Rules: + +- Parameters are **normalized typed values** (0-indexed gains, real `uint16` lengths) — + the adapter has already undone its wire convention. +- **Returns typed native data (structs/scalars), never formatted text and never a JSON + tree.** No `Serial`/`printf`/ArduinoJson inside the core — so the `ambyte` image links + no JSON. Snapshot ops return by value/out-param (e.g. `get_par(u16 spec[10]) -> float`); + the adapter renders it (binary bytes, LINE text, or the JSON tree built in the cloud + adapter §7c). +- **Streaming measurements** write raw frames through `dataclass`/the streaming sink, + keyed by `CONNECTION_TYPE`; the adapter decides framing (binary FSM bytes vs a streamed + JSON array element). The core never builds a tree for a run — runs are thousands of + points. +- Returns a typed status, so adapters can format errors per dialect (binary `CMD_END`, + text `BAD COMMAND`, JSON `{"error":…}`). + +Operation set (the single source of truth; the §11 coverage matrix maps each dialect +onto these): + +``` +set_currents(u8 i620, u8 i720, u8 ir) +set_gains(u8 fluo, u8 fluoRef, u8 ir, u8 irRef, u8 sun, u8 leaf) // 0-indexed +set_pd_gain(u8 sunTia, u8 sunRint, u8 leafTia, u8 leafRint) // D4 +config_detector(...) // conf_slow_FR_1 wrapper +run_array(u8 len, const u8* arr, u8 persist, bool allow_int) +run_mpf(u16 length, u8 interval, bool change_act, u8 act) +external_trigger(), flash_trigger(u32 gate, u32 dt, u16 num) +get_par(u16 spec[10]) -> float +get_temp(double* leaf, double* chip) +get_temp_raw(...) ; baseline(u32 ret[6], bool save) +set_actinic(float), set_name(const char*), set_metadata(const metadata_t*) +set_emit(double), set_spec(float) +blink(u8 id, u8 intensity) +nvs_clean(), nvs_update_scalar(...), nvs_update_array(...) +info(u8 subtype) ; check() ; reboot() +read_light_env() // D4 +``` + +--- + +## 6. The unified CONFIG (`AmbitConfig`) + +Collapse the three copies into one global struct, read/written by every adapter: + +```c +struct AmbitConfig { + struct { uint8_t i620, i720, ir; bool init; } current; + struct { uint8_t fluo, fluoRef, ir, irRef, sun, leaf; + uint8_t sunRint, leafRint; // D4 + bool init; } gain; // ALL 0-indexed (normalized) + uint8_t adpd_mode; // ADPD_CONFIG_MODE +}; +extern AmbitConfig g_cfg; +``` + +- Delete `adpd_current_config_local` / `adpd_gains_config_local` ([run_esp.cpp:21-22]) + and the `do_c.cpp` statics (gone with Wrench). +- **Preserve "set then apply on run" semantics.** Today the text path sets the global + and marks `adpd_mode = MPF_MODE` ("not applied"); the binary path applies at + `config_detector`/`run_array` time. Unify on: setters mutate `g_cfg` only; the + measurement core calls `config_detector` from `g_cfg` when `adpd_mode` requires it. +- **Init/default handling once.** Fork `A` force-sets `init=true` to silence the + "preset not initialized" logs ([commands.cpp:267-268]); the binary path warns. Pick one + rule (default-initialized config is valid) and apply it in the core. +- *Checkpoint after this step:* set currents/gains via the text adapter, run via the + binary adapter → identical values applied. + +--- + +## 7. The three adapters + +### 7a. Binary adapter (`run_esp.cpp`) — FROZEN wire +- Keep the `cmd_arr[0]` switch and **every byte** it reads/writes (§2). +- Each case: decode wire → undo wire convention (gain `−1`, base-128 length) → call the + **same core fn** the other adapters call → encode response in the frozen format. +- This is where §2's conformance gate applies. + +### 7b. Text-line adapter (`frontend_text.cpp`, replaces `do_command.h`) — de-obscure freely +- Keep the verb names (`set_currents`, `arrun`, `q`, `ww`, `ff`, `get_par`, …) as the + human/console surface; route each to a core fn. +- Replace the `hash()` dispatch (silent-collision risk, sweep #19) with a + `strcmp`/`string_view` table if cheap; not a blocker. + +### 7c. openJII text/JSON adapter (cloud/usb only) — built to the spec, not ported wholesale +- Implements [CommunicationProtocolOpenJIISerial.md]: first-byte mode detect (`{`/`[` → + JSON envelope, else LINE), flat `set` envelope, command-as-root, the error taxonomy. +- Reuse fork `A`'s parser/framing ([commands.cpp]) as a **skeleton only**; **discard its + hand-printed `Serial.printf("{…}")` bodies** — they are exactly the per-command + format-strings the spec forbids. Each command instead supplies a **tree-builder** that + consumes the core's typed result. +- **Snapshot commands → build a small tree → serialize.** **Measurement commands + (`arrun`/`q`/MPF) → stream one well-formed JSON value** (an array of frame objects) into + the command's single `set` position — no `stream_start`/`stream_end` side-objects, no + raw bytes (those corrupt the envelope). Exact shape per **D6** (HW-verified). +- ArduinoJson is pulled in **only** for the cloud env (§11); the `ambyte` image stays JSON-free. +- Brings the two `A`-only verbs (`set_pd_gain`, `read_light_env`) into the core (D4). + +### 7d. The shared openJII comms module (`lib/openjii_proto/`) — device-agnostic (D5) +The protocol machinery is **not** amBIT-specific. Factor spec §11's five pieces into a +reusable library other firmware can adopt verbatim: + +1. **Receiver** — first-non-whitespace byte selects mode; string-aware brace/bracket-depth + detects JSON completeness; overflow → `{"error":"rx_overflow"}`, JSON idle 1 s → + `{"error":"json_timeout"}`. +2. **JSON serializer** (strict JSON) + **3. scalar-aware LINE serializer** (scalar → bare, + container → JSON). +4. **Path resolver** — `.`-walk (map key / array index) + `keys()`, with the §9 error set. +5. **Envelope writer** — header → flat `set` (one element per executed command/repeat) → + frame token. + +Device-facing API is tiny: per command, the device registers a **tree-builder** (snapshot) +or a **stream-writer** (measurement) that consumes a core typed result. amBIT supplies +those; the module knows nothing about amBIT. Built **fresh** to the spec (CO2Dot too old — +D5); iterate the spec via HW tests (D5/D6 workflow). + +**Wire-convention boundary (the key correctness rule):** normalization happens in the +adapter. Example — gains: binary subtracts 1 and validates 1..6; text/JSON pass the raw +0-indexed value. The core only ever sees 0-indexed gains. + +--- + +## 8. Wrench deletion (D1) + +Tracing (this session) established Wrench's full footprint: + +- **Single entry point:** [do_command.h:60] `case hash("C")` reads chars until `?` and + calls `do_c(c)`. Text/computer console only. +- **Never on the binary or app paths:** [run_esp.cpp] has no Wrench reference; `H` never + sends a `C` script; fork `A` already shipped without Wrench. +- **Zero unique commands.** `do_c.cpp` registers 11 native fns (`set_currents`, + `set_gains`, `config`/`detector_preset`, `set_arr`, `reset`, `run`, `run_mpf`, + `get_par`, `get_temp`, `print`, `disp`) — every one is a thin wrapper over a core fn + that already exists as a text verb **and** a binary opcode. (`idle` is defined but + never registered — dead even inside the VM.) +- **It is the 3rd config copy:** `do_c.cpp`'s `set_currents`/`set_gains` write file-local + statics independent of both other configs. +- **It owns the `arr_reset` overflow** (sweep #5, now gone with it) and an 8-slot `wr_run_arr[]` + store referenced nowhere else. + +**Action:** delete `src/src/wrench.{cpp,h}`, `src/do_c.cpp`, the `C` case, and all +`#include "src/wrench.h"`. The only capability lost is **on-device control-flow scripting** +(loops/variables/the 8 pre-loaded array slots) on the bench console — which moves +host-side, matching how the ambyte and app already operate. + +**Wins:** −17.4k LOC from the default image, −1 config copy, −1 buffer overflow, −1 dead fn. + +--- + +## 9. PAM / data_utils / sink unification (folded) + +`PAM.cpp` diverged on two orthogonal axes — treat separately. + +- **Axis A (transport):** fork `A`'s `json_output` bool + 5-arg `run_arr_type1` overload + vs `T`'s `CONNECTION_TYPE` branches. **Resolution:** keep `T`'s structure; fold `A`'s + JSON branch in as `case CLOUD` at each send site (~4: `run_arr_type1`, + `external_trigger_run`, `MPF`, the env path). Delete the 5-arg overload + `json_output`. +- **Axis B (semantics):** the D2 centi-°C encoding and D3 ambient correction already + live in `T` ([PAM.cpp] diffs confirmed). Keep them; rewrite fork `A`'s + `send_env_json_decoded` for centi-°C (its old-format unpack is stale — sweep #7). +- `data_utils.cpp`: base = `T` (full FSM send + the **required** byte-sum checksum, §2). + **Re-graft `send_json(const char*)`** from `A` for the `CLOUD` sink (~10 lines, decl in + [data_utils.h]). +- While here, sweep the cheap co-located correctness items (uninit locals #10, + leak-on-early-return #11, unaligned `*(float*)&cmd_arr[3]` → `memcpy` #12). +- *Out of scope:* the `dataclass` ring-buffer over-engineering (#18). + +--- + +## 10. main loop + text-path framing (de-obscure, text only) + +Base = `T`'s [ambit-1.ino] (it has the power management — **keep**: `ambit_light_sleep()`, +UART/GPIO sleep-wake config, sleep thresholds, `AMBIT_BOOT_IDLE` heartbeat, triple-tap-BOOT +reset ISR). + +**Cherry-pick fork `A`'s framing** ([ambit-1.ino:89-111] in `A`) as the de-obscuring fix +for sweep #20: decide LINE vs JSON by the **first printable char** and drop junk +bytes while mode is `UNKNOWN`, instead of `T`'s glitch-sensitive `c > 127` MSB heuristic +that mis-fires on the DTR/RTS open glitch and emits `Unknown cmd`. This is a **text-path** +change and does not touch the binary contract. + +Also (sweep #16): **stop echoing parsed bytes** in `serial_read_until`/`flush_serial` +— that echo currently injects bytes into the binary stream that `H` has to discard as +"debug/echo bytes" ([uart_sensors.c:382]). Move these helpers' single definition into +`serial.*` (kills the 4-TU re-declaration, #15). + +**Variant separation over runtime auto-detect.** The ambyte is a separate product on a +dedicated FFC UART; the app is USB-CDC. Prefer **build-flag-separated frontends** (§11.5) +to runtime first-byte transport sniffing — cleaner and less bug-prone. Keep the framed/junk- +drop fix for robustness either way. + +--- + +## 11. platformio.ini + build flags + +```ini +[platformio] +default_envs = ambyte + +[env] +platform = espressif32 +board = esp32-c3-devkitm-1 +framework = arduino +monitor_speed = 115200 +build_flags = -DDEBUG=0 + +[env:ambyte] ; binary FSM over UART0/FFC, light-sleep +build_flags = + ${env.build_flags} + -DARDUINO_USB_CDC_ON_BOOT=0 + -DVARIANT_AMBYTE=1 + +[env:cloud] ; text+JSON over native USB-CDC +build_flags = + ${env.build_flags} + -DARDUINO_USB_MODE=1 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DUSB_VID=0x16C0 + -DUSB_PID=0x0483 + '-DUSB_MANUFACTURER="JII"' + '-DUSB_PRODUCT="Ambit"' + -DVARIANT_CLOUD=1 +lib_deps = ${env.lib_deps} + bblanchon/ArduinoJson ; only the cloud image pays for JSON +``` + +- `VARIANT_AMBYTE` → binary adapter + FSM + light-sleep idle loop. +- `VARIANT_CLOUD` → JSON/line adapter + `send_json` + (D4) `read_light_env`. +- **No `ENABLE_WRENCH`** — Wrench is deleted (D1). +- Core (`core.*`, drivers, `AmbitConfig`) compiles in both. +- After changing envs, delete the per-env `sdkconfig.*` so it regenerates. + +### Coverage matrix (every dialect → the §5 core ops) + +| Core op | text (`do_command`) | binary (`run_esp` cmd) | JSON (`commands`) | +|---|:--:|:--:|:--:| +| set_currents | ✓ | 2 | ✓ | +| set_gains | ✓ | 1 | ✓ | +| set_pd_gain (D4) | — | (1) | ✓ | +| config_detector | (via run) | 10 | (via run) | +| run_array | `arrun` | 21 | `arrun` | +| run_mpf | `q` | 20 | `q` | +| get_par | ✓ | 31 | ✓ | +| get_temp / _raw | `temp` | 32 / 34 | `temp` | +| baseline | ✓ | 6 | ✓ | +| set_actinic | ✓ | 4 | ✓ | +| set_name | ✓ | 37 | ✓ | +| set_metadata | — | 37 | — | +| set_emit / set_spec | ✓ | — | ✓ | +| external / flash trigger | `ww` / `ff` | — | `ww` / `ff` | +| read_light_env (D4) | — | — | ✓ | +| nvs clean / update | `clean_nvs` | 17 / 18 | `clean_nvs` | +| info / check | `check` | 33 | `check` | +| reboot | `reboot` | — | `reboot` | +| blink | `a`/`aa` | 5 | `a`/`aa` | + +--- + +## 12. Execution order (unification-first) + +1. **Branch + scaffold.** Branch off `fw_new`. Add `CLOUD` to the `CONNECTION_TYPES` + enum; add the two PIO envs + flags (§11). *Checkpoint: `ambyte` env still builds/runs.* +2. **Delete Wrench (D1, §8).** Remove `wrench.*`, `do_c.cpp`, the `C` case, includes. + *Checkpoint: `ambyte` builds; every former Wrench op still reachable as a text verb.* +3. **Unify CONFIG (§6).** Collapse the three copies into `AmbitConfig`. *Checkpoint: set + via text, run via binary → identical currents/gains applied.* +4. **Extract CORE (§5).** Move each command body into one core fn; point the text and + binary adapters at the shared fns. Normalize wire conventions at the adapter boundary. + *Checkpoint: every §11 row reachable from both `ambyte` and text, same numbers.* +5. **Consolidate serial helpers + drop echo (§10).** Single prototype in `serial.*`, + no echo-on-parse. *Checkpoint: clean binary round-trip, no stray echoed bytes.* +6. **PAM + data_utils sink merge (§9).** Add `case CLOUD`, re-graft `send_json`, rewrite + env-JSON for centi-°C, sweep #10/#11/#12. *Checkpoint: AMBYTE numbers byte-identical + to pre-merge `fw_new`; CLOUD JSON validated vs backend schema.* +7. **Port JSON adapter (§7c).** Bring `commands.cpp`'s parser onto the core as + `frontend_json.cpp` (cloud env only); delete `commands.cpp/.h`. +8. **main-loop framing (§10).** Adopt fork `A`'s first-printable-char framing on the text + path; keep `fw_new`'s power management. *Checkpoint: DTR/RTS open glitch no longer + emits `Unknown cmd`.* +9. **Build both envs; HW-test both variants (§14).** + +--- + +## 13. Frozen-contract conformance gate (§2) + +> **Runnable procedure: [HW_CONFORMANCE.md](HW_CONFORMANCE.md).** Step 4b (binary frontend +> → core) was **HW-verified byte-conformant on 2026-06-05** against a real ambyte. Keep the +> procedure as the reusable gate for later `run_esp.cpp` / FSM changes (Step 5+). + +Before merging the binary adapter changes (step 4–5), establish a regression artifact and +keep it green through every later step: + +- **Capture a golden byte trace** of the current `fw_new` ↔ `H` exchange for each binary + command (or derive the expected frames from [uart_sensors.c] + [ambit_protocol.h]): + wake/ack, the 8-byte cmd frame, `CMD_DONE`/`CMD_END`, raw response sizes, and a full FSM + data round-trip (header checksum, **byte-sum** data checksum, length encoding). +- **Assert byte-for-byte identity** of the refactored binary adapter's output against the + golden trace. Any diff = a contract break. +- Explicitly assert the obscure-but-frozen encodings: base-128 MPF length, 1-indexed + gains, struct sizes (136 / 48 / 248), `eof_mark==2025`. +- The conformance gate covers the **wire**; D2/D3 semantic changes are gated separately by + **consumer sign-off** (§3), since `H` forwards those arrays raw. + +--- + +## 14. Risks & test checklist + +| Risk | Mitigation | +|---|---| +| Refactoring the binary adapter breaks the wire contract with shipped ambytes | §13 golden-trace conformance gate, kept green every step; freeze fw_new binary behavior as reference | +| D2/D3 semantic changes alter measured values / cloud ingestion | Consumer sign-off before step 6; freeze AMBYTE numbers; branch raw-vs-corrected on `CLOUD` only if needed | +| Dropped step-marks were feeding `H`'s `mark_event_*` | Confirm with `H` owner before locking D2; else add a dedicated marker channel | +| CONFIG unification changes "when config is applied" semantics | Preserve set-then-apply-on-run; checkpoint 3 verifies cross-adapter equivalence | +| Wrench deletion removes a used scripting workflow | D1 stakeholder check first; move scripting host-side | + +**Per-variant test checklist** +- [ ] `ambyte`: full wake→length→data FSM round-trip to a real ambyte; **byte-sum checksum OK**. +- [ ] `ambyte`: idle current drops to light-sleep between commands; triple-tap BOOT resets; UART wakes within threshold. +- [ ] `cloud`: line + JSON over USB-CDC; `set_pd_gain`/`read_light_env` (if D4=keep). +- [ ] both: `set_currents`/`set_gains` from any adapter reflected in the run (config unify). +- [ ] both: `get_par`, `get_temp`, `baseline`, `arrun`, `run_mpf` expected output. +- [ ] regression: AMBYTE measurement numbers byte-identical vs pre-merge `fw_new`. +- [ ] glitch: opening the USB port (DTR/RTS) does not emit `Unknown cmd`. + +--- + +## Appendix — file-by-file actions + +Legend: `TRUNK`=take `fw_new` · `MERGE`=reconcile · `PORT`=bring `A` feature onto core · +`DROP`=remove · `KEEP`=unchanged. + +| File | Action | +|---|---| +| `wrench.{cpp,h}`, `do_c.cpp` | **DROP** (D1, §8) | +| `do_command.h` | **MERGE** → `frontend_text.cpp` (thin text adapter, §7b) | +| `run_esp.cpp` | **TRUNK + MERGE** → binary adapter, frozen wire, calls core (§7a) | +| `commands.cpp/.h` (fork `A`) | **SKELETON ONLY** → openJII shared module `lib/openjii_proto/` (§7c/§7d); discard hand-printed bodies | +| new `lib/openjii_proto/` | **NEW** — device-agnostic openJII comms module, built fresh to the spec (§7d, D5) | +| `PAM.cpp` / `PAM.h` | **MERGE** — `CONNECTION_TYPE` sinks + `CLOUD`; D2/D3 kept; D4 re-add (§9) | +| `data_utils.cpp` | **MERGE** — FSM + byte-sum checksum (keep) + re-graft `send_json` (§9) | +| `data_utils.h` | TRUNK + re-add `send_json` decl | +| `config.h` | TRUNK + add `CLOUD`; home of `AmbitConfig` (§6) | +| new `core.cpp/.h` | **NEW** — the unified operation set (§5) | +| `serial.cpp/.h` | TRUNK + absorb `serial_read_until`/`flush_serial`, drop echo (§10) | +| `ambit-1.ino` | **MERGE** — `fw_new` power mgmt + `A` text framing (§10) | +| `nvs1.*`, `self_test.cpp`, `devices_init.*`, `pin_config.h`, drivers | KEEP (identical) — nvs checksum `&`→`|` done (#6) | +| `platformio.ini` | **MERGE** — multi-env + flags (§11) | +| `open-jii/`, `*.ipynb` | OUT OF SCOPE (server/tooling, not firmware) | + +--- + +## Appendix B — correctness & cleanup catalog (folded from REFACTOR_PLAN) + +The single-fork review's items, folded here so this file is the only plan. Referenced +above as `sweep #N` / `#N` (original numbering kept). **Status:** ✅ done · ▶ owned by a +named step (do it there) · ☐ open sweep item (cheap, isolated — fold into the nearest +step) · ⏸ deferred/optional. Line numbers are current as of this session. + +| # | Item | Where (current) | Status | +|---|------|-----------------|--------| +| 1 | Wrench VM (17.4k LOC) for ~8 commands | `wrench.*`, `do_c.cpp` | ✅ deleted | +| 2 | Three parallel config copies | global / `run_esp` `_local` / `do_c` statics | ✅ unified to the PAM global | +| 3 | Massive dead code | `PAM.cpp` `/* */` blocks + `data_utils.cpp` `//` block | ✅ deleted (PAM.cpp 1913→1418, data_utils.cpp 716→561; commented-out, so **zero binary change** — Flash identical) | +| 4 | Measurement loop copy-pasted 5–6× | `PAM.cpp` `run_arr_type1`(153)/`run_trigger_spacer`(402)/`external_trigger_run`(580)/`_Flash`(727)/`MPF`(910)/`fluor_offset`(1319) | ▶ Step 4 — extract `PamBuffers` RAII + `read_one_frame()` + `send_results()` | +| 5 | `arr_reset` buffer overflow | was `do_c.cpp` | ✅ gone with Wrench | +| 6 | FW-info checksum `&`→`\|` | `nvs1.cpp:16` | ✅ fixed this session | +| 7 | `PAM_retrieve_env` decode vs centi-°C encode | `PAM.cpp` env path | ▶ Step 6 — rewrite for centi-°C (D2) | +| 8 | Truncating casts in parsing | `w` length clipped to uint8_t; `a`/`aa` pointless casts | ✅ done — `w` length now uint16_t, casts dropped | +| 9 | Non-void FSM fns miss returns (UB) | uncalled `fsm_send_waitesp` (missing return) + dead `fsm_wake_up_calls(void)` | ✅ both deleted (uncalled). Reset-guards N/A — per-run `dataclass` objects reset on construction | +| 10 | Uninitialized locals | `run_arr_type1`/`run_trigger_spacer`/`fluor_offset` comma-decls | ✅ initialized (behavior-neutral; were assigned before use) | +| 11 | Leaks on early return | `PAM.cpp:180-186` 7× `new dataclass` then `return -1` | ☐ stack-allocate `dataclass`, drop new/delete | +| 12 | Unaligned/strict-alias float reads (trap on C3) | `run_esp.cpp:234,242,294,317` `*((float*)&cmd_arr[..])` | ☐ `memcpy` — **frozen wire, bytes unchanged** | +| 13 | Protocol magic numbers scattered | `data_utils.h` vs literals (`ambit-1.ino:167`, `run_esp.cpp`) | ▶ §2 freezes them; centralize into one `proto` header | +| 14 | Pins half-symbolic | `digitalWrite(10/1,…)`, raw `GPIO_NUM_*` | ☐ name pin 10, use symbols | +| 15 | `serial_read_until` re-declared in 4 TUs | `data_utils.cpp`, `PAM.cpp`, `run_esp.cpp`, `ambit-1.ino:59` | ▶ Step 5 — one prototype in `serial.*` | +| 16 | `serial_read_until`/`flush_serial` echo non-target bytes onto the link | corrupts the binary stream | ▶ Step 5 — drop echo | +| 17 | `load_calibration_info` `isKey()`+`getX()` ×~20 | `nvs1.cpp` | ⏸ deferred — dropping `isKey` makes absent keys use the `getX` default not the struct default (config semantics, needs sign-off) | +| 18 | `dataclass` ring-buffer over-engineered | `data_utils.cpp:142` wrap path likely dead; inverted `length`/`_length` | ⏸ deferred (out of scope) | +| 19 | `hash()` dispatch silent-collision risk | `do_command.h:20` | ⏸ optional — `strcmp`/`string_view` table | +| 20 | `c > 127` mode-switch glitch-sensitive | `ambit-1.ino:164` | ▶ Step 8 — framed sync / first-printable-char (text path) | + +[CommunicationProtocolOpenJIISerial.md]: ../CommunicationProtocolOpenJIISerial.md +[uart_sensors.c]: ../ambyte-iot-ludo/components/uart_sensors/uart_sensors.c +[ambit_protocol.h]: ../ambyte-iot-ludo/components/device_commands/include/ambit_protocol.h +[device_commands.c]: ../ambyte-iot-ludo/components/device_commands/device_commands.c +[do_command.h]: src/do_command.h +[run_esp.cpp]: src/run_esp.cpp +[commands.cpp]: ../fw_appCompatible/src/commands.cpp +[PAM.cpp]: src/PAM.cpp +[data_utils.h]: src/data_utils.h +[data_utils.cpp]: src/data_utils.cpp +[config.h]: src/config.h +[ambit-1.ino]: src/ambit-1.ino +[core.cpp]: src/core.cpp +[core.h]: src/core.h diff --git a/plans/RELEASE_PROCESS.md b/plans/RELEASE_PROCESS.md new file mode 100644 index 0000000..b812747 --- /dev/null +++ b/plans/RELEASE_PROCESS.md @@ -0,0 +1,94 @@ +# Firmware release process + +AMBIT releases use the application, bootloader, partition table, OTA data init, +and manifest produced by the qualified pull-request run. The release workflow +does not rebuild firmware. For ticket 21 it is intentionally pinned to v1.1.4, +PR 4, and operator-recorded PR head, Actions run, artifact, and application +SHA-256 values. + +```mermaid +flowchart LR + PR[Qualified PR run] -->|exact artifact ID| S[Stage draft] + S --> V[REST metadata + authenticated byte proof] + V -->|firmware-release approval| F[Publish-only finalizer] + F --> I[Immutable latest release] + I --> A[Anonymous byte + attestation verification] +``` + +## One-time repository setup + +Before merging the release-triggering change, an administrator must: + +1. Enable release immutability in the repository settings. It applies only to + subsequently published releases. +2. Create the Actions environment `firmware-release`, restrict it to `main`, and + configure the required reviewers who authorize the single draft-to-published + transition. +3. Allow GitHub Actions to create releases with the repository `GITHUB_TOKEN`. +4. Set these repository Actions variables from the already-qualified PR run: + + | Variable | Required value | + | --- | --- | + | `AMBIT_V114_PR_HEAD_SHA` | Full 40-character lowercase hexadecimal head SHA of the qualified PR 4 run | + | `AMBIT_V114_PR_RUN_ID` | Numeric ID of the successful `.github/workflows/pr.yml` run at the pinned PR head | + | `AMBIT_V114_ARTIFACT_ID` | Numeric ID of that run's live `firmware-${AMBIT_V114_PR_HEAD_SHA}` artifact | + | `AMBIT_V114_APP_SHA256` | Lowercase SHA-256 of `ambit-fw-v1.1.4.bin` from that artifact | + +The workflow fails if any value is missing or if GitHub says the artifact came +from another run, workflow, event, conclusion, or PR head. It never selects an +artifact by creation time. + +## Staging boundary + +The staging job first requires semantic-release's preview to be exactly 1.1.4 +and requires both `v1.1.4` tag and release to be absent. It downloads the pinned +artifact ID, checks the exact five-file allowlist and manifest offsets, sizes, +and SHA-256 values, and separately requires the application hash above. + +The repository pins `@semantic-release/github` to 11.0.6 with +`draftRelease: true`. PR CI executes its installed `lib/publish.js` with a +mocked Octokit and proves that it creates a draft, uploads assets, and issues no +publication PATCH. The same test executes `lib/success.js` and proves the later +success phase does not publish the draft. Semantic-release is invoked only in +the staging job. The exact npm tarball inspected for this qualification has +registry SHA-1 `3320c373334858a3aefb11cf75abccb7cc438040`, archive SHA-256 +`4bd998c6530867151587e99ca8abb7ede0d1e51904195f8945caf2d1eed22554`, and +registry integrity +`sha512-ctDzdSMrT3H+pwKBPdyCPty6Y47X8dSrjd3aPZ5KKIKKWTwZBE9De8GtsH3TyAlw3Uyo2stegMx6rJMXKpJwJA==`. + +After staging, the job verifies the lightweight tag target, stable draft +metadata, the exact five REST asset records (IDs, labels, timestamps, sizes, +and server SHA-256 digests), manifest agreement, and authenticated asset +downloads. It uploads only a proof record for the protected finalizer; it does +not publish the draft. + +GitHub places draft asset browser URLs in the draft's repository-scoped +`untagged-*` namespace. The staging proof binds that temporary namespace to the +draft release and records it. Publishing moves only those browser URLs to the +canonical `releases/download/v1.1.4/*` namespace; IDs, timestamps, sizes, and +digests are not allowed to change. Anonymous verification always constructs +and downloads the canonical published URLs instead of reusing a draft URL. + +## Protected finalizer + +After environment approval, the finalizer downloads the staging proof and +revalidates the still-draft release, tag, unchanged REST records, and +authenticated bytes. It sends exactly one GitHub REST update with +`draft=false` and `make_latest=true`. It does not invoke semantic-release, +build firmware, upload assets, or create/move a tag. + +The finalizer then requires GitHub to report the same release as immutable, +stable, and latest. Asset IDs, creation/update timestamps, sizes, and digests +must remain identical to the staging proof. Finally, anonymous downloads must +hash to the proved values and `gh release verify` / `gh release verify-asset` +must validate GitHub's immutable-release attestations. + +An existing tag, existing release or draft, duplicate/ambiguous API record, +proof drift, failed publication response, missing immutability, or byte mismatch +stops the workflow. Do not rerun semantic-release to repair a partial stage; +inspect the draft/tag and resolve the exceptional state manually before any new +release attempt. + +For a later firmware version, update the one-shot version/tag/qualification +pins and repository variables in a reviewed release-process change before its +qualified artifact is published. diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..af06cbe --- /dev/null +++ b/platformio.ini @@ -0,0 +1,48 @@ +; PlatformIO Project Configuration File +; https://docs.platformio.org/page/projectconf.html + +[platformio] +; Build OUTSIDE the Documents tree. That folder is OneDrive-synced / AV-scanned, +; which blocks fresh creation of .pio/build sub-dirs and makes the compiler fail with +; "opening dependency file <...>.cpp.d: No such file or directory" on a clean build. +; LOCALAPPDATA is local-only (and, unlike TEMP, not auto-cleaned). +; On Linux/macOS export it first, e.g.: LOCALAPPDATA=$HOME/.cache pio run +build_dir = ${sysenv.LOCALAPPDATA}/pio_build/ambit_fw_new +default_envs = ambit + +; ── single image ───────────────────────────────────────────────────────────── +; One firmware serves every host at runtime by first-byte routing (ambit-1.ino +; loop()): binary FSM for the Ambyte over UART0/FFC (frozen wire, >127 framing +; bytes), the text console for the openJII app + Calibratron (bare printable +; lines), and the openJII JSON envelope ('{'/'[' first byte). Replaces the former +; ambyte/cloud build-variant pair: that split assumed the app variant would live +; on native USB-CDC, but all hosts ended up on the same UART0. +[env] +platform = espressif32 +board = esp32-c3-devkitm-1 +framework = arduino +monitor_speed = 115200 +; Pin the dual-OTA partition layout explicitly. This is the Arduino-ESP32 4 MB +; default (otadata + ota_0/ota_1 1.25 MB each + spiffs) the board was already +; using implicitly (naming it guarantees a future build can't silently switch +; to a single-app scheme and break OTA). SAME layout = no NVS shift, so the +; one-time bootstrap reflash preserves calibration. VERIFY a fielded unit's +; table matches before reflashing it (esptool read_flash 0x8000 0xc00). +board_build.partitions = default.csv +; Version is injected at build time by tools/version.py (semantic-release tag +; in CI, git describe locally). It feeds cmd 33/2, `hello`, and the JSON identity. +extra_scripts = pre:tools/version.py +lib_deps = + adafruit/Adafruit BusIO + bblanchon/ArduinoJson@^7 + +[env:ambit] +build_flags = + -DARDUINO_USB_CDC_ON_BOOT=0 ; Serial -> UART0 (GPIO20 RX / 21 TX) + ; Gag ARDUHAL. setup()'s esp_log_level_set() silences IDF logging only; the + ; Arduino ESP_LOGx macros compile to Serial.printf and were writing ASCII + ; straight onto the protocol port. Observed on the bench: a stray frame byte + ; made the router answer a host with + ; "[E][ambit-1.ino:215] loop(): [INO] Unknown cmd 171" instead of protocol + ; bytes. Nothing may emit on UART0 except an explicit console reply. + -DCORE_DEBUG_LEVEL=0 diff --git a/run_esp.cpp b/run_esp.cpp deleted file mode 100644 index 4c5d612..0000000 --- a/run_esp.cpp +++ /dev/null @@ -1,372 +0,0 @@ -#include -#include "data_utils.h" -#include "src/as7341/spec_meas.h" -#include "src/mlx90632/u_mlx.h" -#include "PAM.h" -#include -#include "nvs1.h" - -#define TAG "ESP" -#define ESP_CMD_HEADER 160 -#define ESP_CMD_DONE 161 -#define ESP_CMD_END 240 -#define ESP_WAKE_FOR_CMD 170 -#define MAX_ARR_LEN 16 - -// keep a local copy of settings for run-time change -static adpd_current_config_t adpd_current_config_local; -static adpd_gains_config_t adpd_gains_config_local; -extern Preferences preferences; - -extern uint8_t CONNECTION_TYPE; -int serial_read_until(uint8_t target1, uint8_t target2 = 0, uint8_t target3 = 0, uint16_t timeout = 20, bool remove = false); - -// extern char ambit_name[]; -// float_t actinic_coef = 1.0; -// float_t spec_coef = 1.0; -// extern double mlx_emissivity; - -int do_esp_cmd() -{ - uint8_t cmd_arr[8], c, ret; - ESP_LOGV(TAG, "parse ESP commands"); - - // search for cmd header - ret = serial_read_until(ESP_CMD_HEADER, 0, 0, 100, true); - if (ret != 1) - { - ESP_LOGE(TAG, "ESP cmd header failed"); - return -1; - } - - CONNECTION_TYPE = CONNECTION_TYPES::AMBYTE; - - // the real cmd will follows - c = Serial.readBytes(cmd_arr, 8); - if (c < 8) - { - ESP_LOGE(TAG, "ESP cmd parse failed"); - return -1; - } - // Serial.printf("cmd is %d, %d, %d, %d, %d, %d, %d, %d\n", cmd_arr[0], cmd_arr[1], cmd_arr[2], cmd_arr[3], cmd_arr[4], cmd_arr[5], cmd_arr[6], cmd_arr[7]); - - switch (cmd_arr[0]) - { - case 1: // set PD gains - if ((cmd_arr[1] > 0) && (cmd_arr[1] < 7)) - adpd_gains_config_local.Fluo = cmd_arr[1] - 1; - if ((cmd_arr[2] > 0) && (cmd_arr[2] < 7)) - adpd_gains_config_local.FluoRef = cmd_arr[2] - 1; - if ((cmd_arr[3] > 0) && (cmd_arr[3] < 7)) - adpd_gains_config_local.IR = cmd_arr[3] - 1; - if ((cmd_arr[4] > 0) && (cmd_arr[4] < 7)) - adpd_gains_config_local.IRRef = cmd_arr[4] - 1; - if ((cmd_arr[5] > 0) && (cmd_arr[5] < 7)) - adpd_gains_config_local.Sun = cmd_arr[5] - 1; - if ((cmd_arr[6] > 0) && (cmd_arr[6] < 7)) - adpd_gains_config_local.Leaf = cmd_arr[6] - 1; - adpd_gains_config_local.init = true; - ESP_LOGV(TAG, "gains are %d, %d, %d, %d, %d, %d", adpd_gains_config_local.Fluo, adpd_gains_config_local.FluoRef, adpd_gains_config_local.IR, adpd_gains_config_local.IRRef, adpd_gains_config_local.Sun, adpd_gains_config_local.Leaf); - Serial.write(ESP_CMD_DONE); - break; - - case 2: // set currents - if ((cmd_arr[1] < 127)) - adpd_current_config_local.I620 = cmd_arr[1]; - if ((cmd_arr[2] < 127)) - adpd_current_config_local.I720 = cmd_arr[2]; - if ((cmd_arr[3] < 127)) - adpd_current_config_local.IR = cmd_arr[3]; - adpd_current_config_local.init = true; - ESP_LOGV(TAG, "currents are %d, %d, %d", adpd_current_config_local.I620, adpd_current_config_local.I720, adpd_current_config_local.IR); - Serial.write(ESP_CMD_DONE); - break; - - case 10: // array run config - if (adpd_gains_config_local.init == false) - ESP_LOGE(TAG, "Gain preset not initized, use default!"); - if (adpd_current_config_local.init == false) - ESP_LOGE(TAG, "Current preset not initized, use default!"); - conf_slow_FR_1(adpd_current_config_local.I620, adpd_current_config_local.I720, adpd_current_config_local.IR, - adpd_gains_config_local.Fluo, adpd_gains_config_local.FluoRef, adpd_gains_config_local.Sun, adpd_gains_config_local.Leaf, adpd_gains_config_local.IR, adpd_gains_config_local.IRRef); - adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; - Serial.write(ESP_CMD_DONE); - break; - - case 20: // run spacer - { - if (adpd_gains_config_local.init == false) - ESP_LOGE(TAG, "Gain preset not initized, use default!"); - if (adpd_current_config_local.init == false) - ESP_LOGE(TAG, "Current preset not initized, use default!"); - - uint16_t length = (((uint16_t)cmd_arr[1]) << 8) + cmd_arr[2]; - uint8_t interval = cmd_arr[3]; - bool change_act = (bool)cmd_arr[4]; - uint8_t act = cmd_arr[5]; - - // Serial.printf("%d, %d, %d, %d\n", length, interval, change_act, act); - - Serial.write(ESP_CMD_DONE); - run_trigger_spacer(length, interval, change_act, act, true); - Serial.write(ESP_CMD_END); - } - break; - - case 21: // run - { - uint8_t arr_length = cmd_arr[1]; - uint8_t led_persist = cmd_arr[2]; - bool allow_interrupt = (bool)cmd_arr[3]; - - uint8_t cc = 0; - if ((arr_length == 0) || (arr_length > MAX_ARR_LEN)) - { - ESP_LOGE(TAG, "run array wrong length: %d", arr_length); - break; - } - uint8_t run_arr[arr_length * 8]; - if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1) - { - if (adpd_gains_config_local.init == false) - ESP_LOGE(TAG, "Gain preset not initized, use default!"); - if (adpd_current_config_local.init == false) - ESP_LOGE(TAG, "Current preset not initized, use default!"); - conf_slow_FR_1(adpd_current_config_local.I620, adpd_current_config_local.I720, adpd_current_config_local.IR, - adpd_gains_config_local.Fluo, adpd_gains_config_local.FluoRef, adpd_gains_config_local.Sun, adpd_gains_config_local.Leaf, adpd_gains_config_local.IR, adpd_gains_config_local.IRRef); - adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; - } - - cc = Serial.readBytes(run_arr, arr_length * 8); - if (cc != (arr_length * 8)) - { - ESP_LOGE(TAG, "run array elements count %d not match config %d", cc, arr_length); - break; - } - Serial.write(ESP_CMD_DONE); - run_arr_type1(arr_length, run_arr, led_persist, allow_interrupt); - Serial.write(ESP_CMD_END); - } - break; - - case 31: // get spec - { - uint16_t spec[12] = {0}; - float par = get_PAR(spec); - memcpy(spec + 10, &par, 4); - Serial.write(ESP_CMD_DONE); - Serial.write((uint8_t *)spec, 24); - Serial.write(ESP_CMD_END); - } - break; - - case 32: // get temp - { - double leaf, chip; - mlx_measure(&leaf, &chip); - Serial.write(ESP_CMD_DONE); - int16_t t1 = (int16_t)(leaf * 10); - int16_t t2 = (int16_t)(chip * 10); - Serial.write((uint8_t *)(&t1), 2); - Serial.write((uint8_t *)(&t2), 2); - Serial.write(ESP_CMD_END); - } - break; - - case 33: // retrieve ambit info - { - if (cmd_arr[1] == 1) - { // send calibration info - Serial.write(ESP_CMD_DONE); - Serial.write((uint8_t *)&ambit_calibration_local, sizeof(ambit_calibration_info_t)); - Serial.write(ESP_CMD_END); - } - else if (cmd_arr[1] == 2) - { // send FW info - Serial.write(ESP_CMD_DONE); - Serial.write((uint8_t *)&ambit_FW_info, sizeof(ambit_FW_info_t)); - Serial.write(ESP_CMD_END); - } - else if (cmd_arr[1] == 3) - { // send metadata - Serial.write(ESP_CMD_DONE); - Serial.write((uint8_t *)&metadata_epprom, sizeof(metadata_t)); - Serial.write(ESP_CMD_END); - } - else - { - Serial.write(ESP_CMD_END); - } - } - break; - - case 34: // get temp and raw - { - double leaf, leaf_1, chip; - int16_t a1, a2, a3, a4; - mlx_measure(&leaf, &chip, &leaf_1, &a1, &a2, &a3, &a4); - Serial.write(ESP_CMD_DONE); - int16_t t1 = (int16_t)(leaf * 10); - int16_t t2 = (int16_t)(leaf_1 * 10); - int16_t t3 = (int16_t)(chip * 10); - Serial.write((uint8_t *)(&t1), 2); - Serial.write((uint8_t *)(&t2), 2); - Serial.write((uint8_t *)(&t3), 2); - Serial.write((uint8_t *)(&a1), 2); - Serial.write((uint8_t *)(&a2), 2); - Serial.write((uint8_t *)(&a3), 2); - Serial.write((uint8_t *)(&a4), 2); - Serial.write(ESP_CMD_END); - } - break; - - case 37: // set metadata - { - Serial.write(ESP_CMD_DONE); - Serial.readBytes((uint8_t *)(&metadata_incoming), sizeof(metadata_t)); - Serial.write(ESP_CMD_END); - if (metadata_incoming.EOF_MARK == 2025) - save_metadata(); - load_info_from_nvs(false); - } - break; - - case 4: // try/set actinic - { - AS_LED_OFF(); - Serial.write(ESP_CMD_DONE); - uint8_t type = cmd_arr[1]; - uint8_t var = cmd_arr[2]; - uint8_t var2 = cmd_arr[3]; - float_t _factor = 1.0; - if (type == 1) - { // try actinics - AS_LED_Current(50); - AS_LED_ON(); - delay(3000); - AS_LED_Current(var); - delay(3000); - AS_LED_OFF(); - AS_LED_Current(0); - } - else if (type == 2) - { // set actinic offset - _factor = *((float *)&(cmd_arr[3])); - if ((_factor > 0.01) && (_factor < 1.01)) - { - preferences.begin("config", false); - preferences.putFloat("actinic", _factor); - preferences.end(); - ambit_calibration_local.actinic_coef = _factor; - } - } - else if (type == 4) - { // set actinic offset - _factor = *((float *)&(cmd_arr[3])); - if ((_factor > 0.05) && (_factor < 100.01)) - { - // preferences.begin("config", false); - // preferences.putFloat("spec", _factor); - // preferences.end(); - ambit_calibration_local.spec_coef = _factor; - } - } - else if (type == 5) - { - AS_LED_Current(var); - AS_LED_ON(); - delay(var2 * 100); - AS_LED_OFF(); - } - Serial.write(ESP_CMD_END); - } - break; - - case 5: - { - uint8_t ambit_id = cmd_arr[1]; - uint8_t intensity = cmd_arr[2]; - Serial.write(ESP_CMD_DONE); - if ((ambit_id < 4) && (intensity > 4) && (intensity < 254)) - as7431_blink(ambit_id, intensity); - Serial.write(ESP_CMD_END); - } - break; - - case 6: // do adpd baseline flash - { - Serial.write(ESP_CMD_DONE); - uint32_t ret[6] = {0}; - fluor_offset(ret); - preferences.begin("config", false); - preferences.putUInt("adpd_lit", ret[1]); - preferences.putUInt("adpd_sun", ret[2]); - preferences.putUInt("adpd_leaf", ret[3]); - preferences.putUInt("adpd_730", ret[4]); - preferences.putUInt("adpd_730r", ret[5]); - preferences.end(); - Serial.write(ESP_CMD_END); - load_info_from_nvs(false); - } - break; - - case 17: // nvs update scalar - { - uint8_t type = cmd_arr[1]; // 1: actinic - uint8_t dtype = cmd_arr[2]; // 1: float - if ((type == 1) && (dtype == 1)) - { // update actinic coef - Serial.write(ESP_CMD_DONE); - float_t _factor = *((float *)&(cmd_arr[3])); - if ((_factor > 0.01) && (_factor < 1.01)) - { - preferences.begin("config", false); - preferences.putFloat("actinic", _factor); - preferences.end(); - ambit_calibration_local.actinic_coef = _factor; - load_info_from_nvs(false); - } - Serial.write(ESP_CMD_END); - } - } - break; - - case 18: // nvs update array - { - uint8_t type = cmd_arr[1]; // 1: actinic linear test - float_t _factor = 1.0; - if (type == 1) - { // update actinic linear readings - uint16_t _readingsf[6] = {0}; - Serial.write(ESP_CMD_DONE); - Serial.readBytes((uint8_t *)_readingsf, 12); - uint16_t checksum = 0; - _factor = *((float *)&(cmd_arr[4])); - for (int i = 0; i < 5; i++) - { - checksum += _readingsf[i]; - } - if (checksum == _readingsf[5]) - { - preferences.begin("config", false); - preferences.putUShort("act_50", _readingsf[0]); - preferences.putUShort("act_100", _readingsf[1]); - preferences.putUShort("act_150", _readingsf[2]); - preferences.putUShort("act_200", _readingsf[3]); - preferences.putUShort("act_250", _readingsf[4]); - preferences.putFloat("actinic", _factor); - preferences.end(); - load_info_from_nvs(false); - } - Serial.write(ESP_CMD_END); - } - } - break; - - default: - ESP_LOGE(TAG, "Bad command"); - break; - } - - return 0; -} diff --git a/run_output.txt b/run_output.txt deleted file mode 100644 index 3465331..0000000 --- a/run_output.txt +++ /dev/null @@ -1,51 +0,0 @@ ----- Sent utf8 encoded message: "hello\r\n" ---- -NEW Name Here Ready ----- Sent utf8 encoded message: "check\r\n" ---- -Checking ADPD ADPD Found, chip version: 2 -Checking AS7341 Success -Checking MLX90632 Success 22 26.714896 ----- Sent utf8 encoded message: "get_par\r\n" ---- -8.47 -12,30,33,50,70,72,63,24,1,21 ----- Sent utf8 encoded message: "PAR\r\n" ---- -8.47 -12,30,33,50,70,72,63,24,1,21 ----- Sent utf8 encoded message: "temp\r\n" ---- -26.7764 27.6544 26.7764 ----- Sent utf8 encoded message: "baseline\r\n" ---- -80,3267,65568,65472,710,64 ----- Sent utf8 encoded message: "ww\r\n" ---- -Start -Run ----- Sent utf8 encoded message: "G\r\n" ---- -T:1617,S:964,R:8650,F:583,B:648 ----- Sent utf8 encoded message: "G\r\n" ---- -T:2707,S:971,R:8616,F:583,B:643 ----- Sent utf8 encoded message: "G\r\n" ---- -T:3670,S:967,R:8616,F:584,B:643 ----- Sent utf8 encoded message: "AA\r\n" ---- ----- Sent utf8 encoded message: "A0\r\n" ---- ----- Sent utf8 encoded message: "E\r\n" ---- -Stop -Exit ----- Sent utf8 encoded message: "reboot\r\n" ---- -ESP-ROM:esp32c3-api1-20210207 -Build:Feb 7 2021 -rst:0xc (RTC_SW_CPU_RST),boot:0xd (SPI_FAST_FLASH_BOOT) -Saved PC:0x40048b92 -SPIWP:0xee -mode:DIO, clock div:1 -load:0x3fcd5820,len:0x1144 -load:0x403cc710,len:0xad8 -load:0x403ce710,len:0x2f80 -entry 0x403cc710 -BOOT -256222 -ADPD Found, chip version: 2 -Metadata: lon:1.000000 lat:1.000000 alt:1.000000 time:0 acc:1.000000 vacc:1.000000 info1:New_Ambit x:0.000000 y:0.000000 z:0.000000 -Calibration: Name:AmbitV004 Actinic:0.100000 Spec:1.000000 Emit:1.000000 Sun:1.000000 Temp_offset:0.000000 Temp_slope:1.000000 -Calibration: Act_50:5 Act_100:4 Act_150:3 Act_200:2 Act_250:1 -Calibration: ADPD: 0 0 0 0 0 0 -MLX: 5829384 78346632 0 6400 4896664 5829384 61146712 -52489784 -36286480 16384 0 10752 10752 67316800 -FW: MAC:18554fa89110 Size:539168 Date:Mar 5 2026 -FW: 0.0.4 \ No newline at end of file diff --git a/self_test.cpp b/self_test.cpp deleted file mode 100644 index c525217..0000000 --- a/self_test.cpp +++ /dev/null @@ -1,309 +0,0 @@ -#include -#include "PAM.h" -#include "src/devices_init.h" -#include "src/adpd/u_adpd6100.h" -#include "src/as7341/spec_meas.h" -#include "src/mlx90632/u_mlx.h" -#include "driver/temperature_sensor.h" - -extern ADPD6 adpd; -static char STR_SUC[] = "Success"; -static char STR_FAIL[] = "Failed"; - - -char* str_results[3] = {STR_FAIL, STR_SUC, STR_FAIL}; - - -static int check_adpd(){ - Serial.print("Checking ADPD\t\t"); - uint8_t ret = (uint8_t)adpd.begin(); - return ret; -} - -static int check_spec(){ - Serial.print("Checking AS7341\t\t"); - uint8_t ret = (uint8_t)check_AS7341(); - uint16_t spec[12] = {0}; - AS_all_channel(99, 199, spec); - - - - - if (ret < 2) Serial.printf("%s", str_results[ret]); - if (ret != 1){ - Serial.println(""); - return ret; - } - Serial.printf("\t%d,%d,%d,%d,%d,%d,%d,%d\n", spec[0], spec[1], spec[2], spec[3], spec[4], spec[5], spec[6], spec[7]); - - - return ret; -} - - -static int check_mlx(){ - Serial.print("Checking MLX90632\t"); - uint8_t ret = (uint8_t)mlx_init(); - double obj, board; - unsigned int start = millis(); - mlx_measure(&obj, &board); - unsigned int time_used = millis() - start; - if (ret < 2) Serial.printf("%s\t%d\t%2.2f\t%2.2f\n", str_results[ret], time_used, obj, board); - return ret; -} - - -static int test_optic_path(){ - adpd.STOP(); - conf_slow_FR_1(40, 8, 0, 5, 5, 5, 5, 5, 5); - - adpd.num_ts(3); - uint8_t expected_readout_bytes = 24; - uint8_t expected_readout = 8; - uint32_t ret[expected_readout] = {0}; - uint16_t fifo_c = 0; - uint32_t counter = 0; - uint32_t num_ptx = 64+64+64; - - AS_LED_OFF(); - AS_LED_Current(15); - adpd.run_freq(128); - - uint32_t dark1[8] = {0}; - uint32_t dark2[8] = {0}; - uint32_t lit[8] = {0}; - - adpd.RUN(); - while (counter < num_ptx){ - fifo_c = adpd.fifo_count(); - while (fifo_c >= expected_readout_bytes){ // read all bytes from FIFO - adpd.readfifo(expected_readout, 3, ret); - fifo_c -= expected_readout_bytes; - if (counter == num_ptx) break; - for (int i=0; i<8; i++){ - if (i < 2) ret[i] -= 65000; - else if (i < 6) ret[i] -= 16000; - if (counter < 64) dark1[i] += ret[i]; - else if (counter >= 128) dark2[i] += ret[i]; - else lit[i] += ret[i]; - // Serial.print(ret[i]); - // if (i < 7) Serial.print(","); - } - // Serial.println(""); - counter++; - if (counter == 64) AS_LED_ON(); - if (counter == 128) AS_LED_OFF(); - } - } - - adpd.STOP(); - AS_LED_OFF(); - AS_LED_Current(0); - - for (int i=0; i<8; i++){ - dark1[i] /= 64; - dark2[i] /= 64; - lit[i] /= 64; - - if (i < 6) Serial.printf("%d:[%d,%d,%d]\n",i + 1, (int)dark1[i]-300, (int)lit[i]-300, (int)dark2[i]-300); - else Serial.printf("%d:[%d,%d,%d]\n",i + 1, dark1[i], lit[i], dark2[i]); - - } - - return 0; -} - - - - -static int test_optic_path2(){ - adpd.STOP(); - adpd.led_config.driver1_current = 0; - adpd.led_config.driver2_current = 0; - - for (int i = 0; i < 5; i++){ - adpd.SNR_config.TIA_gain_CH2 = i + 1; // channel 2: leaf IR reflection - adpd.SNR_config.TIA_gain_CH1 = i + 1; // channel 1: sun vis - adpd.preset_config_1(i, 1); - } - - - adpd.led_config.driver1_current = 0; - adpd.led_config.led1_channel = LED_A; - adpd.led_config.led2_channel = LED_A; - adpd.SNR_config.TIA_gain_CH1 = 5; - adpd.SNR_config.TIA_gain_CH2 = 5; - - for (int i = 0; i < 5; i++){ - adpd.led_config.driver2_current = i * 10 + 10; - adpd.preset_config_2(i + 5, 1); - } - - - uint8_t expected_readout_bytes = 2 * 3 * 5 + 4 * 3 * 5; - uint8_t expected_readout = expected_readout_bytes / 3; - uint32_t ret[expected_readout] = {0}; - uint16_t fifo_c = 0; - uint32_t counter = 0; - uint32_t num_ptx = 64; - - - - AS_LED_OFF(); - adpd.run_freq(128); - - uint32_t sun[5] = {0}; - uint32_t leaf[5] = {0}; - uint32_t sig[5] = {0}; - uint32_t ref[5] = {0}; - - - adpd.RUN(); - while (counter < num_ptx){ - fifo_c = adpd.fifo_count(); - while (fifo_c >= expected_readout_bytes){ // read all bytes from FIFO - adpd.readfifo(expected_readout, 3, ret); - fifo_c -= expected_readout_bytes; - if (counter == num_ptx) break; - - for (uint8_t i = 0; i < 5; i++){ - sun[i] += ret[i * 2] - 16300; - leaf[i] += ret[i * 2 + 1] - 16300; - - sig[i] += ret[10 + i * 4 + 1] - ret[10 + i * 4]; - ref[i] += ret[10 + i * 4 + 3] - ret[10 + i * 4 + 2]; - - - - } - - counter++; - } - } - - adpd.STOP(); - AS_LED_OFF(); - - Serial.printf("Sun PD\t\t%d\t%d\t%d\t%d\t%d\n", sun[4]/64,sun[3]/64,sun[2]/64,sun[1]/64,sun[0]/64); - Serial.printf("Leaf PD\t\t%d\t%d\t%d\t%d\t%d\n", leaf[4]/64,leaf[3]/64,leaf[2]/64,leaf[1]/64,leaf[0]/64); - Serial.printf("Signal\t\t%d\t%d\t%d\t%d\t%d\n", sig[0]/64,sig[1]/64,sig[2]/64,sig[3]/64,sig[4]/64); - Serial.printf("Ref\t\t%d\t%d\t%d\t%d\t%d\n", ref[0]/64,ref[1]/64,ref[2]/64,ref[3]/64,ref[4]/64); - - return 0; -} - - - -int check_connections(){ - int ret1 = check_adpd(); - int ret2 = check_spec(); - int ret3 = check_mlx(); - - temperature_sensor_handle_t temp_handle = NULL; - temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(15, 55); - float tsens_out; - - temperature_sensor_install(&temp_sensor_config, &temp_handle); - temperature_sensor_enable(temp_handle); - temperature_sensor_get_celsius(temp_handle, &tsens_out); - printf("ESP32Temp\t\t%2.2f\n", tsens_out); - temperature_sensor_disable(temp_handle); - temperature_sensor_uninstall(temp_handle); - - - Serial.println("ADPD_readings:"); - test_optic_path2(); - test_optic_path(); - Serial.println("Done!!"); - - // if ((ret1 == 1) && (ret2 == 1) && (ret3 == 1)){ - // AS_LED_OFF(); - // AS_LED_Current(4); - // AS_LED_ON(); - // delay(300); - // AS_LED_OFF(); - // } - - - - - - return 0; -} - - -int optic_test(){ - adpd.STOP(); - conf_slow_FR_1(100, 20, 0, 1, 5, 5, 5, 5, 1); - - adpd.num_ts(3); - uint8_t expected_readout_bytes = 24; - uint8_t expected_readout = 8; - uint32_t ret[expected_readout] = {0}; - uint16_t fifo_c = 0; - uint32_t counter = 0; - uint32_t num_ptx = 2000; - uint32_t sig = 0; - - AS_LED_OFF(); - AS_LED_Current(50); - adpd.run_freq(25); - - adpd.RUN(); - while (counter < num_ptx){ - fifo_c = adpd.fifo_count(); - while (fifo_c >= expected_readout_bytes){ // read all bytes from FIFO - adpd.readfifo(expected_readout, 3, ret); - fifo_c -= expected_readout_bytes; - if (counter == num_ptx) break; - sig = calc_signal(ret[2], ret[3], 1); - Serial.printf("%d,%d,%d,%d,%d,%d,%d\n", ret[0]-65000,ret[1]-65000,ret[2]-16000,ret[3]-16000,ret[4]-16000,ret[5]-16000,sig); - counter++; - } - } - - adpd.STOP(); - AS_LED_OFF(); - AS_LED_Current(0); - return 0; -} - - -int optic_test(uint8_t current, uint8_t num_integ, uint8_t lit_offset, uint8_t dark1_offset, uint8_t dark2_offset, uint8_t pulse_offset, uint8_t pulse_duration){ - adpd.STOP(); - fluor_offset_test(current, num_integ, lit_offset, dark1_offset, dark2_offset, pulse_offset, pulse_duration); - - adpd.num_ts(1); - uint8_t expected_readout_bytes = 12; - uint8_t expected_readout = 4; - uint32_t ret[expected_readout] = {0}; - uint16_t fifo_c = 0; - uint32_t counter = 0; - uint32_t num_ptx = 300; - uint32_t sig = 0; - - AS_LED_OFF(); - AS_LED_Current(100); - adpd.run_freq(25); - - adpd.RUN(); - while (counter < num_ptx){ - fifo_c = adpd.fifo_count(); - while (fifo_c >= expected_readout_bytes){ // read all bytes from FIFO - adpd.readfifo(expected_readout, 3, ret); - fifo_c -= expected_readout_bytes; - if (counter == num_ptx) break; - if (counter == 100) AS_LED_ON(); - if (counter == 200) AS_LED_OFF(); - sig = calc_signal(ret[0], ret[1], num_integ); - Serial.printf("%d,%d,%d,%d,%d\n", ret[0]-16000*num_integ, ret[1]-16000*num_integ,ret[2]-16000*num_integ,ret[3]-16000*num_integ, ret[1] - ret[0]); - counter++; - } - } - - adpd.STOP(); - AS_LED_OFF(); - AS_LED_Current(0); - return 0; -} - diff --git a/sketch.yaml b/sketch.yaml deleted file mode 100644 index 770994b..0000000 --- a/sketch.yaml +++ /dev/null @@ -1,17 +0,0 @@ -profiles: - ambit: - fqbn: esp32:esp32:esp32c3:UploadSpeed=921600,CDCOnBoot=default,CPUFreq=160,FlashFreq=80,FlashMode=qio,FlashSize=4M,PartitionScheme=minimal,DebugLevel=none,EraseFlash=all,JTAGAdapter=default,ZigbeeMode=default - platforms: - - platform: esp32:esp32 (3.1.0) - platform_index_url: https://espressif.github.io/arduino-esp32/package_esp32_index.json - libraries: - - Adafruit BusIO (1.17.4) - programmer: esptool - port: COM25 - port_config: - baudrate: "115200" - lineEnding: "\r\n" - bits: "8" - parity: none - stop_bits: "1" -default_profile: ambit diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..9fe751a --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,2 @@ +.vscode/c_cpp_properties.json +.upload.bat \ No newline at end of file diff --git a/PAM.cpp b/src/PAM.cpp similarity index 61% rename from PAM.cpp rename to src/PAM.cpp index d5b7bb9..62fa97b 100644 --- a/PAM.cpp +++ b/src/PAM.cpp @@ -1,1939 +1,1597 @@ -#include "PAM.h" - -static const char* TAG = "PAM"; -static bool measure_temp = true; -static bool PAM_interrupt(bool, bool); - -uint8_t adpd_mode = 0; -adpd_current_config_t adpd_current_config; -adpd_gains_config_t adpd_gains_config; - - -int serial_read_until(uint8_t target1, uint8_t target2 = 0, uint8_t target3 = 0, uint16_t timeout = 20, bool remove = false); - -// use pre-set values -int conf_slow_FR_1(void){ - - if (adpd_gains_config.init == false) ESP_LOGE(TAG, "Gain preset not initized, use default!"); - if (adpd_current_config.init == false) ESP_LOGE(TAG, "Current preset not initized, use default!"); - return conf_slow_FR_1(adpd_current_config.I620, adpd_current_config.I720, adpd_current_config.IR, adpd_gains_config.Fluo, adpd_gains_config.FluoRef, adpd_gains_config.Sun, adpd_gains_config.Leaf, adpd_gains_config.IR, adpd_gains_config.IRRef); -} - - -// Slow measurements with 4 time slots -// @param I620 measuring light current (0 - 127) -// @param I730 IR reflection current (0 - 127) -// @param I_FR Far-red treatment current (0 - 127) -// @param G_Fluor fluorescence signal gain (0 - 5) -// @param G_FluorRef fluorescence rerefence gain (0 - 5) -// @param G_Sun Sun facing PD gain (0 - 5) -// @param G_IR Leaf facing PD gain (0 - 5) -// @param G_FR IR reflection signal gain (0 - 5) -// @param G_FRref IR reflection reference gain (0 - 5) -int conf_slow_FR_1(uint8_t I620, uint8_t I730, uint8_t I_FR, uint8_t G_Fluor, uint8_t G_FluorRef, uint8_t G_Sun, uint8_t G_IR, uint8_t G_FR, uint8_t G_FRref){ - - // Setup timeslot 1: two ambient light channels, 2 x 3 bytes - adpd.led_config.driver1_current = 0; - adpd.led_config.driver2_current = 0; - adpd.SNR_config.TIA_gain_CH2 = G_IR; // channel 2: leaf IR reflection - adpd.SNR_config.TIA_gain_CH1 = G_Sun; // channel 1: sun vis - adpd.preset_config_1(0, 4); - - - - // Setup timeslot 2: Fluor and Ref channels, 4 x 3 bytes - // LED 1A = 620nm - adpd.led_config.driver1_current = I620; - adpd.led_config.led1_channel = LED_A; - // LED 2A = 730nm - adpd.led_config.driver2_current = 0; - adpd.led_config.led2_channel = LED_A; - adpd.SNR_config.TIA_gain_CH1 = G_Fluor; - adpd.SNR_config.TIA_gain_CH2 = G_FluorRef; - adpd.preset_config_2(1, 1); - - - // Setup timeslot 3: IR leave reflection, 2 x 3 bytes - // LED 1A = 620nm - adpd.led_config.driver1_current = 0; - adpd.led_config.led1_channel = LED_A; - // LED 2A = 730nm - adpd.led_config.driver2_current = I730; - adpd.led_config.led2_channel = LED_A; - adpd.SNR_config.TIA_gain_CH1 = G_FR; - adpd.SNR_config.TIA_gain_CH2 = G_FRref; - - adpd.preset_config_3(2, 4); - - // Setup timeslot 4-5-6: Far-red illumination, 0 data - // LED 1A = 620nm - adpd.led_config.driver1_current = 0; - adpd.led_config.led1_channel = LED_A; - // LED 2A = 730nm - adpd.led_config.driver2_current = I_FR; - adpd.led_config.led2_channel = LED_A; - - //make 6 time slots, ~ 2 ms without repeats, can get 200 repeats ~ 400ms - adpd.preset_config_4(3); - adpd.preset_config_4(4); - adpd.preset_config_4(5); - adpd.preset_config_4(6); - adpd.preset_config_4(7); - adpd.preset_config_4(8); - - adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; - - return 0; -} - - -uint32_t arr_line_parse_type1(uint8_t* line, uint8_t* num1, uint16_t* num2, uint16_t* num3, uint8_t* num4, uint8_t* num5, uint16_t* data_count){ - // type 1: Must have measurements: Fluor/Ref - // optional measurements: leaf IR, sun VIs, Reflect/Ref - // 0 = no points, 1 = same freq, 2 = every 8 - // data_count 0: fluor, 1: ambient, 2: reflection - uint8_t type = line[0]; // run type 1 = steady state, 0 = skip, 2 = no ir - *num1 = line[1]; // FR on / off - *num2 = line[3] + (line[2] << 8); // sample number - *num3 = line[5] + (line[4] << 8); // frequency - *num4 = line[6]; // actinic - *num5 = line[7]; // sub-sampling factor - - if (data_count == NULL) return 0; - - if ((type == 1) || (type == 2)){ - data_count[0] = *num2; - if (line[7] == 0){ // no ambient - data_count[1] = 0; - data_count[2] = 0; - }else if (line[7] == 1){ // every - data_count[1] = *num2; - data_count[2] = *num2; - }else if (line[7] == 2){ // every 8 - data_count[1] = (*num2) / 8; - data_count[2] = (*num2) / 8; - }else{ - data_count[1] = 0; - data_count[2] = 0; - } - if (type == 2) data_count[2] = 0; - - return data_count[0] * 2 + data_count[1] * 2 + data_count[2] * 2; - - } - return 0; -} - - - -int run_preprocess_type1(uint8_t length, uint8_t* arr, uint16_t* data_counter){ - uint8_t pc = 0; - uint8_t _type = 0; - uint8_t para1, actinic, subsampling = 0; - uint16_t num_ptx, freq = 0; - uint16_t _data_counter[4] = {0}; - - while (pc < length){ - _type = *(arr + pc * 8); - if ((_type == 1) || (_type == 2)){ - arr_line_parse_type1(arr + pc * 8, ¶1, &num_ptx, &freq, &actinic, &subsampling, _data_counter); - ESP_LOGV(TAG, "Run type:%d, number:%d, freq: %d, actinic: %d, subfactor %d", _type, num_ptx, freq, actinic, subsampling); - for (uint8_t i = 0; i < 4; i++) data_counter[i] += _data_counter[i]; - } - pc += 1; - } - return 0; -} - -int run_arr_type1(uint8_t length, uint8_t* arr, bool led_persist){ - return run_arr_type1(length, arr, led_persist, false); -} - - -int run_arr_type1(uint8_t length, uint8_t* arr, bool led_persist, bool allow_interrupt){ - - if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1){ - conf_slow_FR_1(); - ESP_LOGW(TAG, "Run array was not configured!"); - } - // set to max possible bytes - uint8_t expected_readout = 8; - uint8_t expected_readout_bytes = expected_readout * 3; - const uint8_t num_integration = 1; - const unsigned int start_t0 = millis(); - - // Run protocol preprecess, get storage size - uint16_t data_count[] = {0, 0, 0, 0}; - if (run_preprocess_type1(length, arr, data_count) == -1) return -1; // calculate data counts - ESP_LOGV(TAG, "Sample & Ref: %d, optionals: %d", data_count[0], data_count[1]); - - dataclass *d_env = new dataclass; // - dataclass *d_fluor = new dataclass; // fluorescence signal - dataclass *d_fluoRef = new dataclass; // fluorescence reference - - dataclass *d_sun = new dataclass; // sun-side ambient - dataclass *d_leaf = new dataclass; // leaf-side ambient - dataclass *d_730 = new dataclass; // 730nm reflectance signal - dataclass *d_730Ref = new dataclass; // 730nm reference - - - if (!(d_env->init(512))) return -1; - if (!( (d_fluor->init(data_count[0])) && (d_fluoRef->init(data_count[0])) )) return -1; - if (data_count[1] > 0){ - if (!( (d_sun->init(data_count[1])) && (d_leaf->init(data_count[1])) )) return -1;} - if (data_count[2] > 0){ - if (!( (d_730->init(data_count[2])) && (d_730Ref->init(data_count[2])) )) return -1; - } - - - ESP_LOGV(TAG, "Memory allocation completed"); - - - // variables for each trace - uint8_t pc = 0; - uint8_t _type = 0; - uint8_t farred, actinic, subsampling = 0; - uint16_t num_ptx, freq = 0; - - // data counter and buffer - // [sun-amb, leaf-ir, lit_leaf-ir, dark_leaf-ir, lit_leaf-ref, dark_leaf-ref] - uint32_t ret[expected_readout] = {0}; - uint32_t counter, ploter1, ploter2 = 0; - uint16_t fifo_c = 0; - uint8_t watch_dog_timer = 0; - int32_t tmp_var = 0; - uint32_t buf_opt[4] = {0}; - uint32_t _tmparr = 0; - uint8_t _repeats = 1; - uint32_t light_sleep_time = 1; - float_t leaf_temp = 0.0; - unsigned int env_timer1 = millis(); - bool measure_temperature = false; - bool interrupt_run = false; - - _tmparr = PAM_get_env(4, start_t0); - d_env->put(_tmparr); - leaf_temp = ((int16_t) (_tmparr & 0xFFF)) / 20.0 - 20; - - - adpd.STOP(); - while (pc < length){ - if (interrupt_run) break; - _type = *(arr + pc * 8); //get line type - if ((_type == 1) || (_type == 2)){ // all channels - arr_line_parse_type1((arr + pc * 8), &farred, &num_ptx, &freq, &actinic, &subsampling, NULL); - adpd.run_freq(freq); - adpd.clear_fifo(); - light_sleep_time = (1000/freq); - measure_temperature = (light_sleep_time > 20) && measure_temp && actinic < 50; - - - if (_type == 1){ // use IR reflect - if (farred == 1){ // whether use actinic IR - adpd.num_ts(9); - _repeats = int(400/freq); - if (freq < 3) _repeats = 250; - if (_repeats == 0) _repeats = 1; - for (uint8_t i = 3; i < 9;i++) adpd.repeats_only(i, 1, _repeats); - } - else{ - adpd.num_ts(3); - } - expected_readout = 8; - expected_readout_bytes = expected_readout * 3; - }else{ // NO IR - adpd.num_ts(2); - expected_readout = 6; - expected_readout_bytes = expected_readout * 3; - } - - if (actinic > 3){ - AS_LED_Current(actinic); - AS_LED_ON(); - }else{ - AS_LED_OFF(); - AS_LED_Current(0); - } - - - counter = 0; - for (uint8_t i = 0; i < 4; i++) buf_opt[i] = 0; - d_env->put(PAM_get_env(0, start_t0)); - adpd.RUN(); - delay(2); - while (counter < num_ptx){ - if (interrupt_run) break; - fifo_c = adpd.fifo_count(); - while (fifo_c >= expected_readout_bytes){ // read all bytes from FIFO - adpd.readfifo(expected_readout, 3, ret); - fifo_c -= expected_readout_bytes; - if (counter == num_ptx) break; - // 0: sun-vis; 1: leaf-ir; 2: fluoS_dark; 3: fluoS_lit; 4: fluoR_dark; 5: fluoR_lit; 6: Reflect_signal; 7: reflect_ref - // save fluor signal and ref - d_fluor->put(calc_signal(ret[2], ret[3], num_integration)); - d_fluoRef->put(calc_signal(ret[4], ret[5], num_integration)); - - - // save option data - if (subsampling > 0){ - if (subsampling == 1){ // every point - d_sun->put(ret[0]); - d_leaf->put(ret[1]); - if (_type == 1){d_730->put(ret[6]);d_730Ref->put(ret[7]);} // ir enabled - - }else if (subsampling == 2){ - buf_opt[0] += ret[0]; - buf_opt[1] += ret[1]; - if (_type == 1){ - buf_opt[2] += ret[6]; - buf_opt[3] += ret[7]; - } - - if (counter % 8 == 7){ - d_sun->put(buf_opt[0]/8); - d_leaf->put(buf_opt[1]/8); - if(_type == 1){d_730->put(buf_opt[2]/8);d_730Ref->put(buf_opt[3]/8);} - for (uint8_t i = 0; i < 4; i++) buf_opt[i] = 0; - } - } - } - - if (CONNECTION_TYPE == CONNECTION_TYPES::PLOTTING){ - ploter1 = calc_signal(ret[2], ret[3], num_integration); - ploter2 = calc_signal(ret[4], ret[5], num_integration); - if (ploter2 == 0){ - ploter2 = 1; - ploter1 = 0; - } - if (_type == 1) { - Serial.printf("T:%2.3f,F:%3.4f,S:%d,R:%d,7:%d,7R:%d,Sun:%d,L:%d\n", leaf_temp, (float)ploter1/(float)ploter2, ploter1, ploter2, ret[6], ret[7], ret[0]-65000, ret[1]-65000); - }else if (_type == 2){ - Serial.printf("T:%2.3f,F:%3.4f,S:%d,R:%d,Sun:%d,L:%d\n", leaf_temp, (float)ploter1/(float)ploter2, ploter1, ploter2, ret[0]-65000, ret[1]-65000); } - Serial.flush(); - } - counter++; - watch_dog_timer = 0; - } - - // do light sleep - //esp_sleep_enable_timer_wakeup(1000); - if (counter + 10 < num_ptx){ // a lot of measurements - // do temperature measurement? - if (measure_temperature && (millis() - env_timer1 > 2000)){ - _tmparr = PAM_get_env(4, start_t0); - d_env->put(_tmparr); - leaf_temp = ((int16_t) (_tmparr & 0xFFF)) / 20.0 - 20; - env_timer1 = millis(); - esp_sleep_enable_timer_wakeup(1000); - } - else esp_sleep_enable_timer_wakeup(light_sleep_time * 8000); - - }else if (counter + 2 < num_ptx){ // not many - esp_sleep_enable_timer_wakeup(light_sleep_time * 1000); - }else{ - esp_sleep_enable_timer_wakeup(1000); - } - // run interrupted by serial input "S" - interrupt_run = PAM_interrupt(allow_interrupt, false); - if (!interrupt_run) esp_light_sleep_start(); - interrupt_run = PAM_interrupt(allow_interrupt, true); - } - - adpd.STOP(); - d_env->put(PAM_get_env(1, start_t0)); - if (!led_persist) AS_LED_OFF(); - digitalWrite(1, LOW); - } - pc += 1; - } - - if (interrupt_run){ - digitalWrite(STF_FLASH_PIN, LOW); - adpd.STOP(); - AS_LED_OFF(); - }; - - - if (CONNECTION_TYPE == CONNECTION_TYPES::COMPUTER){ - d_env->send_serial("ENV"); - d_fluor->send_serial("Fluo"); - d_fluoRef->send_serial("Fluoref"); - d_sun->send_serial("SUN"); - d_leaf->send_serial("leaf"); - d_730->send_serial("730"); - d_730Ref->send_serial("730ref"); - - Serial.println("Data sent"); - }else if(CONNECTION_TYPE == CONNECTION_TYPES::AMBYTE){ - int ret = d_env->fsm_send_esp(0, allow_interrupt); - if (ret != 1) d_env->fsm_send_esp(0, allow_interrupt); - - d_fluor->fsm_send_esp(1); - d_fluoRef->fsm_send_esp(2); - if (subsampling > 0){ - d_sun->fsm_send_esp(3); - d_leaf->fsm_send_esp(4); - if (data_count[2] > 0){ - d_730->fsm_send_esp(5); - d_730Ref->fsm_send_esp(6); - } - } - } - - delete d_fluor; - delete d_fluoRef; - delete d_sun; - delete d_leaf; - delete d_730; - delete d_730Ref; - delete d_env; - - return 0; - -} - - -void adpd_trigger(void){ - digitalWrite(10, HIGH); - delayMicroseconds(1); - digitalWrite(10, LOW); -} - - - -int run_trigger_spacer(uint16_t length, uint8_t interval, bool change_act, uint8_t act, bool interrrupt){ - - if (length > 3000) return -1; - adpd.STOP(); - - if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1) conf_slow_FR_1(); - - digitalWrite(10, LOW); - adpd.gpio_config.GPIO0_cfg = 1; - adpd.gpio_config.SYNC_GPIO = 0; - adpd.gpio_config.EXT_SYNC_EN = 1; - adpd.gpio_setup(&(adpd.gpio_config)); - adpd_mode = ADPD_CONFIG_MODE::ARRAY_SLOW; - - gpio_sleep_set_direction(GPIO_NUM_10, GPIO_MODE_OUTPUT); - gpio_sleep_set_pull_mode(GPIO_NUM_10, GPIO_PULLDOWN_ONLY); - - - - // set to max possible bytes - uint8_t expected_readout = 8; - uint8_t expected_readout_bytes = expected_readout * 3; - const uint32_t _wait_time_ms = interval * 100; - const uint8_t num_integration = 1; - const unsigned int start_t0 = millis(); - - // data counter and buffer - // [sun-amb, leaf-ir, lit_leaf-ir, dark_leaf-ir, lit_leaf-ref, dark_leaf-ref] - uint32_t ret[expected_readout] = {0}; - uint16_t fifo_c = 0; - uint8_t watch_dog_timer = 0; - int32_t tmp_var = 0; - uint32_t buf_opt[4] = {0}; - uint32_t _tmparr = 0; - uint32_t read_fluor, read_fluoRef, read_sun, read_leaf, read_7, read_7Ref; - float_t leaf_temp = 0.0; - - unsigned int env_timer1 = millis(), trigger_timer = 0, expected_millis = 0; - int waiting_time = 0; - bool measure_temperature = false; - bool interrupt_run = false; - esp_sleep_enable_timer_wakeup(90000); - - dataclass *d_env = new dataclass; // - dataclass *d_fluor = new dataclass; // fluorescence signal - dataclass *d_fluoRef = new dataclass; // fluorescence reference - - dataclass *d_sun = new dataclass; // sun-side ambient - dataclass *d_leaf = new dataclass; // leaf-side ambient - dataclass *d_730 = new dataclass; // 730nm reflectance signal - dataclass *d_730Ref = new dataclass; // 730nm reference - - int _func_ret = -1; - if (!(d_env->init(512))) goto del_classes; - if (!( (d_fluor->init(length)) && (d_fluoRef->init(length)) )) goto del_classes; - if (!( (d_sun->init(length)) && (d_leaf->init(length)) )) goto del_classes; - if (!( (d_730->init(length)) && (d_730Ref->init(length)) )) goto del_classes; - - - _tmparr = PAM_get_env(4, start_t0); - d_env->put(_tmparr); - leaf_temp = ((int16_t) (_tmparr & 0xFFF)) / 20.0 - 20; - env_timer1 = millis(); - - - adpd.clear_fifo(); - adpd.run_freq(10); - adpd.num_ts(3); - adpd.RUN(); - delay(5); - - if(change_act){ - if (act == 0) AS_LED_OFF(); - if (act > 0) { - AS_LED_Current(act); - AS_LED_ON(); - } - } - - for (uint16_t n = 0; n < length; n++){ - fifo_c = 0; - adpd_trigger(); - trigger_timer = millis(); - expected_millis = trigger_timer + _wait_time_ms; - delay(1); - while (fifo_c != expected_readout_bytes){ - fifo_c = adpd.fifo_count(); - if (fifo_c >= expected_readout_bytes) break; - if (millis() - trigger_timer > 100) break; - } - if (fifo_c < expected_readout_bytes){ - ESP_LOGE(TAG, "NOT ENOUGH IN FIFO"); - break; - } - adpd.readfifo(expected_readout, 3, ret); - if (fifo_c > expected_readout_bytes){ - adpd.clear_fifo(); - ESP_LOGE(TAG, "Extra %d byte in FIFO", fifo_c - expected_readout_bytes); - } - - read_fluor = calc_signal(ret[2], ret[3], num_integration); d_fluor->put(read_fluor); - read_fluoRef = calc_signal(ret[4], ret[5], num_integration); d_fluoRef->put(read_fluoRef); - read_sun = ret[0]; d_sun->put(read_sun); - read_leaf = ret[1]; d_leaf->put(read_leaf); - read_7 = ret[6]; d_730->put(read_7); - read_7Ref = ret[7]; d_730Ref->put(read_7Ref); - - if (CONNECTION_TYPE == CONNECTION_TYPES::PLOTTING){ - Serial.printf("T:%2.3f,F:%3.4f,S:%d,R:%d,7:%d,7R:%d,Sun:%d,L:%d\n", leaf_temp, (float)read_fluor/(float)read_fluoRef, read_fluor, read_fluoRef, read_7, read_7Ref, read_sun-65000, read_leaf-65000); - Serial.flush(); - } - - if (interrupt_run) break; - if (millis() > expected_millis) continue; // overdue - waiting_time = expected_millis - millis(); - - if ((n % 4 == 3) && (waiting_time > 500)){ - if (millis() - env_timer1 > 2000){ - if (n % 32 != 31){ // measure leaf temp - _tmparr = PAM_get_env(4, start_t0); - leaf_temp = ((int16_t) (_tmparr & 0x0FFF)) / 20.0 - 20; - d_env->put(_tmparr); - env_timer1 = millis(); - } else { // measure PAR - _tmparr = PAM_get_env(3, start_t0); - d_env->put(_tmparr); - env_timer1 = millis(); - } - - } - } - - waiting_time = expected_millis - millis(); - while (waiting_time > 250){ - esp_sleep_enable_timer_wakeup((waiting_time - 50) * 1000); - interrupt_run = PAM_interrupt(interrrupt, false); - if (!interrupt_run) esp_light_sleep_start(); - interrupt_run = PAM_interrupt(interrrupt, true); - if (interrupt_run) break; - waiting_time = expected_millis - millis(); - } - if (interrupt_run) break; - waiting_time = expected_millis - millis(); - if (waiting_time > 1) delay(waiting_time); - } /// End of Loop - - - adpd.STOP(); - adpd.clear_fifo(); - - if (CONNECTION_TYPE == CONNECTION_TYPES::AMBYTE){ - int ret = d_env->fsm_send_esp(0, interrrupt); - if (ret != 1) d_env->fsm_send_esp(0, interrrupt); - d_fluor->fsm_send_esp(1); - d_fluoRef->fsm_send_esp(2); - d_sun->fsm_send_esp(3); - d_leaf->fsm_send_esp(4); - d_730->fsm_send_esp(5); - d_730Ref->fsm_send_esp(6); - } - - _func_ret = 0; - del_classes: - delete d_fluor; - delete d_fluoRef; - delete d_sun; - delete d_leaf; - delete d_730; - delete d_730Ref; - delete d_env; - - adpd.gpio_config.GPIO0_cfg = 0; - adpd.gpio_config.EXT_SYNC_EN = 0; - adpd.gpio_setup(&(adpd.gpio_config)); - adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; - return _func_ret; -} - - -int external_trigger_run(void){ - - adpd.STOP(); - conf_slow_FR_1(); - - adpd.led_config.driver1_current = adpd_current_config.I620; - adpd.led_config.led1_channel = LED_A; - adpd.led_config.driver2_current = 0; - adpd.led_config.led2_channel = LED_A; - adpd.SNR_config.TIA_gain_CH1 = adpd_gains_config.Fluo; - adpd.SNR_config.TIA_gain_CH2 = adpd_gains_config.FluoRef; - adpd.preset_config_2(1, 4); - - - digitalWrite(10, LOW); - adpd.gpio_config.GPIO0_cfg = 1; - adpd.gpio_config.SYNC_GPIO = 0; - adpd.gpio_config.EXT_SYNC_EN = 1; - adpd.gpio_setup(&(adpd.gpio_config)); - - - // set to max possible bytes - uint8_t expected_readout = 6, num_integration = 4; - uint8_t expected_readout_bytes = expected_readout * 3; - - // data counter and buffer - // [sun-amb, leaf-ir, lit_leaf-ir, dark_leaf-ir, lit_leaf-ref, dark_leaf-ref] - uint32_t ret[expected_readout] = {0}; - uint16_t fifo_c = 0; - uint32_t read_fluor, read_fluoRef, read_sun, read_leaf; - float_t leaf_temp = 0.0; - - adpd.clear_fifo(); - adpd.run_freq(10); - adpd.num_ts(2); - adpd.RUN(); - delay(5); - uint8_t unknown_input_counter = 0; - - unsigned int watchdog_timer = millis(), trigger_timer = 0, start_timer = millis(), temp_timer = millis(); - bool keep_running = true, do_measure = false, change_act = false; - double obj_T, chip_T; - char c, c1, c2; - - Serial.println("Run"); - - while(keep_running){ - - while (Serial.available() == 0){ - if (millis() - watchdog_timer > 30000){ - keep_running = false; - break; - } - delayMicroseconds(200); - } - if (!keep_running) break; - - while (Serial.available() > 0){ - c = Serial.read(); - if (c == 'G'){ - do_measure = true; - watchdog_timer = millis(); - }else if(c == 'E'){ - do_measure = false; - keep_running = false; - }else if(c == 'A'){ - delay(1); - c1 = 0; - if (Serial.available() > 0){ - c1 = Serial.read(); - change_act = true; - } - }else if(c == 'T'){ - temp_timer = millis(); - mlx_measure(&obj_T, &chip_T); - Serial.printf("T:%d,o:%.3f,a:%.3f,d:%d\n", millis() - start_timer, obj_T, chip_T, millis() - temp_timer); - Serial.flush(); - watchdog_timer = millis(); - continue; - } - else{ - unknown_input_counter += 1; - if (unknown_input_counter > 200) keep_running = false; - } - } - - if (!keep_running) break; - if (change_act){ - change_act = false; - if (c1 > 3){ - AS_LED_Current(c1); - AS_LED_ON(); - }else{ - AS_LED_OFF(); - } - } - if (!do_measure) continue; - - - fifo_c = 0; - adpd_trigger(); - do_measure = false; - trigger_timer = millis(); - delay(1); - while (fifo_c != expected_readout_bytes){ - fifo_c = adpd.fifo_count(); - if (fifo_c >= expected_readout_bytes) break; - if (millis() - trigger_timer > 100) break; - } - if (fifo_c < expected_readout_bytes){ - Serial.println("[E] NOT ENOUGH IN FIFO"); - break; - } - adpd.readfifo(expected_readout, 3, ret); - if (fifo_c > expected_readout_bytes){ - adpd.clear_fifo(); - Serial.printf("[E] Extra %d byte in FIFO\n", fifo_c - expected_readout_bytes); - } - - read_fluor = calc_signal(ret[2], ret[3], num_integration); - read_fluoRef = calc_signal(ret[4], ret[5], num_integration); - read_sun = ret[0]; - read_leaf = ret[1]; - - Serial.print("T:"); - Serial.print(millis() - start_timer); - Serial.print(",S:"); - Serial.print(read_fluor); - Serial.print(",R:"); - Serial.print(read_fluoRef); - Serial.print(",F:"); - Serial.print(read_sun - 65000); - Serial.print(",B:"); - Serial.println(read_leaf - 65000); - - - - // Serial.printf("T:%d,S:%d,R:%d,F:%d,B:%d\n", millis() - start_timer, read_fluor, read_fluoRef, read_sun-65000, read_leaf-65000); - Serial.flush(); - } /// End of Loop - - - adpd.STOP(); - adpd.clear_fifo(); - - adpd.gpio_config.GPIO0_cfg = 0; - adpd.gpio_config.EXT_SYNC_EN = 0; - adpd.gpio_setup(&(adpd.gpio_config)); - - AS_LED_Current(0); - AS_LED_OFF(); - - Serial.println("Stop"); - - return 0; -} - - -int external_trigger_run_Flash(unsigned int gate_time, unsigned int dt, const uint16_t num){ - adpd.STOP(); - const uint8_t _NUM_TS = 8; - - adpd.led_config.driver1_current = 80; - adpd.led_config.led1_channel = LED_A; - // LED 2A = 730nm - adpd.led_config.driver2_current = 0; - adpd.led_config.led2_channel = LED_A; - adpd.SNR_config.TIA_gain_CH1 = 1; - adpd.SNR_config.TIA_gain_CH2 = 5; - - for (uint8_t i = 0; i < 12; i++){ - adpd.preset_config_ext_fast(i, 2); - } - - digitalWrite(STF_FLASH_PIN, LOW); - digitalWrite(10, LOW); - adpd.gpio_config.GPIO0_cfg = 1; - adpd.gpio_config.SYNC_GPIO = 0; - adpd.gpio_config.EXT_SYNC_EN = 1; - adpd.gpio_setup(&(adpd.gpio_config)); - AS_LED_OFF(); - AS_LED_Current(dt); - - - // set to max possible bytes - uint8_t expected_readout = _NUM_TS * 4, num_integration = 2; - uint8_t expected_readout_bytes = expected_readout * 3; - - // data counter and buffer - - uint32_t ret[expected_readout] = {0}; - uint16_t fifo_c = 0; - uint32_t read_fluor, read_fluoRef, read_sun, read_leaf; - float_t leaf_temp = 0.0; - - adpd.clear_fifo(); - adpd.run_freq(10); - adpd.num_ts(_NUM_TS); - adpd.RUN(); - delay(5); - uint16_t _counter = 0; - - unsigned int watchdog_timer = millis(), trigger_timer = 0, start_timer = millis(), temp_timer = millis(); - bool keep_running = true, do_measure = false, change_act = false; - double obj_T, chip_T; - char c, c1, c2; - - dataclass *d_fluor = new dataclass; // fluorescence signal - dataclass *d_fluoRef = new dataclass; // fluorescence reference - - - - Serial.println("Run"); - unsigned long timer1 = micros(); - unsigned long timer2 = micros(); - unsigned int flash_duration = expected_readout; - uint16_t _num_sample = num < 20 ? num * _NUM_TS : 20 * _NUM_TS + (num - 20); - - - - if (!( (d_fluor->init(_num_sample + 1)) && (d_fluoRef->init(_num_sample + 1)) )) goto del_classes; - while(keep_running){ - _counter += 1; - if (_counter > num) break; - if (!keep_running) break; - fifo_c = 0; - //if ((_counter == 5) && (dt > 0)) AS_LED_ON(); - adpd_trigger(); - timer1 = micros(); - delayMicroseconds(1000); - fifo_c = adpd.fifo_count(); - while (fifo_c != expected_readout_bytes){ - fifo_c = adpd.fifo_count(); - if (fifo_c >= expected_readout_bytes) break; - if (micros() - timer1 > 10000) break; - } - //Serial.printf("%ld: %d\n", micros() - timer1, fifo_c); - - if (fifo_c < expected_readout_bytes){ - Serial.printf("NOT ENOUGH IN FIFO: %d < %d\n", fifo_c, expected_readout_bytes); - break; - } - - - if ((gate_time > 0) && (flash_duration > 0) && (_counter > 2)) digitalWrite(STF_FLASH_PIN, HIGH); - //if ((_counter == 15) && (dt > 0)) AS_LED_OFF(); - - timer1 = micros(); - for (uint8_t r = 0; r < expected_readout; r++){ - adpd.readfifo(1, 3, ret + r); - if (flash_duration == r) digitalWrite(STF_FLASH_PIN, LOW); - } - //Serial.println(micros() - timer1); - - - if (_counter <= 20){ - for (uint8_t r = 0; r < _NUM_TS; r++){ - d_fluor->put(calc_signal(ret[0 + r * 4], ret[1 + r * 4], num_integration)); - d_fluoRef->put(calc_signal(ret[2 + r * 4], ret[3 + r * 4], num_integration)); - } - }else{ - read_fluor = 0; read_fluoRef = 0; - for (uint8_t r = 0; r < _NUM_TS; r++){ - read_fluor += calc_signal(ret[0 + r * 4], ret[1 + r * 4], num_integration); - read_fluoRef += calc_signal(ret[2 + r * 4], ret[3 + r * 4], num_integration); - } - d_fluor->put(read_fluor / _NUM_TS); - d_fluoRef->put(read_fluoRef / _NUM_TS); - if (_counter == 100) flash_duration = 4; - if (_counter == 300) flash_duration = 2; - if (_counter == 500) flash_duration = 1; - if (_counter == 700) flash_duration = 32; - } - - - if (fifo_c > expected_readout_bytes){ - fifo_c = adpd.fifo_count(); - if (fifo_c > 0){ - adpd.clear_fifo(); - digitalWrite(STF_FLASH_PIN, LOW); - Serial.printf("Extra %d byte in FIFO\n", fifo_c - expected_readout_bytes); - } - } - - // flash_duration = gate_time - (micros() - timer1); - // if ((flash_duration > 1) && (flash_duration < 500)) delayMicroseconds(flash_duration - 1); - // digitalWrite(STF_FLASH_PIN, LOW); - // - - - //Serial.printf("T:%d,S:%d,R:%d,F:%d,B:%d\n", millis() - start_timer, read_fluor, read_fluoRef, read_sun-65000, read_leaf-65000); - // Serial.printf("T:%d,S:%d,R:%d\n", millis() - start_timer, read_fluor, read_fluoRef); - // flash_duration = gate_time - (micros() - timer1); - // if (flash_duration > 2){ - // digitalWrite(STF_FLASH_PIN, HIGH); - // delayMicroseconds(flash_duration - 1); - // } - - - - digitalWrite(STF_FLASH_PIN, LOW); - } /// End of Loop - //Serial.printf("%f for %dpoints\n", float(micros() - timer2)/num, num); - AS_LED_OFF(); - digitalWrite(STF_FLASH_PIN, LOW); - adpd.STOP(); - adpd.clear_fifo(); - - - - adpd.gpio_config.GPIO0_cfg = 0; - adpd.gpio_config.EXT_SYNC_EN = 0; - adpd.gpio_setup(&(adpd.gpio_config)); - - AS_LED_Current(0); - AS_LED_OFF(); - digitalWrite(STF_FLASH_PIN, LOW); - - for (uint16_t n = 2; n < _num_sample; n++){ - Serial.printf("%d,%d,%.4f\n", d_fluor->arr[n], d_fluoRef->arr[n], ((float) d_fluor->arr[n]) / d_fluoRef->arr[n]); - } - - del_classes: - delete d_fluor; - delete d_fluoRef; - - - return 0; -} - - - - -int MPF(uint16_t mode, uint16_t dc_current){ - if (adpd_gains_config.init == false) ESP_LOGE(TAG, "Gain preset not initized, use default!"); - if (adpd_current_config.init == false) ESP_LOGE(TAG, "Current preset not initized, use default!"); - return MPF(mode, adpd_current_config.I620, dc_current, adpd_gains_config.Fluo, adpd_gains_config.FluoRef); - -} - - -int MPF(uint16_t mode, uint16_t current, uint16_t dc_current, uint8_t sign_gain, uint8_t ref_gain){ - - - const uint8_t num_integration = 1; - const uint16_t _data_size = 1080; - - ESP_LOGV(TAG, "RUN MPF with mode:%d, pulse current:%d, DC current %d", mode, current, dc_current); - - dataclass *d_fluor = new dataclass; // fluorescence signal - dataclass *d_fluoRef = new dataclass; // fluorescence reference - if (!( (d_fluor->init(_data_size)) && (d_fluoRef->init(_data_size)) )) return -1; - ESP_LOGV(TAG, "Memory allocation completed"); - - - adpd.STOP(); - adpd.run_freq(10); - adpd.clear_fifo(); - adpd.gpio_config.GPIO0_cfg = 1; - adpd.gpio_config.SYNC_GPIO = 0; - adpd.gpio_config.EXT_SYNC_EN = 1; - adpd.gpio_setup(&(adpd.gpio_config)); - - adpd.led_config.driver1_current = current; - adpd.led_config.driver2_current = 0; - adpd.SNR_config.TIA_gain_CH2 = ref_gain; - adpd.SNR_config.TIA_gain_CH1 = sign_gain; - - adpd_mode = ADPD_CONFIG_MODE::MPF_MODE; - - - // data buffers - uint32_t ret[48] = {0}; - uint16_t fifo_c = 0; - int32_t tmp_var = 0; - uint32_t avg_arr1[12] = {0}; - uint32_t avg_buf[4] = {0}; - uint16_t as_current = 255; - uint8_t expected_readout = 12 * 4; // 12 timeslots, 4 data per ts - uint16_t decay_interval = 1; - // PHASE 0---------------------------------------------- - // Apply a baseline without actinic - // fixed at 2 Hz x 20 pts - if (mode == 0){ - AS_LED_OFF(); - AS_LED_Current(as_current); - adpd.preset_config_ext_fast(0); - expected_readout = 4; - adpd.RUN(); - delayMicroseconds(1500); - adpd_trigger(); - delayMicroseconds(1500); - ESP_LOGV(TAG, "Phase-0 Started"); - for (uint8_t i = 0; i < 20; i++){ - adpd_trigger(); - adpd.readfifo(expected_readout, 3, ret); - for (uint8_t m = 0; m < 4; m++){ // sign-dark, sign-lit, ref-dark, ref lit - avg_buf[m] = ret[m]; - } - d_fluor->put(calc_signal(avg_buf[0], avg_buf[1], num_integration)); - d_fluoRef->put(calc_signal(avg_buf[2], avg_buf[3], num_integration)); - delay(500); - } - ESP_LOGV(TAG, "Phase-0 Completed"); - } - - - // PHASE 1--------------------------------------------------- - // 12 timeslots - // Rapid induction, all timeslot saved - ESP_LOGV(TAG, "Phase-1 Config"); - expected_readout = 48; - for (uint8_t i = 0; i < 12; i++){ - adpd.preset_config_ext_fast(i); - } - adpd.RUN(); - // AS Light kept until this point - AS_LED_OFF(); - AS_LED_Current(0); - delay(2); - ESP_LOGV(TAG, "Phase-1 LED ON"); - // Light ON - AS_LED_ON(); - delayMicroseconds(1); - adpd_trigger(); - delayMicroseconds(1500); - - for (uint8_t i = 0; i < 4; i++){ - adpd_trigger(); - adpd.readfifo(expected_readout, 3, ret); - for (uint8_t j = 0; j < 12; j++){ - - d_fluor->put(calc_signal(ret[0 + j * 4], ret[1 + j * 4], num_integration)); - d_fluoRef->put(calc_signal(ret[2 + j * 4], ret[3 + j * 4], num_integration)); - } - delayMicroseconds(500); - } - ESP_LOGV(TAG, "Phase-1 Completed; Phase-2 start"); - - // PHASE 2------------------------------------ - // 300ms induction, 200x12ts, 200 final pts - for (uint8_t i = 0; i < 200; i++){ - adpd_trigger(); - adpd.readfifo(expected_readout, 3, ret); - // reset avg arrays - for (uint8_t m = 0; m < 4; m++){ // find median for sign-dark, sign-lit, ref-dark, ref lit - memset(avg_arr1, 0, sizeof(avg_arr1)); - for (uint8_t n = 0; n < 12; n++){ // put 12 ts into sorting array - sorted_insert(avg_arr1, 12, ret[m + n * 4]); - } - avg_buf[m] = (avg_arr1[5] + avg_arr1[6] + avg_arr1[7]) / 3; // pick the middle 3 numbers and average - } - - - // Serial.printf("P2, %d, %d, %d\n", avg_buf[0], avg_buf[1], calc_signal(avg_buf[0], avg_buf[1], num_integration)); - // Serial.printf("P2, %d, %d, %d\n", avg_buf[2], avg_buf[3], calc_signal(avg_buf[2], avg_buf[3], num_integration)); - - - d_fluor->put(calc_signal(avg_buf[0], avg_buf[1], num_integration)); - // Serial.println(d_fluor->arr[d_fluor->write_ptr - 1]); - d_fluoRef->put(calc_signal(avg_buf[2], avg_buf[3], num_integration)); - // Serial.println(d_fluoRef->arr[d_fluoRef->write_ptr - 1]); - delayMicroseconds(500); - } - - ESP_LOGV(TAG, "Phase-2 Completed; Phase-3 Start"); - - - // PHASE 3------------------------------------ - // 150ms induction X cycles, 100x12ts, 50 final pts - for (uint8_t j = 0; j < 8; j++){ - AS_LED_Current(220 - j * 30); - ESP_LOGV(TAG, "LED set to %d", 220 - j * 30); - memset(avg_buf, 0, sizeof(avg_buf)); - for (uint8_t i = 0; i < 40; i++){ - adpd_trigger(); - adpd.readfifo(expected_readout, 3, ret); - // reset avg arrays - for (uint8_t m = 0; m < 4; m++){ // find median for sign-dark, sign-lit, ref-dark, ref lit - memset(avg_arr1, 0, sizeof(avg_arr1)); - for (uint8_t n = 0; n < 12; n++){ // put 12 ts into sorting array - sorted_insert(avg_arr1, 12, ret[m + n * 4]); - } - avg_buf[m] += (avg_arr1[5] + avg_arr1[6] + avg_arr1[7]) / 3; // pick the middle 3 numbers and average - } - - if (i % 2 == 1){ - d_fluor->put(calc_signal(avg_buf[0] / 2, avg_buf[1] / 2, num_integration)); - d_fluoRef->put(calc_signal(avg_buf[2] / 2, avg_buf[3] / 2, num_integration)); - memset(avg_buf, 0, sizeof(avg_buf)); - } - delayMicroseconds(500); - } - } - - ESP_LOGV(TAG, "Phase-3 Completed; Phase-4 Start"); - // PHASE 4------------------------------------ - AS_LED_Current(as_current); - // prepare for single timeslot decay kinetic - expected_readout = 4; - adpd.preset_config_ext_fast(0); - adpd.clear_fifo(); - adpd.RUN(); - delayMicroseconds(1000); - adpd_trigger(); - delayMicroseconds(1500); - memset(avg_buf, 0, sizeof(avg_buf)); - - for (uint8_t i = 0; i < 200; i++){ - adpd_trigger(); - adpd.readfifo(expected_readout, 3, ret); - for (uint8_t m = 0; m < 4; m++){ // sign-dark, sign-lit, ref-dark, ref lit - avg_buf[m] += ret[m]; - } - if (i % 4 == 3){ - - - - // Serial.printf("P4, %d, %d, %d\n", avg_buf[0]/4, avg_buf[1]/4, calc_signal(avg_buf[0]/4, avg_buf[1]/4, num_integration)); - // Serial.printf("P4, %d, %d, %d\n", avg_buf[2]/4, avg_buf[3]/4, calc_signal(avg_buf[2]/4, avg_buf[3]/4, num_integration)); - - - d_fluor->put(calc_signal(avg_buf[0] / 4, avg_buf[1] / 4, num_integration)); - // Serial.println(d_fluor->arr[d_fluor->write_ptr - 1]); - d_fluoRef->put(calc_signal(avg_buf[2] / 4, avg_buf[3] / 4, num_integration)); - // Serial.println(d_fluoRef->arr[d_fluoRef->write_ptr - 1]); - memset(avg_buf, 0, sizeof(avg_buf)); - } - if (i == 149) { - if (mode == 0) AS_LED_OFF(); // dark mode, actinic OFF - if (mode == 1) AS_LED_Current(dc_current); // light mode, actinic set - } - delayMicroseconds(1400); - } - - ESP_LOGV(TAG, "Phase-4 Completed"); -// PHASE -1 --------------------------- -// Dark decay with increasing interval - if (mode == 0){ - ESP_LOGV(TAG, "Phase-minus1 Started"); - AS_LED_OFF(); - for (uint8_t i = 0; i < 160; i++){ - adpd_trigger(); - adpd.readfifo(expected_readout, 3, ret); - for (uint8_t m = 0; m < 4; m++){ // sign-dark, sign-lit, ref-dark, ref lit - avg_buf[m] += ret[m]; - } - if (i % 4 == 3){ - d_fluor->put(calc_signal(avg_buf[0] / 4, avg_buf[1] / 4, num_integration)); - d_fluoRef->put(calc_signal(avg_buf[2] / 4, avg_buf[3] / 4, num_integration)); - memset(avg_buf, 0, sizeof(avg_buf)); - decay_interval += 5; - } - delayMicroseconds(1400); - delay(decay_interval); - } - } - - ESP_LOGV(TAG, "Measurement Completed"); - // Completed - - adpd.STOP(); - - if (CONNECTION_TYPE == CONNECTION_TYPES::PLOTTING){ - uint16_t l = d_fluor->get_length(); - uint32_t ploter1, ploter2; - for (uint16_t i = 0; i < l; i++){ - ploter1 = d_fluor->pop(); - ploter2 = d_fluoRef->pop(); - Serial.printf("F:%3.4f,S:%d,R:%d\n", (float)ploter1/(float)ploter2, ploter1, ploter2); - } - }else if (CONNECTION_TYPE == CONNECTION_TYPES::AMBYTE){ - d_fluor->fsm_send_esp(0); - d_fluoRef->fsm_send_esp(1); - } - else{ - d_fluor->send_serial("Fluo"); - d_fluoRef->send_serial("Fluoref"); - Serial.println("Data sent"); - } - - ESP_LOGV(TAG, "All Completed"); - - - adpd.gpio_config.GPIO0_cfg = 0; - adpd.gpio_config.SYNC_GPIO = 0; - adpd.gpio_config.EXT_SYNC_EN = 0; - adpd.gpio_setup(&(adpd.gpio_config)); - - delete d_fluor; - delete d_fluoRef; - return 0; -} - - -uint32_t PAM_get_env(uint8_t mode, unsigned int t0){ - uint32_t ret = 0; - unsigned int time = millis() - t0; - uint16_t time_16 = 0; - time_16 = (uint16_t) (time >> 6); - uint8_t d_type = 0; - int16_t data = 0; - - if (mode < 3){ // timestamp only 0 - 2 - data = (time & 0x3F); - d_type = mode; - ret = time_16 << 16 | d_type << 12 | data; - return ret; - - } - - if (mode == 3){ // get leaf temp - - data = (uint16_t) (get_PAR()); - data &= 0x0FFF; - d_type = mode; - ret = time_16 << 16 | d_type << 12 | data; - return ret; - } - - if (mode == 4){ // get leaf temp - data = (int16_t) ((mlx_measure() + 20) * 20); - d_type = mode; - ret = time_16 << 16 | d_type << 12 | data; - return ret; - } - return ret; -} - -uint32_t PAM_retrieve_env(uint32_t r, uint8_t* mode, float_t* data_f, int16_t* data_i){ - int16_t data = (int16_t) (r & 0x00000FFF); - uint8_t d_type = (uint8_t) ((r & 0x0000F000) >> 12); - uint32_t t = ((r & 0xFFFF0000) >> 10); - - if (d_type < 4){ - t += data; - if (mode != NULL) *mode = d_type; - }else if (d_type == 4){ // temperature - if (data_f != NULL) *data_f = (data / 20.0 - 20); - if (mode != NULL) *mode = d_type; } - - - return t; -} - - - - - -int sandbox(uint8_t I620, uint8_t g1, uint8_t g2){ - - - // variables for each trace - - // data counter and buffer - // [sun-amb, leaf-ir, lit_leaf-ir, dark_leaf-ir, lit_leaf-ref, dark_leaf-ref] - uint32_t ret[16] = {0}; - uint16_t fifo_c = 0; - uint16_t fifo_c1 = 0; - - - adpd.STOP(); - adpd.gpio_config.GPIO0_cfg = 1; - adpd.gpio_config.SYNC_GPIO = 0; - adpd.gpio_config.EXT_SYNC_EN = 1; - adpd.gpio_setup(&(adpd.gpio_config)); - - adpd.led_config.driver1_current = I620; - adpd.led_config.driver2_current = 0; - adpd.SNR_config.TIA_gain_CH2 = 5; - adpd.SNR_config.TIA_gain_CH1 = 1; - - adpd.preset_config_ext_fast(0, 1); - adpd.preset_config_ext_fast(1, 2); - adpd.preset_config_ext_fast(2, 3); - adpd.preset_config_ext_fast(3, 4); - - adpd.run_freq(10); - adpd.clear_fifo(); - - adpd.RUN(); - delay(1); - int64_t timer = 0; - - - for (uint16_t i = 0; i < 200; i++){ - digitalWrite(10, HIGH); - delayMicroseconds(1); - digitalWrite(10, LOW); - timer = esp_timer_get_time(); - if (i > 0){ - - adpd.readfifo(16, 3, ret); - - Serial.printf("Data:%d,%d,%d,%d,", ret[0], ret[1], ret[2],ret[3]); - Serial.printf("%d,%d,%d,%d,", ret[4], ret[5], ret[6],ret[7]); - Serial.printf("%d,%d,%d,%d,", ret[8], ret[9], ret[10],ret[11]); - Serial.printf("%d,%d,%d,%d\n", ret[12], ret[13], ret[14],ret[15]); - - - delay(100); - - }else{ - delayMicroseconds(1500); - } - //Serial.println(esp_timer_get_time() - timer); - - } - //Serial.println(adpd.fifo_count()); - adpd.readfifo(16, 3, ret); - - adpd.STOP(); - - - return 0; - -} - - - -static bool PAM_interrupt(bool enable, bool check_sleep){ - uint8_t ret = 0; - if (!enable) return false; - if (check_sleep){ // after light sleep - if (esp_sleep_get_wakeup_cause() == 8){ // wake up serial - Serial.flush(); - ret = serial_read_until(177, 0, 0, 25, true); - } - }else{ - ret = serial_read_until(177, 0, 0, 15, true); - } - if (ret == 1) return true; - return false; -} - - -void actinic_test(uint8_t act1, uint8_t act2, uint8_t t1, uint8_t t2){ - AS_LED_Current(act1); - AS_LED_ON(); - delay(t1 * 1000); - AS_LED_Current(act2); - delay(3000); - AS_LED_OFF(); - AS_LED_Current(0); - - -} - - - - -int fluor_offset_test(uint8_t current, uint8_t num_integ, uint8_t lit_offset, uint8_t dark1_offset, uint8_t dark2_offset, uint8_t pulse_offset, uint8_t pulse_duration){ - - // Setup timeslot 2: Fluor and Ref channels, 4 x 3 bytes - // LED 1A = 620nm - adpd.led_config.driver1_current = current; - adpd.led_config.led1_channel = LED_A; - // LED 2A = 730nm - adpd.led_config.driver2_current = 0; - adpd.led_config.led2_channel = LED_A; - adpd.SNR_config.TIA_gain_CH1 = 1; - adpd.SNR_config.TIA_gain_CH2 = 5; - adpd.preset_config_2x(0, num_integ, lit_offset, dark1_offset, dark2_offset, pulse_offset, pulse_duration); - - adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; - - return 0; -} - - -int fluor_offset(uint32_t* fret){ - - if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1){ - conf_slow_FR_1(); - ESP_LOGW(TAG, "Run array was not configured!"); - } - // set to max possible bytes - uint8_t expected_readout = 8; - uint8_t expected_readout_bytes = expected_readout * 3; - const uint8_t num_integration = 1; - const unsigned int start_t0 = millis(); - - - // variables for each trace - uint8_t pc = 0; - uint8_t _type = 0; - uint8_t farred, actinic, subsampling = 0; - uint16_t num_ptx = 0; - - // data counter and buffer - // [sun-amb, leaf-ir, lit_leaf-ir, dark_leaf-ir, lit_leaf-ref, dark_leaf-ref] - uint32_t ret[expected_readout] = {0}; - uint32_t counter = 0; - uint16_t fifo_c = 0; - uint8_t watch_dog_timer = 0; - int32_t tmp_var = 0; - uint32_t buf_opt[4] = {0}; - uint32_t _tmparr = 0; - uint8_t _repeats = 1; - - - - adpd.STOP(); - adpd.run_freq(100); - adpd.clear_fifo(); - adpd.num_ts(3); - expected_readout = 8; - expected_readout_bytes = expected_readout * 3; - AS_LED_OFF(); - AS_LED_Current(0); - - - num_ptx = 64; - - uint32_t fluor, fluoRef, sun, leaf, r730, r730Ref; - uint32_t ret_fluor = 0, ret_fluoRef = 0, ret_sun = 0, ret_leaf = 0, ret_r730 = 0, ret_r730Ref = 0; - - for (uint8_t n = 0; n < 4; n++){ - adpd.RUN(); - delay(1); - fluor = 0; fluoRef = 0; sun = 0; leaf = 0; r730 = 0; r730Ref = 0; - counter = 0; - - while (counter < num_ptx){ - fifo_c = adpd.fifo_count(); - while (fifo_c >= expected_readout_bytes){ // read all bytes from FIFO - adpd.readfifo(expected_readout, 3, ret); - fifo_c -= expected_readout_bytes; - if (counter == num_ptx) break; - // 0: sun-vis; 1: leaf-ir; 2: fluoS_dark; 3: fluoS_lit; 4: fluoR_dark; 5: fluoR_lit; 6: Reflect_signal; 7: reflect_ref - // save fluor signal and ref - fluor += calc_signal(ret[2], ret[3], num_integration); - fluoRef += calc_signal(ret[4], ret[5], num_integration); - r730 += ret[6]; - r730Ref += ret[7]; - sun += ret[0]; - leaf += ret[1]; - counter += 1; - } - - } - adpd.STOP(); - - if (counter > 10){ - ret_fluor += fluor / counter; - ret_fluoRef += fluoRef / counter; - ret_sun += sun / counter; - ret_leaf += leaf / counter; - ret_r730 += r730 / counter; - ret_r730Ref += r730Ref / counter; - } - - } - fret[0] = ret_fluor / 4; - fret[1] = ret_fluoRef / 4; - fret[2] = ret_sun / 4; - fret[3] = ret_leaf / 4; - fret[4] = ret_r730 / 4; - fret[5] = ret_r730Ref / 4; - return 0; - -} - - - - - - - - - - - - - - - - - - - - - - - -/* - - - -// - -int detector_preset_1(uint8_t current, uint8_t gain_fluo, uint8_t gain_ref, uint8_t gain_par_ir, uint8_t gain_par_vis){ - - // Setup timeslot 1: two ambient light channels - adpd.led_config.driver1_current = 0; - adpd.led_config.driver2_current = 0; - adpd.SNR_config.TIA_gain_CH2 = gain_par_ir; - adpd.SNR_config.TIA_gain_CH1 = gain_par_vis; - adpd.preset_config_1(0, 4); - - // Setup timeslot 2: Fluor and Ref channels - // LED 1A = 620nm - adpd.led_config.driver1_current = current; - adpd.led_config.led1_channel = LED_A; - // LED 2A = 730nm - adpd.led_config.driver2_current = 0; - adpd.led_config.led2_channel = LED_A; - - adpd.SNR_config.TIA_gain_CH1 = gain_fluo; - adpd.SNR_config.TIA_gain_CH2 = gain_ref; - - adpd.preset_config_2(1, 4); - ESP_LOGI(TAG, "Preset 1 set"); - return 0; -} - - - - - - -int run_preprocess(uint8_t length, uint8_t* arr, uint16_t* data_counter){ - uint8_t pc = 0; - uint8_t _type = 0; - uint8_t para1, actinic, subsampling = 0; - uint16_t num_ptx, freq = 0; - uint16_t _data_counter[4] = {0}; - - while (pc < length){ - _type = *(arr + pc * 8); - if (_type == 1){ - arr_line_parse_type1(arr + pc * 8, ¶1, &num_ptx, &freq, &actinic, &subsampling, _data_counter); - ESP_LOGV(TAG, "Run type:%d, number:%d, freq: %d, actinic: %d, subfactor %d", _type, num_ptx, freq, actinic, subsampling); - for (uint8_t i = 0; i < 4; i++) data_counter[i] += _data_counter[i]; - } - pc += 1; - } - return 0; -} - -int send_binary_data(uint32_t* arr, uint16_t len){ - char c; - Serial.setTimeout(100); - while(Serial.available()){ - c = Serial.read(); - Serial.print(c); - } - - Serial.println("Wake!"); - - if (Serial.read("Ready", 5)){ - while(Serial.available()){ - c = Serial.read(); - Serial.print(c); - } - }else{ - Serial.println("No response"); - } -} - - -int run_arr(uint8_t length, uint8_t* arr){ // old version - const uint8_t expected_readout = 6; - const uint8_t expected_readout_bytes = expected_readout * 3; - const uint8_t num_integration = 4; - - // Run protocol preprecess, get storage size - uint16_t data_count[] = {0, 0, 0, 0}; - if (run_preprocess(length, arr, data_count) == -1) return -1; // calculate data counts - ESP_LOGV(TAG, "Sample: %d, ref: %d, amb: %d, dark: %d", data_count[0], data_count[1], data_count[2], data_count[3]); - - dataclass *Fdata = new dataclass; - dataclass *Rdata = new dataclass; - dataclass *Adata = new dataclass; - dataclass *Ddata = new dataclass; - - if (!(Fdata->init(data_count[0]) && Rdata->init(data_count[1]) && Adata->init(data_count[2]) && Ddata->init(data_count[3]))) return -1; - ESP_LOGV(TAG, "Memory allocation completed"); - - // configure the timeslots - //detector_preset_1(100, 1, 4, 4, 4); - - // variables for each trace - uint8_t pc = 0; - uint8_t _type = 0; - uint8_t para1, actinic, subsampling = 0; - uint16_t num_ptx, freq = 0; - - // data counter and buffer - // [sun-amb, leaf-ir, lit_leaf-ir, dark_leaf-ir, lit_leaf-ref, dark_leaf-ref] - uint32_t ret[expected_readout] = {0}; - uint32_t counter = 0; - uint16_t fifo_c = 0; - uint8_t watch_dog_timer = 0; - int32_t tmp_var = 0; - uint32_t send_arr[200]; - - - adpd.STOP(); - while (pc < length){ - _type = *(arr + pc * 8); - if (_type == 1){ - arr_line_parse_type1((arr + pc * 8), ¶1, &num_ptx, &freq, &actinic, &subsampling, data_count); - adpd.run_freq(freq); - adpd.clear_fifo(); - if (actinic > 4){ - if (actinic == 255){ - AS_LED_Current(255); - AS_LED_ON(); - digitalWrite(1, HIGH); - } - AS_LED_Current(actinic - 1); - AS_LED_ON(); - }else{ - AS_LED_OFF(); - } - - counter = 0; - adpd.RUN(); - - while (counter < num_ptx){ - fifo_c = adpd.fifo_count(); - while (fifo_c >= expected_readout_bytes){ - adpd.readfifo(expected_readout, 3, ret); - fifo_c -= expected_readout_bytes; - if (counter == num_ptx) break; - tmp_var = ((int)ret[3] - (int)ret[2] + 250) - (0.006 / (int)num_integration) * ((int)ret[2] - 16384 * num_integration); - if ((tmp_var > 0) && (ret[3] > ret[2])){ - Fdata->put(tmp_var); - }else{ - Fdata->put(0); - } - - // Serial.printf("%d, %d, %d, %d, %d, %d\n", ret[0], ret[1], ret[2], ret[3], ret[4], ret[5]); - - Rdata->put(ret[5] - ret[4]); - Adata->put(ret[0]); - Ddata->put(ret[1]); - counter++; - watch_dog_timer = 0; - } - - if (Fdata->length > 4){ - for (uint8_t z = 0; z < 4; z++){ - send_arr[0 + z * 4] = Fdata->pop(); - send_arr[1 + z * 4] = Rdata->pop(); - send_arr[2 + z * 4] = Adata->pop(); - send_arr[3 + z * 4] = Ddata->pop(); - Serial.printf("%d, %d, %d, %d\n", send_arr[0 + z * 4], send_arr[1 + z * 4], send_arr[2 + z * 4],send_arr[3 + z * 4]); - } - - //send_data(send_arr, 16); - }else{ - // esp_sleep_enable_timer_wakeup(wait_time * 5000); - // esp_light_sleep_start(); - delay(1); - } - - } - - adpd.STOP(); - AS_LED_OFF(); - digitalWrite(1, LOW); - } - if (_type == 2){ - //flash duration - para1 = *(arr + pc * 8 + 6); - digitalWrite(1, HIGH); - delay(para1); - digitalWrite(1, LOW); - } - pc += 1; - } - - delete Ddata; - delete Adata; - delete Rdata; - delete Fdata; - - return 0; - -} - -*/ - -/* - -int data_allocation(void** F_data, void **R_data, void **A_data, void **D_data, uint16_t* data_count){ - - if ((data_count[0] + data_count[1] + data_count[2] + data_count[3]) > MAX_MEMORY_ALLOC){ - ESP_LOGE(TAG, "More than %d", MAX_MEMORY_ALLOC*4); - return -1; - } - - - if ((data_count[0] > 0) and data_count[0] < MAX_MEMORY_ALLOC) *F_data = heap_caps_calloc(data_count[0], sizeof(uint32_t), MALLOC_CAP_32BIT); - if (*F_data == NULL) goto F_data_failed; - ESP_LOGV(TAG, "Allocation F_data: %d bytes", data_count[0] * 4); - - if ((data_count[1] > 0) and data_count[1] < MAX_MEMORY_ALLOC) *R_data = heap_caps_calloc(data_count[1], sizeof(uint32_t), MALLOC_CAP_32BIT); - if (*R_data == NULL) goto R_data_failed; - ESP_LOGV(TAG, "Allocation R_data: %d bytes", data_count[1] * 4); - - if ((data_count[2] > 0) and data_count[2] < MAX_MEMORY_ALLOC) *A_data = heap_caps_calloc(data_count[2], sizeof(uint32_t), MALLOC_CAP_32BIT); - if (*A_data == NULL) goto A_data_failed; - ESP_LOGV(TAG, "Allocation A_data: %d bytes", data_count[2] * 4); - - if ((data_count[3] > 0) and data_count[3] < MAX_MEMORY_ALLOC) *D_data = heap_caps_calloc(data_count[3], sizeof(uint32_t), MALLOC_CAP_32BIT); - if (*D_data == NULL) goto D_data_failed; - ESP_LOGV(TAG, "Allocation D_data: %d bytes", data_count[3] * 4); - - - - return 0; - - D_data_failed: - free(D_data); - A_data_failed: - free(A_data); - R_data_failed: - free(R_data); - F_data_failed: - free(F_data); - ESP_LOGE(TAG, "Allocation failed"); - return -1; -} - - - - - -int run_arr(uint8_t length, uint8_t* arr){ // old version - const uint8_t expected_readout = 6; - const uint8_t expected_readout_bytes = expected_readout * 3; - const uint8_t num_integration = 4; - - // Run protocol preprecess, get storage size - uint16_t data_count[] = {0, 0, 0, 0}; - if (run_preprocess(length, arr, data_count) == -1) return -1; // calculate data counts - ESP_LOGV(TAG, "Sample: %d, ref: %d, amb: %d, dark: %d", data_count[0], data_count[1], data_count[2], data_count[3]); - uint32_t *F_data = NULL; - uint32_t *R_data = NULL; - uint32_t *A_data = NULL; - uint32_t *D_data = NULL; - if (data_allocation((void**)(&F_data), (void**)&R_data, (void**)&A_data, (void**)&D_data, data_count) == -1) return -1; - F_data[0] = 0; - ESP_LOGV(TAG, "Memory allocation completed"); - - // configure the timeslots - //detector_preset_1(100, 1, 4, 4, 4); - - // variables for each trace - uint8_t pc = 0; - uint8_t _type = 0; - uint8_t para1, actinic, subsampling = 0; - uint16_t num_ptx, freq = 0; - - // data counter and buffer - // [sun-amb, leaf-ir, lit_leaf-ir, dark_leaf-ir, lit_leaf-ref, dark_leaf-ref] - uint32_t ret[expected_readout] = {0}; - uint32_t counter = 0; - uint16_t fifo_c = 0; - uint8_t watch_dog_timer = 0; - int32_t tmp_var = 0; - - - adpd.STOP(); - while (pc < length){ - _type = *(arr + pc * 8); - if (_type == 1){ - arr_line_parse_type1((arr + pc * 8), ¶1, &num_ptx, &freq, &actinic, &subsampling, data_count); - adpd.run_freq(freq); - adpd.clear_fifo(); - if (actinic > 4){ - AS_LED_Current(actinic); - AS_LED_ON(); - }else{ - AS_LED_OFF(); - } - counter = 0; - adpd.RUN(); - - while (counter < num_ptx){ - fifo_c = adpd.fifo_count(); - while (fifo_c >= expected_readout_bytes){ - adpd.readfifo(expected_readout, 3, ret); - fifo_c -= expected_readout_bytes; - if (counter == num_ptx) break; - tmp_var = ((int)ret[3] - (int)ret[2] + 250) - (0.006 / (int)num_integration) * ((int)ret[2] - 16384 * num_integration); - F_data[counter] = 0; - if ((tmp_var > 0) && (ret[3] > ret[2])) F_data[counter] = tmp_var; - R_data[counter] = ret[5] - ret[4]; - A_data[counter] = ret[0]; - D_data[counter] = ret[1]; - - if (true){ - Serial.print(F_data[counter]); - Serial.print(","); - Serial.print(R_data[counter]); - Serial.print(","); - Serial.print(A_data[counter]); - Serial.print(","); - Serial.print(D_data[counter]); - Serial.println(); - } - counter++; - watch_dog_timer = 0; - } - - // esp_sleep_enable_timer_wakeup(wait_time * 5000); - // esp_light_sleep_start(); - delay(1); - } - adpd.STOP(); - AS_LED_OFF(); - } - pc += 1; - } - - - - free(D_data); - free(A_data); - free(R_data); - free(F_data); - - return 0; - - -} - - - - - - -int steady_state(uint8_t current, uint8_t gain_fluo, uint8_t gain_ref, uint8_t gain_dark, uint16_t freq, uint16_t num, uint16_t extra_info1, uint16_t extra_info2){ - - const uint8_t num_integration = 4; - - uint32_t *F_data = NULL; - uint32_t *R_data = NULL; - uint32_t *A_data = NULL; - - - uint16_t wait_time = 1000 / freq; - if (wait_time > 1000) wait_time = 1000; - - F_data = (uint32_t*)heap_caps_calloc(num, sizeof *F_data, MALLOC_CAP_32BIT); - if (F_data == NULL) return -8; - R_data = (uint32_t*)heap_caps_calloc(num, sizeof *R_data, MALLOC_CAP_32BIT); - if (R_data == NULL){ - free(F_data); - return -9; - }; - A_data = (uint32_t*)heap_caps_calloc(num, sizeof *A_data, MALLOC_CAP_32BIT); - if (A_data == NULL){ - free(F_data); - free(R_data); - return -10; - }; - - - - // LED 1A = 620nm - adpd.led_config.driver1_current = 40; - adpd.led_config.driver1_current = LED_A; - // LED 2A = 730nm - adpd.led_config.driver2_current = 0; - adpd.led_config.led2_channel = LED_A; - - - //adpd.SNR_config.TIA_gain_CH1 = gain_dark; - - adpd.led_config.driver2_current = current; - adpd.SNR_config.TIA_gain_CH1 = gain_fluo; - adpd.SNR_config.TIA_gain_CH2 = gain_ref; - adpd.preset_config_2(0, num_integration); - - - adpd.run_freq(freq); - adpd.clear_fifo(); - //AS_LED_OFF(); - - unsigned long start_time = millis(); - - adpd.RUN(); - uint32_t ret[6] = {0}; - uint32_t counter = 0; - uint16_t fifo_c = 0; - uint8_t watch_dog_timer = 0; - int32_t tmp_var = 0; - - - while (counter < num){ - fifo_c = adpd.fifo_count(); - while (fifo_c > 14){ - adpd.readfifo(5, 3, ret); - if (counter == num) break; - - tmp_var = (ret[2] - ret[1] + 250) - (0.006 / num_integration) * ((int)ret[1] - 16384 * num_integration); - - F_data[counter] = 0; - if (tmp_var > 0) F_data[counter] = tmp_var; - R_data[counter] = ret[4] - ret[3]; - A_data[counter] = ret[0]; - - - if (true){ - Serial.print(int((float)F_data[counter] / R_data[counter] * 1000)); - Serial.print(","); - Serial.print(F_data[counter]); - Serial.print(","); - Serial.print(R_data[counter]); - Serial.print(","); - Serial.println(A_data[counter]); - } - - // if (printout){ - // Serial.print(ret[2] - 16384 * num_integration + 1000); - // Serial.print(","); - // Serial.print(ret[1] - 16384 * num_integration + 1000); - // Serial.print(","); - // Serial.print(tmp_var); - // Serial.print(","); - // Serial.println(A_data[counter]); - // } - - - counter++; - watch_dog_timer = 0; - fifo_c -= 15; - } - - // esp_sleep_enable_timer_wakeup(wait_time * 5000); - // esp_light_sleep_start(); - delay(wait_time); - watch_dog_timer += 1; - if (watch_dog_timer > 4){ - Serial.println("RUN timeout"); - break; - } - - } - adpd.STOP(); - - - - free(F_data); - free(R_data); - free(A_data); - - if (watch_dog_timer > 4) return -8; - - return 1; -} -*/ +#include "PAM.h" +#include "nvs1.h" + +static const char* TAG = "PAM"; +static bool measure_temp = true; +static bool PAM_interrupt(bool, bool); + +uint8_t adpd_mode = 0; +adpd_current_config_t adpd_current_config; +adpd_gains_config_t adpd_gains_config; + + +int serial_read_until(uint8_t target1, uint8_t target2 = 0, uint8_t target3 = 0, uint16_t timeout = 20, bool remove = false); + +// use pre-set values +int conf_slow_FR_1(void){ + + if (adpd_gains_config.init == false) ESP_LOGE(TAG, "Gain preset not initized, use default!"); + if (adpd_current_config.init == false) ESP_LOGE(TAG, "Current preset not initized, use default!"); + return conf_slow_FR_1(adpd_current_config.I620, adpd_current_config.I720, adpd_current_config.IR, adpd_gains_config.Fluo, adpd_gains_config.FluoRef, adpd_gains_config.Sun, adpd_gains_config.Leaf, adpd_gains_config.IR, adpd_gains_config.IRRef); +} + + +// Slow measurements with 4 time slots +// @param I620 measuring light current (0 - 127) +// @param I730 IR reflection current (0 - 127) +// @param I_FR Far-red treatment current (0 - 127) +// @param G_Fluor fluorescence signal gain (0 - 5) +// @param G_FluorRef fluorescence rerefence gain (0 - 5) +// @param G_Sun Sun facing PD gain (0 - 5) +// @param G_IR Leaf facing PD gain (0 - 5) +// @param G_FR IR reflection signal gain (0 - 5) +// @param G_FRref IR reflection reference gain (0 - 5) +int conf_slow_FR_1(uint8_t I620, uint8_t I730, uint8_t I_FR, uint8_t G_Fluor, uint8_t G_FluorRef, uint8_t G_Sun, uint8_t G_IR, uint8_t G_FR, uint8_t G_FRref){ + + int32_t config_result = 0; + + // Setup timeslot 1: two ambient light channels, 2 x 3 bytes + adpd.led_config.driver1_current = 0; + adpd.led_config.driver2_current = 0; + adpd.SNR_config.TIA_gain_CH2 = G_IR; // channel 2: leaf IR reflection + adpd.SNR_config.TIA_gain_CH1 = G_Sun; // channel 1: sun vis + config_result = adpd.preset_config_1(0, 4); + if (config_result != jii::adpd6000::kOk) return config_result; + + + + // Setup timeslot 2: Fluor and Ref channels, 4 x 3 bytes + // LED 1A = 620nm + adpd.led_config.driver1_current = I620; + adpd.led_config.led1_channel = LED_A; + // LED 2A = 730nm + adpd.led_config.driver2_current = 0; + adpd.led_config.led2_channel = LED_A; + adpd.SNR_config.TIA_gain_CH1 = G_Fluor; + adpd.SNR_config.TIA_gain_CH2 = G_FluorRef; + config_result = adpd.preset_config_2(1, 1); + if (config_result != jii::adpd6000::kOk) return config_result; + + + // Setup timeslot 3: IR leave reflection, 2 x 3 bytes + // LED 1A = 620nm + adpd.led_config.driver1_current = 0; + adpd.led_config.led1_channel = LED_A; + // LED 2A = 730nm + adpd.led_config.driver2_current = I730; + adpd.led_config.led2_channel = LED_A; + adpd.SNR_config.TIA_gain_CH1 = G_FR; + adpd.SNR_config.TIA_gain_CH2 = G_FRref; + + config_result = adpd.preset_config_3(2, 4); + if (config_result != jii::adpd6000::kOk) return config_result; + + // Setup timeslot 4-5-6: Far-red illumination, 0 data + // LED 1A = 620nm + adpd.led_config.driver1_current = 0; + adpd.led_config.led1_channel = LED_A; + // LED 2A = 730nm + adpd.led_config.driver2_current = I_FR; + adpd.led_config.led2_channel = LED_A; + + //make 6 time slots, ~ 2 ms without repeats, can get 200 repeats ~ 400ms + for (uint8_t slot = 3; slot <= 8; ++slot){ + config_result = adpd.preset_config_4(slot); + if (config_result != jii::adpd6000::kOk) return config_result; + } + + adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; + + return 0; +} + + +uint32_t arr_line_parse_type1(uint8_t* line, uint8_t* num1, uint16_t* num2, uint16_t* num3, uint8_t* num4, uint8_t* num5, uint16_t* data_count){ + // type 1: Must have measurements: Fluor/Ref + // optional measurements: leaf IR, sun VIs, Reflect/Ref + // 0 = no points, 1 = same freq, 2 = every 8 + // data_count 0: fluor, 1: ambient, 2: reflection + uint8_t type = line[0]; // run type 1 = steady state, 0 = skip, 2 = no ir + *num1 = line[1]; // FR on / off + *num2 = line[3] + (line[2] << 8); // sample number + *num3 = line[5] + (line[4] << 8); // frequency + *num4 = line[6]; // actinic + *num5 = line[7]; // sub-sampling factor + + if (data_count == NULL) return 0; + + if ((type == 1) || (type == 2)){ + data_count[0] = *num2; + if (line[7] == 0){ // no ambient + data_count[1] = 0; + data_count[2] = 0; + }else if (line[7] == 1){ // every + data_count[1] = *num2; + data_count[2] = *num2; + }else if (line[7] == 2){ // every 8 + data_count[1] = (*num2) / 8; + data_count[2] = (*num2) / 8; + }else{ + data_count[1] = 0; + data_count[2] = 0; + } + if (type == 2) data_count[2] = 0; + + return data_count[0] * 2 + data_count[1] * 2 + data_count[2] * 2; + + } + return 0; +} + + + +int run_preprocess_type1(uint8_t length, uint8_t* arr, uint16_t* data_counter){ + uint8_t pc = 0; + uint8_t _type = 0; + uint8_t para1, actinic, subsampling = 0; + uint16_t num_ptx = 0, freq = 0; + uint16_t _data_counter[4] = {0}; + + while (pc < length){ + _type = *(arr + pc * 8); + if ((_type == 1) || (_type == 2)){ + arr_line_parse_type1(arr + pc * 8, ¶1, &num_ptx, &freq, &actinic, &subsampling, _data_counter); + ESP_LOGV(TAG, "Run type:%d, number:%d, freq: %d, actinic: %d, subfactor %d", _type, num_ptx, freq, actinic, subsampling); + for (uint8_t i = 0; i < 4; i++) data_counter[i] += _data_counter[i]; + } + pc += 1; + } + return 0; +} + +// Shared output sink for a completed run: stream the 7 channel buffers over the +// active CONNECTION_TYPE. COMPUTER -> ASCII send_serial; AMBYTE -> binary FSM +// (env retried once; sun/leaf/730 only when present). Extracted verbatim from the +// per-run send block so every run path (sweep #4) shares one copy. Wire-touching: +// re-run HW_CONFORMANCE.md after changing it. +static void pam_send_results(dataclass* d_env, dataclass* d_fluor, dataclass* d_fluoRef, + dataclass* d_sun, dataclass* d_leaf, dataclass* d_730, + dataclass* d_730Ref, dataclass* d_timing, uint8_t subsampling, + bool has_730, bool allow_interrupt){ + if (CONNECTION_TYPE == CONNECTION_TYPES::COMPUTER){ + d_env->send_serial("env"); + d_fluor->send_serial("s_630"); + d_fluoRef->send_serial("r_630"); + d_sun->send_serial("sun"); + d_leaf->send_serial("leaf"); + d_730->send_serial("s_730"); + d_730Ref->send_serial("r_730"); + if (d_timing != NULL) d_timing->send_serial("timing"); + + Serial.println("Data sent"); + }else if(CONNECTION_TYPE == CONNECTION_TYPES::AMBYTE){ + // Change 4: one wake per run, then every array streams back-to-back as + // (length header + data + trailer); run_esp.cpp's trailing ESP_CMD_END (240) + // terminates the stream. If the ambyte never wakes, there is nothing to send. + if (ambyte_wake(allow_interrupt) != 1) return; + + // Per-array element width / dtype (Change 1 self-describing header, Change 2 widths): + // ENV -> int16 centi-degC (elem_width 2, dtype 1) + // ADC ch -> uint16 (16-bit ADPD regs) (elem_width 2, dtype 0, clamped to 0xFFFF) + // TIMING -> uint32 ticks (elem_width 4, dtype 0) + d_env->fsm_send_array(0, 2, 1); + d_fluor->fsm_send_array(1, 2, 0); + d_fluoRef->fsm_send_array(2, 2, 0); + if (subsampling > 0){ + d_sun->fsm_send_array(3, 2, 0); + d_leaf->fsm_send_array(4, 2, 0); + if (has_730){ + d_730->fsm_send_array(5, 2, 0); + d_730Ref->fsm_send_array(6, 2, 0); + } + } + if (d_timing != NULL) d_timing->fsm_send_array(7, 4, 0); // Change 3: TIMING block + } +} + +int run_arr_type1(uint8_t length, uint8_t* arr, bool led_persist){ + return run_arr_type1(length, arr, led_persist, false); +} + +// ── Async result holder (parallel trigger/poll/fetch protocol) ────────────── +// Holds the eight result buffers of one retained run plus the metadata +// pam_send_results() needs, until the host FETCHes them. One run at a time. +struct ambit_async_result_t { + dataclass *d_env = NULL, *d_fluor = NULL, *d_fluoRef = NULL, *d_sun = NULL, + *d_leaf = NULL, *d_730 = NULL, *d_730Ref = NULL, *d_timing = NULL; + uint8_t subsampling = 0; + bool has_730 = false; + bool allow_interrupt = false; + uint8_t state = AMBIT_ASYNC_IDLE; +}; +static ambit_async_result_t g_async; + +void ambit_async_clear(void){ + delete g_async.d_env; delete g_async.d_fluor; delete g_async.d_fluoRef; + delete g_async.d_sun; delete g_async.d_leaf; delete g_async.d_730; + delete g_async.d_730Ref; delete g_async.d_timing; + g_async = ambit_async_result_t(); // re-init all pointers to NULL, state IDLE +} + +uint8_t ambit_async_get_state(void){ return g_async.state; } + + +// openJII env channel: fw_new stores leaf temp as centi-degC in the low 16 bits. +// Used only on the json_output path (arrun via the JSON envelope). +static void send_env_json(dataclass* d){ + Serial.print("\"env\":["); + if (d->available){ + uint16_t n = d->get_length(); + for (uint16_t i = 0; i < n; i++){ + uint32_t raw = d->pop(); + float temp = (int16_t)(raw & 0xFFFF) / 100.0f; + if (i > 0) Serial.print(','); + Serial.printf("{\"temp_c\":%.2f}", temp); + } + } + Serial.print(']'); +} + +// openJII derived channel: fluo = s_630 / r_630 (signal / reference), one float +// per sample. Computed on-device only for the JSON path because the openJII sink +// consumes the JSON verbatim and does no math. MUST run BEFORE d_fluor / d_fluoRef are +// drained by send_json(): it reads arr[] directly and relies on read_ptr == 0 (nothing +// popped yet). den == 0 -> 0 (calc_signal floors the reference at 0 when dark dominates). +static void send_fluo_json(dataclass* num, dataclass* den){ + Serial.print("\"fluo\":["); + if (num->available && den->available){ + uint16_t n = num->get_length(); + uint16_t m = den->get_length(); + if (m < n) n = m; // defensive: emit only paired samples + for (uint16_t i = 0; i < n; i++){ + if (i > 0) Serial.print(','); + uint32_t d = den->arr[i]; + if (d == 0) Serial.print('0'); + else Serial.printf("%.5f", (double)num->arr[i] / (double)d); + } + } + Serial.print(']'); +} + +int run_arr_type1(uint8_t length, uint8_t* arr, bool led_persist, bool allow_interrupt, bool json_output, bool retain){ + if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1){ + conf_slow_FR_1(); + ESP_LOGW(TAG, "Run array was not configured!"); + } + // set to max possible bytes + uint8_t expected_readout = 8; + uint8_t expected_readout_bytes = expected_readout * 3; + const uint8_t num_integration = 1; + const unsigned int start_t0 = millis(); + const int64_t run_tick_begin = esp_timer_get_time(); // Change 3: TIMING - us at run start + + // Run protocol preprecess, get storage size + uint16_t data_count[] = {0, 0, 0, 0}; + if (run_preprocess_type1(length, arr, data_count) == -1) return -1; // calculate data counts + ESP_LOGV(TAG, "Sample & Ref: %d, optionals: %d", data_count[0], data_count[1]); + + dataclass *d_env = new dataclass; // + dataclass *d_fluor = new dataclass; // fluorescence signal + dataclass *d_fluoRef = new dataclass; // fluorescence reference + + dataclass *d_sun = new dataclass; // sun-side ambient + dataclass *d_leaf = new dataclass; // leaf-side ambient + dataclass *d_730 = new dataclass; // 730nm reflectance signal + dataclass *d_730Ref = new dataclass; // 730nm reference + dataclass *d_timing = new dataclass; // Change 3: [tick_begin, tick_end] controller ticks (us) + + + if (!(d_env->init(512))) return -1; + if (!(d_timing->init(2))) return -1; + if (!( (d_fluor->init(data_count[0])) && (d_fluoRef->init(data_count[0])) )) return -1; + if (data_count[1] > 0){ + if (!( (d_sun->init(data_count[1])) && (d_leaf->init(data_count[1])) )) return -1;} + if (data_count[2] > 0){ + if (!( (d_730->init(data_count[2])) && (d_730Ref->init(data_count[2])) )) return -1; + } + + + ESP_LOGV(TAG, "Memory allocation completed"); + + + // variables for each trace + uint8_t pc = 0; + uint8_t _type = 0; + uint8_t farred = 0, actinic = 0, subsampling = 0; + uint16_t num_ptx = 0, freq = 0; + + // data counter and buffer + // [sun-amb, leaf-ir, lit_leaf-ir, dark_leaf-ir, lit_leaf-ref, dark_leaf-ref] + uint32_t ret[expected_readout] = {0}; + uint32_t counter = 0, ploter1 = 0, ploter2 = 0; + uint16_t fifo_c = 0; + uint8_t watch_dog_timer = 0; + int32_t tmp_var = 0; + uint32_t buf_opt[4] = {0}; + uint32_t _tmparr = 0; + uint8_t _repeats = 1; + uint32_t light_sleep_time = 1; + float_t leaf_temp = 0.0; + unsigned int env_timer1 = millis(); + bool measure_temperature = false; + bool interrupt_run = false; + + _tmparr = PAM_get_env(4, start_t0); + d_env->put(_tmparr); + leaf_temp = (int16_t) (_tmparr & 0xFFFF) / 100.0; + + + adpd.STOP(); + while (pc < length){ + if (interrupt_run) break; + _type = *(arr + pc * 8); //get line type + if ((_type == 1) || (_type == 2)){ // all channels + arr_line_parse_type1((arr + pc * 8), &farred, &num_ptx, &freq, &actinic, &subsampling, NULL); + adpd.run_freq(freq); + adpd.clear_fifo(); + light_sleep_time = (1000/freq); + // JSON path: exactly ONE env temperature per array, sampled once at the start + // (the unconditional d_env->put() before this loop). No in-run 2 s resampling, so + // env is a single {"temp_c":..} for every array — fast or slow. All other paths + // keep the original cadence: that env stream is part of the FROZEN wire contract + // with the datalogger, so its bytes must not change. Runtime branch on + // json_output (was compile-time VARIANT_CLOUD) — same semantics per caller. + if (json_output) measure_temperature = false; + else measure_temperature = (light_sleep_time > 20) && measure_temp && actinic < 50; + + + if (_type == 1){ // use IR reflect + if (farred == 1){ // whether use actinic IR + adpd.num_ts(9); + _repeats = int(400/freq); + if (freq < 3) _repeats = 250; + if (_repeats == 0) _repeats = 1; + for (uint8_t i = 3; i < 9;i++) adpd.repeats_only(i, 1, _repeats); + } + else{ + adpd.num_ts(3); + } + expected_readout = 8; + expected_readout_bytes = expected_readout * 3; + }else{ // NO IR + adpd.num_ts(2); + expected_readout = 6; + expected_readout_bytes = expected_readout * 3; + } + + if (actinic > 3){ + AS_LED_Current(actinic); + AS_LED_ON(); + }else{ + AS_LED_OFF(); + AS_LED_Current(0); + } + + + counter = 0; + for (uint8_t i = 0; i < 4; i++) buf_opt[i] = 0; + adpd.RUN(); + delay(2); + while (counter < num_ptx){ + if (interrupt_run) break; + fifo_c = adpd.fifo_count(); + while (fifo_c >= expected_readout_bytes){ // read all bytes from FIFO + adpd.readfifo(expected_readout, 3, ret); + fifo_c -= expected_readout_bytes; + if (counter == num_ptx) break; + // 0: sun-vis; 1: leaf-ir; 2: fluoS_dark; 3: fluoS_lit; 4: fluoR_dark; 5: fluoR_lit; 6: Reflect_signal; 7: reflect_ref + // Apply each stored baseline exactly once at the result boundary. + const uint32_t calibrated_fluor = apply_adpd_calibration( + ambit_calibration::S630, calc_signal(ret[2], ret[3], num_integration)); + const uint32_t calibrated_fluo_ref = apply_adpd_calibration( + ambit_calibration::R630, calc_signal(ret[4], ret[5], num_integration)); + const uint32_t calibrated_sun = apply_adpd_calibration(ambit_calibration::SUN, ret[0]); + const uint32_t calibrated_leaf = apply_adpd_calibration(ambit_calibration::LEAF, ret[1]); + const uint32_t calibrated_730 = _type == 1 + ? apply_adpd_calibration(ambit_calibration::S730, ret[6]) : 0; + const uint32_t calibrated_730_ref = _type == 1 + ? apply_adpd_calibration(ambit_calibration::R730, ret[7]) : 0; + d_fluor->put(calibrated_fluor); + d_fluoRef->put(calibrated_fluo_ref); + + + // save option data + if (subsampling > 0){ + if (subsampling == 1){ // every point + d_sun->put(calibrated_sun); + d_leaf->put(calibrated_leaf); + if (_type == 1){d_730->put(calibrated_730);d_730Ref->put(calibrated_730_ref);} // ir enabled + + }else if (subsampling == 2){ + buf_opt[0] += calibrated_sun; + buf_opt[1] += calibrated_leaf; + if (_type == 1){ + buf_opt[2] += calibrated_730; + buf_opt[3] += calibrated_730_ref; + } + + if (counter % 8 == 7){ + d_sun->put(buf_opt[0]/8); + d_leaf->put(buf_opt[1]/8); + if(_type == 1){d_730->put(buf_opt[2]/8);d_730Ref->put(buf_opt[3]/8);} + for (uint8_t i = 0; i < 4; i++) buf_opt[i] = 0; + } + } + } + + if (CONNECTION_TYPE == CONNECTION_TYPES::PLOTTING){ + ploter1 = calibrated_fluor; + ploter2 = calibrated_fluo_ref; + if (ploter2 == 0){ + ploter2 = 1; + ploter1 = 0; + } + if (_type == 1) { + Serial.printf("T:%2.3f,F:%3.4f,S:%d,R:%d,7:%d,7R:%d,Sun:%d,L:%d\n", leaf_temp, (float)ploter1/(float)ploter2, ploter1, ploter2, calibrated_730, calibrated_730_ref, calibrated_sun, calibrated_leaf); + }else if (_type == 2){ + Serial.printf("T:%2.3f,F:%3.4f,S:%d,R:%d,Sun:%d,L:%d\n", leaf_temp, (float)ploter1/(float)ploter2, ploter1, ploter2, calibrated_sun, calibrated_leaf); } + Serial.flush(); + } + counter++; + watch_dog_timer = 0; + } + + // do light sleep + //esp_sleep_enable_timer_wakeup(1000); + if (counter + 10 < num_ptx){ // a lot of measurements + // do temperature measurement? + if (measure_temperature && (millis() - env_timer1 > 2000)){ + _tmparr = PAM_get_env(4, start_t0); + d_env->put(_tmparr); + leaf_temp = (int16_t) (_tmparr & 0xFFFF) / 100.0; + env_timer1 = millis(); + esp_sleep_enable_timer_wakeup(1000); + } + else esp_sleep_enable_timer_wakeup(light_sleep_time * 8000); + + }else if (counter + 2 < num_ptx){ // not many + esp_sleep_enable_timer_wakeup(light_sleep_time * 1000); + }else{ + esp_sleep_enable_timer_wakeup(1000); + } + // run interrupted by serial input "S" + interrupt_run = PAM_interrupt(allow_interrupt, false); + if (!interrupt_run) esp_light_sleep_start(); + interrupt_run = PAM_interrupt(allow_interrupt, true); + } + + adpd.STOP(); + if (!led_persist) AS_LED_OFF(); + digitalWrite(1, LOW); + } + pc += 1; + } + + if (interrupt_run){ + digitalWrite(STF_FLASH_PIN, LOW); + adpd.STOP(); + AS_LED_OFF(); + }; + + + if (json_output) { // one JSON object: {"env":[..],"fluo":[..],"s_630":[..],...} + Serial.print('{'); + send_env_json(d_env); + // fluo first: it reads d_fluor/d_fluoRef before send_json() drains them. + Serial.print(','); send_fluo_json(d_fluor, d_fluoRef); + Serial.print(','); d_fluor->send_json("s_630"); + Serial.print(','); d_fluoRef->send_json("r_630"); + if (d_sun->available) { Serial.print(','); d_sun->send_json("sun"); } + if (d_leaf->available) { Serial.print(','); d_leaf->send_json("leaf"); } + if (d_730->available) { Serial.print(','); d_730->send_json("s_730"); } + if (d_730Ref->available) { Serial.print(','); d_730Ref->send_json("r_730"); } + Serial.print('}'); + } else { + d_timing->put((uint32_t) run_tick_begin); // Change 3: run start tick (us) + d_timing->put((uint32_t) esp_timer_get_time()); // run end tick (us) + if (retain){ + // Parallel protocol: transfer ownership of the buffers to the async holder + // instead of streaming + freeing. The host fetches them later (cmd 24). + g_async.d_env = d_env; g_async.d_fluor = d_fluor; g_async.d_fluoRef = d_fluoRef; + g_async.d_sun = d_sun; g_async.d_leaf = d_leaf; g_async.d_730 = d_730; + g_async.d_730Ref = d_730Ref; g_async.d_timing = d_timing; + g_async.subsampling = subsampling; g_async.has_730 = (data_count[2] > 0); + g_async.allow_interrupt = allow_interrupt; + return 0; // holder owns the buffers now — do NOT delete + } + pam_send_results(d_env, d_fluor, d_fluoRef, d_sun, d_leaf, d_730, d_730Ref, + d_timing, subsampling, data_count[2] > 0, allow_interrupt); + } + + delete d_fluor; + delete d_fluoRef; + delete d_sun; + delete d_leaf; + delete d_730; + delete d_730Ref; + delete d_env; + delete d_timing; + + return 0; + +} + +// ── Async run-start / fetch (parallel trigger/poll/fetch protocol) ────────── +// Blocks to completion (like the synchronous run) but retains the results, so +// the host can trigger every sensor back-to-back and collect them afterwards. +int ambit_async_run_start(uint8_t length, uint8_t* arr, bool led_persist, bool allow_interrupt){ + ambit_async_clear(); // drop any stale, un-fetched result first + int rc = run_arr_type1(length, arr, led_persist, allow_interrupt, false, true /*retain*/); + if (rc == 0 && g_async.d_env != NULL){ + g_async.state = AMBIT_ASYNC_DONE; + } else { + ambit_async_clear(); // partial/failed run — free whatever stuck + g_async.state = AMBIT_ASYNC_ERROR; + } + return rc; +} + +// Stream the retained arrays to the ambyte over the existing AMBYTE FSM, then +// free them. Caller (cmd 24) has already written ESP_CMD_DONE and will write +// ESP_CMD_END after this returns. +int ambit_async_fetch(void){ + if (g_async.state != AMBIT_ASYNC_DONE || g_async.d_env == NULL) return -1; + uint8_t saved = CONNECTION_TYPE; + CONNECTION_TYPE = CONNECTION_TYPES::AMBYTE; + pam_send_results(g_async.d_env, g_async.d_fluor, g_async.d_fluoRef, g_async.d_sun, + g_async.d_leaf, g_async.d_730, g_async.d_730Ref, g_async.d_timing, + g_async.subsampling, g_async.has_730, g_async.allow_interrupt); + CONNECTION_TYPE = saved; + ambit_async_clear(); + return 0; +} + + +void adpd_trigger(void){ + digitalWrite(10, HIGH); + delayMicroseconds(1); + digitalWrite(10, LOW); +} + + + +int run_trigger_spacer(uint16_t length, uint8_t interval, bool change_act, uint8_t act, bool interrrupt){ + + if (length > 3000) return -1; + adpd.STOP(); + + if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1) conf_slow_FR_1(); + + digitalWrite(10, LOW); + adpd.gpio_config.GPIO0_cfg = 1; + adpd.gpio_config.SYNC_GPIO = 0; + adpd.gpio_config.EXT_SYNC_EN = 1; + adpd.gpio_setup(&(adpd.gpio_config)); + adpd_mode = ADPD_CONFIG_MODE::ARRAY_SLOW; + + gpio_sleep_set_direction(GPIO_NUM_10, GPIO_MODE_OUTPUT); + gpio_sleep_set_pull_mode(GPIO_NUM_10, GPIO_PULLDOWN_ONLY); + + + + // set to max possible bytes + uint8_t expected_readout = 8; + uint8_t expected_readout_bytes = expected_readout * 3; + const uint32_t _wait_time_ms = interval * 100; + const uint8_t num_integration = 1; + const unsigned int start_t0 = millis(); + const int64_t run_tick_begin = esp_timer_get_time(); // Change 3: TIMING - us at run start + + // data counter and buffer + // [sun-amb, leaf-ir, lit_leaf-ir, dark_leaf-ir, lit_leaf-ref, dark_leaf-ref] + uint32_t ret[expected_readout] = {0}; + uint16_t fifo_c = 0; + uint8_t watch_dog_timer = 0; + int32_t tmp_var = 0; + uint32_t buf_opt[4] = {0}; + uint32_t _tmparr = 0; + uint32_t read_fluor, read_fluoRef, read_sun, read_leaf, read_7, read_7Ref; + float_t leaf_temp = 0.0; + + unsigned int env_timer1 = millis(), trigger_timer = 0, expected_millis = 0; + int waiting_time = 0; + bool measure_temperature = false; + bool interrupt_run = false; + esp_sleep_enable_timer_wakeup(90000); + + dataclass *d_env = new dataclass; // + dataclass *d_fluor = new dataclass; // fluorescence signal + dataclass *d_fluoRef = new dataclass; // fluorescence reference + + dataclass *d_sun = new dataclass; // sun-side ambient + dataclass *d_leaf = new dataclass; // leaf-side ambient + dataclass *d_730 = new dataclass; // 730nm reflectance signal + dataclass *d_730Ref = new dataclass; // 730nm reference + dataclass *d_timing = new dataclass; // Change 3: [tick_begin, tick_end] controller ticks (us) + + int _func_ret = -1; + if (!(d_env->init(512))) goto del_classes; + if (!(d_timing->init(2))) goto del_classes; + if (!( (d_fluor->init(length)) && (d_fluoRef->init(length)) )) goto del_classes; + if (!( (d_sun->init(length)) && (d_leaf->init(length)) )) goto del_classes; + if (!( (d_730->init(length)) && (d_730Ref->init(length)) )) goto del_classes; + + + _tmparr = PAM_get_env(4, start_t0); + d_env->put(_tmparr); + leaf_temp = (int16_t) (_tmparr & 0xFFFF) / 100.0; + env_timer1 = millis(); + + + adpd.clear_fifo(); + adpd.run_freq(10); + adpd.num_ts(3); + adpd.RUN(); + delay(5); + + if(change_act){ + if (act == 0) AS_LED_OFF(); + if (act > 0) { + AS_LED_Current(act); + AS_LED_ON(); + } + } + + for (uint16_t n = 0; n < length; n++){ + fifo_c = 0; + adpd_trigger(); + trigger_timer = millis(); + expected_millis = trigger_timer + _wait_time_ms; + delay(1); + while (fifo_c != expected_readout_bytes){ + fifo_c = adpd.fifo_count(); + if (fifo_c >= expected_readout_bytes) break; + if (millis() - trigger_timer > 100) break; + } + if (fifo_c < expected_readout_bytes){ + ESP_LOGE(TAG, "NOT ENOUGH IN FIFO"); + break; + } + adpd.readfifo(expected_readout, 3, ret); + if (fifo_c > expected_readout_bytes){ + adpd.clear_fifo(); + ESP_LOGE(TAG, "Extra %d byte in FIFO", fifo_c - expected_readout_bytes); + } + + read_fluor = apply_adpd_calibration(ambit_calibration::S630, calc_signal(ret[2], ret[3], num_integration)); d_fluor->put(read_fluor); + read_fluoRef = apply_adpd_calibration(ambit_calibration::R630, calc_signal(ret[4], ret[5], num_integration)); d_fluoRef->put(read_fluoRef); + read_sun = apply_adpd_calibration(ambit_calibration::SUN, ret[0]); d_sun->put(read_sun); + read_leaf = apply_adpd_calibration(ambit_calibration::LEAF, ret[1]); d_leaf->put(read_leaf); + read_7 = apply_adpd_calibration(ambit_calibration::S730, ret[6]); d_730->put(read_7); + read_7Ref = apply_adpd_calibration(ambit_calibration::R730, ret[7]); d_730Ref->put(read_7Ref); + + + if (CONNECTION_TYPE == CONNECTION_TYPES::PLOTTING){ + Serial.printf("T:%2.3f,F:%3.4f,S:%d,R:%d,7:%d,7R:%d,Sun:%d,L:%d\n", leaf_temp, (float)read_fluor/(float)read_fluoRef, read_fluor, read_fluoRef, read_7, read_7Ref, read_sun, read_leaf); + Serial.flush(); + } + + + + if (interrupt_run) break; + if (millis() > expected_millis) continue; // overdue + waiting_time = expected_millis - millis(); + + if ((n % 8 == 7) && (waiting_time > 100)){ + if (millis() - env_timer1 > 2000){ + _tmparr = PAM_get_env(4, start_t0); + d_env->put(_tmparr); + leaf_temp = (int16_t) (_tmparr & 0xFFFF) / 100.0; + env_timer1 = millis(); + } + } + + + + + waiting_time = expected_millis - millis(); + while (waiting_time > 250){ + esp_sleep_enable_timer_wakeup((waiting_time - 50) * 1000); + interrupt_run = PAM_interrupt(interrrupt, false); + if (!interrupt_run) esp_light_sleep_start(); + interrupt_run = PAM_interrupt(interrrupt, true); + if (interrupt_run) break; + waiting_time = expected_millis - millis(); + } + if (interrupt_run) break; + waiting_time = expected_millis - millis(); + if (waiting_time > 1) delay(waiting_time); + } /// End of Loop + + + adpd.STOP(); + adpd.clear_fifo(); + + // Trigger-spacer always produces the full 7-channel set (subsampling=1, has_730=true), + // sent unconditionally. This path never runs as COMPUTER, so pam_send_results' COMPUTER + // branch is inert here; the AMBYTE branch is identical to the former inline block. + d_timing->put((uint32_t) run_tick_begin); // Change 3: run start tick (us) + d_timing->put((uint32_t) esp_timer_get_time()); // run end tick (us) + pam_send_results(d_env, d_fluor, d_fluoRef, d_sun, d_leaf, d_730, d_730Ref, + d_timing, 1, true, interrrupt); + + _func_ret = 0; + del_classes: + delete d_fluor; + delete d_fluoRef; + delete d_sun; + delete d_leaf; + delete d_730; + delete d_730Ref; + delete d_env; + delete d_timing; + + adpd.gpio_config.GPIO0_cfg = 0; + adpd.gpio_config.EXT_SYNC_EN = 0; + adpd.gpio_setup(&(adpd.gpio_config)); + adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; + return _func_ret; +} + + +int external_trigger_run(void){ + + adpd.STOP(); + conf_slow_FR_1(); + + adpd.led_config.driver1_current = adpd_current_config.I620; + adpd.led_config.led1_channel = LED_A; + adpd.led_config.driver2_current = 0; + adpd.led_config.led2_channel = LED_A; + adpd.SNR_config.TIA_gain_CH1 = adpd_gains_config.Fluo; + adpd.SNR_config.TIA_gain_CH2 = adpd_gains_config.FluoRef; + adpd.preset_config_2(1, 4); + + + digitalWrite(10, LOW); + adpd.gpio_config.GPIO0_cfg = 1; + adpd.gpio_config.SYNC_GPIO = 0; + adpd.gpio_config.EXT_SYNC_EN = 1; + adpd.gpio_setup(&(adpd.gpio_config)); + + + // set to max possible bytes + uint8_t expected_readout = 6, num_integration = 4; + uint8_t expected_readout_bytes = expected_readout * 3; + + // data counter and buffer + // [sun-amb, leaf-ir, lit_leaf-ir, dark_leaf-ir, lit_leaf-ref, dark_leaf-ref] + uint32_t ret[expected_readout] = {0}; + uint16_t fifo_c = 0; + uint32_t read_fluor, read_fluoRef, read_sun, read_leaf; + float_t leaf_temp = 0.0; + + adpd.clear_fifo(); + adpd.run_freq(10); + adpd.num_ts(2); + adpd.RUN(); + delay(5); + uint8_t unknown_input_counter = 0; + + unsigned int watchdog_timer = millis(), trigger_timer = 0, start_timer = millis(), temp_timer = millis(); + bool keep_running = true, do_measure = false, change_act = false; + double obj_T, chip_T; + char c, c1, c2; + + Serial.println("Run"); + + while(keep_running){ + + while (Serial.available() == 0){ + if (millis() - watchdog_timer > 30000){ + keep_running = false; + break; + } + delayMicroseconds(200); + } + if (!keep_running) break; + + while (Serial.available() > 0){ + c = Serial.read(); + if (c == 'G'){ + do_measure = true; + watchdog_timer = millis(); + }else if(c == 'E'){ + do_measure = false; + keep_running = false; + }else if(c == 'A'){ + delay(1); + c1 = 0; + if (Serial.available() > 0){ + c1 = Serial.read(); + change_act = true; + } + }else if(c == 'T'){ + temp_timer = millis(); + mlx_measure(&obj_T, &chip_T); + Serial.printf("T:%d,o:%.3f,a:%.3f,d:%d\n", millis() - start_timer, obj_T, chip_T, millis() - temp_timer); + Serial.flush(); + watchdog_timer = millis(); + continue; + } + else{ + unknown_input_counter += 1; + if (unknown_input_counter > 200) keep_running = false; + } + } + + if (!keep_running) break; + if (change_act){ + change_act = false; + if (c1 > 3){ + AS_LED_Current(c1); + AS_LED_ON(); + }else{ + AS_LED_OFF(); + } + } + if (!do_measure) continue; + + + fifo_c = 0; + adpd_trigger(); + do_measure = false; + trigger_timer = millis(); + delay(1); + while (fifo_c != expected_readout_bytes){ + fifo_c = adpd.fifo_count(); + if (fifo_c >= expected_readout_bytes) break; + if (millis() - trigger_timer > 100) break; + } + if (fifo_c < expected_readout_bytes){ + Serial.println("NOT ENOUGH IN FIFO"); + break; + } + adpd.readfifo(expected_readout, 3, ret); + if (fifo_c > expected_readout_bytes){ + adpd.clear_fifo(); + Serial.printf("Extra %d byte in FIFO", fifo_c - expected_readout_bytes); + } + + read_fluor = apply_adpd_calibration(ambit_calibration::S630, calc_signal(ret[2], ret[3], num_integration)); + read_fluoRef = apply_adpd_calibration(ambit_calibration::R630, calc_signal(ret[4], ret[5], num_integration)); + read_sun = apply_adpd_calibration(ambit_calibration::SUN, ret[0]); + read_leaf = apply_adpd_calibration(ambit_calibration::LEAF, ret[1]); + + + + Serial.printf("T:%d,S:%d,R:%d,F:%d,B:%d\n", millis() - start_timer, read_fluor, read_fluoRef, read_sun, read_leaf); + Serial.flush(); + } /// End of Loop + + + adpd.STOP(); + adpd.clear_fifo(); + + adpd.gpio_config.GPIO0_cfg = 0; + adpd.gpio_config.EXT_SYNC_EN = 0; + adpd.gpio_setup(&(adpd.gpio_config)); + + AS_LED_Current(0); + AS_LED_OFF(); + + Serial.println("Stop"); + + return 0; +} + + +int external_trigger_run_Flash(unsigned int gate_time, unsigned int dt, const uint16_t num){ + adpd.STOP(); + const uint8_t _NUM_TS = 8; + + adpd.led_config.driver1_current = 80; + adpd.led_config.led1_channel = LED_A; + // LED 2A = 730nm + adpd.led_config.driver2_current = 0; + adpd.led_config.led2_channel = LED_A; + adpd.SNR_config.TIA_gain_CH1 = 1; + adpd.SNR_config.TIA_gain_CH2 = 5; + + for (uint8_t i = 0; i < 12; i++){ + adpd.preset_config_ext_fast(i, 2); + } + + digitalWrite(STF_FLASH_PIN, LOW); + digitalWrite(10, LOW); + adpd.gpio_config.GPIO0_cfg = 1; + adpd.gpio_config.SYNC_GPIO = 0; + adpd.gpio_config.EXT_SYNC_EN = 1; + adpd.gpio_setup(&(adpd.gpio_config)); + AS_LED_OFF(); + AS_LED_Current(dt); + + + // set to max possible bytes + uint8_t expected_readout = _NUM_TS * 4, num_integration = 2; + uint8_t expected_readout_bytes = expected_readout * 3; + + // data counter and buffer + + uint32_t ret[expected_readout] = {0}; + uint16_t fifo_c = 0; + uint32_t read_fluor, read_fluoRef, read_sun, read_leaf; + float_t leaf_temp = 0.0; + + adpd.clear_fifo(); + adpd.run_freq(10); + adpd.num_ts(_NUM_TS); + adpd.RUN(); + delay(5); + uint16_t _counter = 0; + + unsigned int watchdog_timer = millis(), trigger_timer = 0, start_timer = millis(), temp_timer = millis(); + bool keep_running = true, do_measure = false, change_act = false; + double obj_T, chip_T; + char c, c1, c2; + + dataclass *d_fluor = new dataclass; // fluorescence signal + dataclass *d_fluoRef = new dataclass; // fluorescence reference + + + + Serial.println("Run"); + unsigned long timer1 = micros(); + unsigned long timer2 = micros(); + unsigned int flash_duration = expected_readout; + uint16_t _num_sample = num < 20 ? num * _NUM_TS : 20 * _NUM_TS + (num - 20); + + + + if (!( (d_fluor->init(_num_sample + 1)) && (d_fluoRef->init(_num_sample + 1)) )) goto del_classes; + while(keep_running){ + _counter += 1; + if (_counter > num) break; + if (!keep_running) break; + fifo_c = 0; + //if ((_counter == 5) && (dt > 0)) AS_LED_ON(); + adpd_trigger(); + timer1 = micros(); + delayMicroseconds(1000); + fifo_c = adpd.fifo_count(); + while (fifo_c != expected_readout_bytes){ + fifo_c = adpd.fifo_count(); + if (fifo_c >= expected_readout_bytes) break; + if (micros() - timer1 > 10000) break; + } + //Serial.printf("%ld: %d\n", micros() - timer1, fifo_c); + + if (fifo_c < expected_readout_bytes){ + Serial.printf("NOT ENOUGH IN FIFO: %d < %d\n", fifo_c, expected_readout_bytes); + break; + } + + + if ((gate_time > 0) && (flash_duration > 0) && (_counter > 2)) digitalWrite(STF_FLASH_PIN, HIGH); + //if ((_counter == 15) && (dt > 0)) AS_LED_OFF(); + + timer1 = micros(); + for (uint8_t r = 0; r < expected_readout; r++){ + adpd.readfifo(1, 3, ret + r); + if (flash_duration == r) digitalWrite(STF_FLASH_PIN, LOW); + } + //Serial.println(micros() - timer1); + + + if (_counter <= 20){ + for (uint8_t r = 0; r < _NUM_TS; r++){ + d_fluor->put(apply_adpd_calibration(ambit_calibration::S630, + calc_signal(ret[0 + r * 4], ret[1 + r * 4], num_integration))); + d_fluoRef->put(apply_adpd_calibration(ambit_calibration::R630, + calc_signal(ret[2 + r * 4], ret[3 + r * 4], num_integration))); + } + }else{ + read_fluor = 0; read_fluoRef = 0; + for (uint8_t r = 0; r < _NUM_TS; r++){ + read_fluor += calc_signal(ret[0 + r * 4], ret[1 + r * 4], num_integration); + read_fluoRef += calc_signal(ret[2 + r * 4], ret[3 + r * 4], num_integration); + } + d_fluor->put(apply_adpd_calibration(ambit_calibration::S630, read_fluor / _NUM_TS)); + d_fluoRef->put(apply_adpd_calibration(ambit_calibration::R630, read_fluoRef / _NUM_TS)); + if (_counter == 100) flash_duration = 4; + if (_counter == 300) flash_duration = 2; + if (_counter == 500) flash_duration = 1; + if (_counter == 700) flash_duration = 32; + } + + + if (fifo_c > expected_readout_bytes){ + fifo_c = adpd.fifo_count(); + if (fifo_c > 0){ + adpd.clear_fifo(); + digitalWrite(STF_FLASH_PIN, LOW); + Serial.printf("Extra %d byte in FIFO\n", fifo_c - expected_readout_bytes); + } + } + + // flash_duration = gate_time - (micros() - timer1); + // if ((flash_duration > 1) && (flash_duration < 500)) delayMicroseconds(flash_duration - 1); + // digitalWrite(STF_FLASH_PIN, LOW); + // + + + //Serial.printf("T:%d,S:%d,R:%d,F:%d,B:%d\n", millis() - start_timer, read_fluor, read_fluoRef, read_sun-65000, read_leaf-65000); + // Serial.printf("T:%d,S:%d,R:%d\n", millis() - start_timer, read_fluor, read_fluoRef); + // flash_duration = gate_time - (micros() - timer1); + // if (flash_duration > 2){ + // digitalWrite(STF_FLASH_PIN, HIGH); + // delayMicroseconds(flash_duration - 1); + // } + + + + digitalWrite(STF_FLASH_PIN, LOW); + } /// End of Loop + //Serial.printf("%f for %dpoints\n", float(micros() - timer2)/num, num); + AS_LED_OFF(); + digitalWrite(STF_FLASH_PIN, LOW); + adpd.STOP(); + adpd.clear_fifo(); + + + + adpd.gpio_config.GPIO0_cfg = 0; + adpd.gpio_config.EXT_SYNC_EN = 0; + adpd.gpio_setup(&(adpd.gpio_config)); + + AS_LED_Current(0); + AS_LED_OFF(); + digitalWrite(STF_FLASH_PIN, LOW); + + for (uint16_t n = 2; n < _num_sample; n++){ + Serial.printf("%d,%d,%.4f\n", d_fluor->arr[n], d_fluoRef->arr[n], ((float) d_fluor->arr[n]) / d_fluoRef->arr[n]); + } + + del_classes: + delete d_fluor; + delete d_fluoRef; + + + return 0; +} + + + + +int MPF(uint16_t mode, uint16_t dc_current){ + if (adpd_gains_config.init == false) ESP_LOGE(TAG, "Gain preset not initized, use default!"); + if (adpd_current_config.init == false) ESP_LOGE(TAG, "Current preset not initized, use default!"); + return MPF(mode, adpd_current_config.I620, dc_current, adpd_gains_config.Fluo, adpd_gains_config.FluoRef); + +} + + +int MPF(uint16_t mode, uint16_t current, uint16_t dc_current, uint8_t sign_gain, uint8_t ref_gain){ + + + const uint8_t num_integration = 1; + const uint16_t _data_size = 1080; + + ESP_LOGV(TAG, "RUN MPF with mode:%d, pulse current:%d, DC current %d", mode, current, dc_current); + + dataclass *d_fluor = new dataclass; // fluorescence signal + dataclass *d_fluoRef = new dataclass; // fluorescence reference + if (!( (d_fluor->init(_data_size)) && (d_fluoRef->init(_data_size)) )) return -1; + ESP_LOGV(TAG, "Memory allocation completed"); + + + adpd.STOP(); + adpd.run_freq(10); + adpd.clear_fifo(); + adpd.gpio_config.GPIO0_cfg = 1; + adpd.gpio_config.SYNC_GPIO = 0; + adpd.gpio_config.EXT_SYNC_EN = 1; + adpd.gpio_setup(&(adpd.gpio_config)); + + adpd.led_config.driver1_current = current; + adpd.led_config.driver2_current = 0; + adpd.SNR_config.TIA_gain_CH2 = ref_gain; + adpd.SNR_config.TIA_gain_CH1 = sign_gain; + + adpd_mode = ADPD_CONFIG_MODE::MPF_MODE; + + + // data buffers + uint32_t ret[48] = {0}; + uint16_t fifo_c = 0; + int32_t tmp_var = 0; + uint32_t avg_arr1[12] = {0}; + uint32_t avg_buf[4] = {0}; + uint16_t as_current = 255; + uint8_t expected_readout = 12 * 4; // 12 timeslots, 4 data per ts + uint16_t decay_interval = 1; + // PHASE 0---------------------------------------------- + // Apply a baseline without actinic + // fixed at 2 Hz x 20 pts + if (mode == 0){ + AS_LED_OFF(); + AS_LED_Current(as_current); + adpd.preset_config_ext_fast(0); + expected_readout = 4; + adpd.RUN(); + delayMicroseconds(1500); + adpd_trigger(); + delayMicroseconds(1500); + ESP_LOGV(TAG, "Phase-0 Started"); + for (uint8_t i = 0; i < 20; i++){ + adpd_trigger(); + adpd.readfifo(expected_readout, 3, ret); + for (uint8_t m = 0; m < 4; m++){ // sign-dark, sign-lit, ref-dark, ref lit + avg_buf[m] = ret[m]; + } + d_fluor->put(apply_adpd_calibration(ambit_calibration::S630, + calc_signal(avg_buf[0], avg_buf[1], num_integration))); + d_fluoRef->put(apply_adpd_calibration(ambit_calibration::R630, + calc_signal(avg_buf[2], avg_buf[3], num_integration))); + delay(500); + } + ESP_LOGV(TAG, "Phase-0 Completed"); + } + + + // PHASE 1--------------------------------------------------- + // 12 timeslots + // Rapid induction, all timeslot saved + ESP_LOGV(TAG, "Phase-1 Config"); + expected_readout = 48; + for (uint8_t i = 0; i < 12; i++){ + adpd.preset_config_ext_fast(i); + } + adpd.RUN(); + // AS Light kept until this point + AS_LED_OFF(); + AS_LED_Current(0); + delay(2); + ESP_LOGV(TAG, "Phase-1 LED ON"); + // Light ON + AS_LED_ON(); + delayMicroseconds(1); + adpd_trigger(); + delayMicroseconds(1500); + + for (uint8_t i = 0; i < 4; i++){ + adpd_trigger(); + adpd.readfifo(expected_readout, 3, ret); + for (uint8_t j = 0; j < 12; j++){ + + d_fluor->put(apply_adpd_calibration(ambit_calibration::S630, + calc_signal(ret[0 + j * 4], ret[1 + j * 4], num_integration))); + d_fluoRef->put(apply_adpd_calibration(ambit_calibration::R630, + calc_signal(ret[2 + j * 4], ret[3 + j * 4], num_integration))); + } + delayMicroseconds(500); + } + ESP_LOGV(TAG, "Phase-1 Completed; Phase-2 start"); + + // PHASE 2------------------------------------ + // 300ms induction, 200x12ts, 200 final pts + for (uint8_t i = 0; i < 200; i++){ + adpd_trigger(); + adpd.readfifo(expected_readout, 3, ret); + // reset avg arrays + for (uint8_t m = 0; m < 4; m++){ // find median for sign-dark, sign-lit, ref-dark, ref lit + memset(avg_arr1, 0, sizeof(avg_arr1)); + for (uint8_t n = 0; n < 12; n++){ // put 12 ts into sorting array + sorted_insert(avg_arr1, 12, ret[m + n * 4]); + } + avg_buf[m] = (avg_arr1[5] + avg_arr1[6] + avg_arr1[7]) / 3; // pick the middle 3 numbers and average + } + + + // Serial.printf("P2, %d, %d, %d\n", avg_buf[0], avg_buf[1], calc_signal(avg_buf[0], avg_buf[1], num_integration)); + // Serial.printf("P2, %d, %d, %d\n", avg_buf[2], avg_buf[3], calc_signal(avg_buf[2], avg_buf[3], num_integration)); + + + d_fluor->put(apply_adpd_calibration(ambit_calibration::S630, + calc_signal(avg_buf[0], avg_buf[1], num_integration))); + // Serial.println(d_fluor->arr[d_fluor->write_ptr - 1]); + d_fluoRef->put(apply_adpd_calibration(ambit_calibration::R630, + calc_signal(avg_buf[2], avg_buf[3], num_integration))); + // Serial.println(d_fluoRef->arr[d_fluoRef->write_ptr - 1]); + delayMicroseconds(500); + } + + ESP_LOGV(TAG, "Phase-2 Completed; Phase-3 Start"); + + + // PHASE 3------------------------------------ + // 150ms induction X cycles, 100x12ts, 50 final pts + for (uint8_t j = 0; j < 8; j++){ + AS_LED_Current(220 - j * 30); + ESP_LOGV(TAG, "LED set to %d", 220 - j * 30); + memset(avg_buf, 0, sizeof(avg_buf)); + for (uint8_t i = 0; i < 40; i++){ + adpd_trigger(); + adpd.readfifo(expected_readout, 3, ret); + // reset avg arrays + for (uint8_t m = 0; m < 4; m++){ // find median for sign-dark, sign-lit, ref-dark, ref lit + memset(avg_arr1, 0, sizeof(avg_arr1)); + for (uint8_t n = 0; n < 12; n++){ // put 12 ts into sorting array + sorted_insert(avg_arr1, 12, ret[m + n * 4]); + } + avg_buf[m] += (avg_arr1[5] + avg_arr1[6] + avg_arr1[7]) / 3; // pick the middle 3 numbers and average + } + + if (i % 2 == 1){ + d_fluor->put(apply_adpd_calibration(ambit_calibration::S630, + calc_signal(avg_buf[0] / 2, avg_buf[1] / 2, num_integration))); + d_fluoRef->put(apply_adpd_calibration(ambit_calibration::R630, + calc_signal(avg_buf[2] / 2, avg_buf[3] / 2, num_integration))); + memset(avg_buf, 0, sizeof(avg_buf)); + } + delayMicroseconds(500); + } + } + + ESP_LOGV(TAG, "Phase-3 Completed; Phase-4 Start"); + // PHASE 4------------------------------------ + AS_LED_Current(as_current); + // prepare for single timeslot decay kinetic + expected_readout = 4; + adpd.preset_config_ext_fast(0); + adpd.clear_fifo(); + adpd.RUN(); + delayMicroseconds(1000); + adpd_trigger(); + delayMicroseconds(1500); + memset(avg_buf, 0, sizeof(avg_buf)); + + for (uint8_t i = 0; i < 200; i++){ + adpd_trigger(); + adpd.readfifo(expected_readout, 3, ret); + for (uint8_t m = 0; m < 4; m++){ // sign-dark, sign-lit, ref-dark, ref lit + avg_buf[m] += ret[m]; + } + if (i % 4 == 3){ + + + + // Serial.printf("P4, %d, %d, %d\n", avg_buf[0]/4, avg_buf[1]/4, calc_signal(avg_buf[0]/4, avg_buf[1]/4, num_integration)); + // Serial.printf("P4, %d, %d, %d\n", avg_buf[2]/4, avg_buf[3]/4, calc_signal(avg_buf[2]/4, avg_buf[3]/4, num_integration)); + + + d_fluor->put(apply_adpd_calibration(ambit_calibration::S630, + calc_signal(avg_buf[0] / 4, avg_buf[1] / 4, num_integration))); + // Serial.println(d_fluor->arr[d_fluor->write_ptr - 1]); + d_fluoRef->put(apply_adpd_calibration(ambit_calibration::R630, + calc_signal(avg_buf[2] / 4, avg_buf[3] / 4, num_integration))); + // Serial.println(d_fluoRef->arr[d_fluoRef->write_ptr - 1]); + memset(avg_buf, 0, sizeof(avg_buf)); + } + if (i == 149) { + if (mode == 0) AS_LED_OFF(); // dark mode, actinic OFF + if (mode == 1) AS_LED_Current(dc_current); // light mode, actinic set + } + delayMicroseconds(1400); + } + + ESP_LOGV(TAG, "Phase-4 Completed"); +// PHASE -1 --------------------------- +// Dark decay with increasing interval + if (mode == 0){ + ESP_LOGV(TAG, "Phase-minus1 Started"); + AS_LED_OFF(); + for (uint8_t i = 0; i < 160; i++){ + adpd_trigger(); + adpd.readfifo(expected_readout, 3, ret); + for (uint8_t m = 0; m < 4; m++){ // sign-dark, sign-lit, ref-dark, ref lit + avg_buf[m] += ret[m]; + } + if (i % 4 == 3){ + d_fluor->put(apply_adpd_calibration(ambit_calibration::S630, + calc_signal(avg_buf[0] / 4, avg_buf[1] / 4, num_integration))); + d_fluoRef->put(apply_adpd_calibration(ambit_calibration::R630, + calc_signal(avg_buf[2] / 4, avg_buf[3] / 4, num_integration))); + memset(avg_buf, 0, sizeof(avg_buf)); + decay_interval += 5; + } + delayMicroseconds(1400); + delay(decay_interval); + } + } + + ESP_LOGV(TAG, "Measurement Completed"); + // Completed + + adpd.STOP(); + + if (CONNECTION_TYPE == CONNECTION_TYPES::PLOTTING){ + uint16_t l = d_fluor->get_length(); + uint32_t ploter1, ploter2; + for (uint16_t i = 0; i < l; i++){ + ploter1 = d_fluor->pop(); + ploter2 = d_fluoRef->pop(); + Serial.printf("F:%3.4f,S:%d,R:%d\n", (float)ploter1/(float)ploter2, ploter1, ploter2); + } + }else if (CONNECTION_TYPE == CONNECTION_TYPES::AMBYTE){ + d_fluor->fsm_send_esp(0); + d_fluoRef->fsm_send_esp(1); + } + else{ + d_fluor->send_serial("s_630"); + d_fluoRef->send_serial("r_630"); + Serial.println("Data sent"); + } + + ESP_LOGV(TAG, "All Completed"); + + + adpd.gpio_config.GPIO0_cfg = 0; + adpd.gpio_config.SYNC_GPIO = 0; + adpd.gpio_config.EXT_SYNC_EN = 0; + adpd.gpio_setup(&(adpd.gpio_config)); + + delete d_fluor; + delete d_fluoRef; + return 0; +} + + +/* Leaf temperature as centi-degC (signed int16 in the low 16 bits). The old + * time/type bit-packing is gone: env arrays sent to the ambyte are now a plain + * centi-degC series. Absolute time comes from the ambyte RTC; within-run point + * timing from the run freq. Only mode 4 (leaf temp) is emitted now; mode/t0 are + * kept so call sites are unchanged. */ +uint32_t PAM_get_env(uint8_t mode, unsigned int t0){ + (void) t0; + if (mode == 4){ + int16_t centi = (int16_t) (mlx_measure() * 100.0); + return (uint32_t) (uint16_t) centi; + } + return 0; +} + +uint32_t PAM_retrieve_env(uint32_t r, uint8_t* mode, float_t* data_f, int16_t* data_i){ + int16_t data = (int16_t) (r & 0x00000FFF); + uint8_t d_type = (uint8_t) ((r & 0x0000F000) >> 12); + uint32_t t = ((r & 0xFFFF0000) >> 10); + + if (d_type < 4){ + t += data; + if (mode != NULL) *mode = d_type; + }else if (d_type == 4){ // temperature + if (data_f != NULL) *data_f = (data / 20.0 - 20); + if (mode != NULL) *mode = d_type; } + + + return t; +} + + + + + +int sandbox(uint8_t I620, uint8_t g1, uint8_t g2){ + + + // variables for each trace + + // data counter and buffer + // [sun-amb, leaf-ir, lit_leaf-ir, dark_leaf-ir, lit_leaf-ref, dark_leaf-ref] + uint32_t ret[16] = {0}; + uint16_t fifo_c = 0; + uint16_t fifo_c1 = 0; + + + adpd.STOP(); + adpd.gpio_config.GPIO0_cfg = 1; + adpd.gpio_config.SYNC_GPIO = 0; + adpd.gpio_config.EXT_SYNC_EN = 1; + adpd.gpio_setup(&(adpd.gpio_config)); + + adpd.led_config.driver1_current = I620; + adpd.led_config.driver2_current = 0; + adpd.SNR_config.TIA_gain_CH2 = 5; + adpd.SNR_config.TIA_gain_CH1 = 1; + + adpd.preset_config_ext_fast(0, 1); + adpd.preset_config_ext_fast(1, 2); + adpd.preset_config_ext_fast(2, 3); + adpd.preset_config_ext_fast(3, 4); + + adpd.run_freq(10); + adpd.clear_fifo(); + + adpd.RUN(); + delay(1); + int64_t timer = 0; + + + for (uint16_t i = 0; i < 200; i++){ + digitalWrite(10, HIGH); + delayMicroseconds(1); + digitalWrite(10, LOW); + timer = esp_timer_get_time(); + if (i > 0){ + + adpd.readfifo(16, 3, ret); + + Serial.printf("Data:%d,%d,%d,%d,", ret[0], ret[1], ret[2],ret[3]); + Serial.printf("%d,%d,%d,%d,", ret[4], ret[5], ret[6],ret[7]); + Serial.printf("%d,%d,%d,%d,", ret[8], ret[9], ret[10],ret[11]); + Serial.printf("%d,%d,%d,%d\n", ret[12], ret[13], ret[14],ret[15]); + + + delay(100); + + }else{ + delayMicroseconds(1500); + } + //Serial.println(esp_timer_get_time() - timer); + + } + //Serial.println(adpd.fifo_count()); + adpd.readfifo(16, 3, ret); + + adpd.STOP(); + + + return 0; + +} + + + +static bool PAM_interrupt(bool enable, bool check_sleep){ + uint8_t ret = 0; + if (!enable) return false; + if (check_sleep){ // after light sleep + if (esp_sleep_get_wakeup_cause() == 8){ // wake up serial + Serial.flush(); + ret = serial_read_until(177, 0, 0, 25, true); + } + }else{ + ret = serial_read_until(177, 0, 0, 15, true); + } + if (ret == 1) return true; + return false; +} + + +void actinic_test(uint8_t act1, uint8_t act2, uint8_t t1, uint8_t t2){ + AS_LED_Current(act1); + AS_LED_ON(); + delay(t1 * 1000); + AS_LED_Current(act2); + delay(3000); + AS_LED_OFF(); + AS_LED_Current(0); + + +} + + + + +int fluor_offset_test(uint8_t current, uint8_t num_integ, uint8_t lit_offset, uint8_t dark1_offset, uint8_t dark2_offset, uint8_t pulse_offset, uint8_t pulse_duration){ + + // Setup timeslot 2: Fluor and Ref channels, 4 x 3 bytes + // LED 1A = 620nm + adpd.led_config.driver1_current = current; + adpd.led_config.led1_channel = LED_A; + // LED 2A = 730nm + adpd.led_config.driver2_current = 0; + adpd.led_config.led2_channel = LED_A; + adpd.SNR_config.TIA_gain_CH1 = 1; + adpd.SNR_config.TIA_gain_CH2 = 5; + adpd.preset_config_2x(0, num_integ, lit_offset, dark1_offset, dark2_offset, pulse_offset, pulse_duration); + + adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; + + return 0; +} + + +int fluor_offset(uint32_t* fret){ + if (fret == nullptr) return FLUOR_OFFSET_INVALID_ARGUMENT; + + if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1){ + const int config_result = conf_slow_FR_1(); + if (config_result != jii::adpd6000::kOk) return FLUOR_OFFSET_CONFIG_ERROR; + ESP_LOGW(TAG, "Run array was not configured!"); + } + + constexpr uint8_t expected_readout = 8; + constexpr uint16_t expected_readout_bytes = expected_readout * 3; + constexpr uint8_t num_integration = 1; + constexpr uint16_t num_points = 64; + constexpr uint8_t repeat_count = 4; + // Four 64-point blocks at 100 Hz normally take about 2.6 seconds. Five + // seconds tolerates scheduling/FIFO jitter but bounds a missing or failed + // sensor. wrap_safe_timeout_elapsed() remains correct across millis() wrap. + constexpr uint32_t acquisition_timeout_ms = 5000U; + const uint32_t acquisition_start = millis(); + + auto fail_after_stop = [](int result) { + adpd.STOP(); + return result; + }; + + if (adpd.STOP() != jii::adpd6000::kOk) return FLUOR_OFFSET_ADPD_ERROR; + if (adpd.run_freq(100) != jii::adpd6000::kOk || + adpd.num_ts(3) != jii::adpd6000::kOk){ + return fail_after_stop(FLUOR_OFFSET_ADPD_ERROR); + } + AS_LED_OFF(); + AS_LED_Current(0); + + uint32_t ret_fluor = 0, ret_fluoRef = 0, ret_sun = 0; + uint32_t ret_leaf = 0, ret_r730 = 0, ret_r730Ref = 0; + + for (uint8_t repeat = 0; repeat < repeat_count; ++repeat){ + // STOP does not guarantee the FIFO is empty. Clear it for every block so + // a queued frame from the prior block cannot bias the next average. + if (adpd.clear_fifo() != jii::adpd6000::kOk || + adpd.RUN() != jii::adpd6000::kOk){ + return fail_after_stop(FLUOR_OFFSET_ADPD_ERROR); + } + delay(1); + + uint32_t fluor = 0, fluoRef = 0, sun = 0; + uint32_t leaf = 0, r730 = 0, r730Ref = 0; + uint16_t counter = 0; + + while (counter < num_points){ + if (ambit_calibration::wrap_safe_timeout_elapsed( + acquisition_start, millis(), acquisition_timeout_ms)){ + return fail_after_stop(FLUOR_OFFSET_TIMEOUT); + } + + uint16_t fifo_count = 0; + if (adpd.fifo_count(&fifo_count) != jii::adpd6000::kOk){ + return fail_after_stop(FLUOR_OFFSET_ADPD_ERROR); + } + if (fifo_count < expected_readout_bytes){ + delay(1); + continue; + } + + while (fifo_count >= expected_readout_bytes && counter < num_points){ + uint32_t samples[expected_readout] = {0}; + if (adpd.readfifo(expected_readout, 3, samples) != jii::adpd6000::kOk){ + return fail_after_stop(FLUOR_OFFSET_ADPD_ERROR); + } + fifo_count -= expected_readout_bytes; + + // 0: sun-vis; 1: leaf-ir; 2/3: fluo signal dark/lit; + // 4/5: fluo reference dark/lit; 6/7: 730 signal/reference. + fluor += calc_signal(samples[2], samples[3], num_integration); + fluoRef += calc_signal(samples[4], samples[5], num_integration); + r730 += samples[6]; + r730Ref += samples[7]; + sun += samples[0]; + leaf += samples[1]; + ++counter; + } + } + + if (adpd.STOP() != jii::adpd6000::kOk) return FLUOR_OFFSET_ADPD_ERROR; + ret_fluor += fluor / counter; + ret_fluoRef += fluoRef / counter; + ret_sun += sun / counter; + ret_leaf += leaf / counter; + ret_r730 += r730 / counter; + ret_r730Ref += r730Ref / counter; + } + + // Leave the caller-provided vector untouched unless the full acquisition + // succeeded. Callers can therefore safely gate NVS writes on this result. + fret[0] = ret_fluor / repeat_count; + fret[1] = ret_fluoRef / repeat_count; + fret[2] = ret_sun / repeat_count; + fret[3] = ret_leaf / repeat_count; + fret[4] = ret_r730 / repeat_count; + fret[5] = ret_r730Ref / repeat_count; + return FLUOR_OFFSET_OK; + +} diff --git a/PAM.h b/src/PAM.h similarity index 57% rename from PAM.h rename to src/PAM.h index b1dac58..aac3091 100644 --- a/PAM.h +++ b/src/PAM.h @@ -41,10 +41,30 @@ int fluor_offset_test(uint8_t current, uint8_t num_integ, uint8_t lit_offset, ui int MPF(uint16_t mode, uint16_t current, uint16_t dc_current, uint8_t sign_gain, uint8_t ref_gain); int MPF(uint16_t mode, uint16_t dc_current); int run_arr_type1(uint8_t length, uint8_t* arr, bool led_persist); -int run_arr_type1(uint8_t length, uint8_t* arr, bool led_persist, bool allow_interrupt); +// retain=true: keep the result buffers alive in the async holder (no FSM/serial +// send, no delete) so a later FETCH can stream them — used by the parallel +// trigger/poll/fetch protocol. Default false = legacy synchronous send+free. +int run_arr_type1(uint8_t length, uint8_t* arr, bool led_persist, bool allow_interrupt, bool json_output = false, bool retain = false); int run_trigger_spacer(uint16_t length, uint8_t interval, bool change_act, uint8_t act, bool interrrupt); + +// ── Async (trigger/poll/fetch) result holder — parallel measurement protocol ── +// A run started via ambit_async_run_start() blocks the C3 to completion (as the +// synchronous run does today) but RETAINS its result arrays instead of streaming +// them, so the host can trigger all sensors, then poll state and fetch later. +enum { AMBIT_ASYNC_IDLE = 0, AMBIT_ASYNC_DONE = 1, AMBIT_ASYNC_ERROR = 2 }; +int ambit_async_run_start(uint8_t length, uint8_t* arr, bool led_persist, bool allow_interrupt); +uint8_t ambit_async_get_state(void); // AMBIT_ASYNC_IDLE | DONE | ERROR +int ambit_async_fetch(void); // stream retained arrays (AMBYTE FSM), then clear; 0 ok +void ambit_async_clear(void); // free any retained buffers, state -> IDLE uint32_t PAM_get_env(uint8_t mode, unsigned int t0); uint32_t PAM_retrieve_env(uint32_t r, uint8_t* mode, float_t* data_f = NULL, int16_t* data_i = NULL); +enum FluorOffsetResult { + FLUOR_OFFSET_OK = 0, + FLUOR_OFFSET_INVALID_ARGUMENT = -1, + FLUOR_OFFSET_CONFIG_ERROR = -2, + FLUOR_OFFSET_ADPD_ERROR = -3, + FLUOR_OFFSET_TIMEOUT = -4, +}; int fluor_offset(uint32_t* ret); enum ADPD_CONFIG_MODE { MPF_MODE, @@ -54,4 +74,4 @@ enum ADPD_CONFIG_MODE { FUTURE }; -#endif \ No newline at end of file +#endif diff --git a/src/adpd/ADPD_platform_ops.cpp b/src/adpd/ADPD_platform_ops.cpp deleted file mode 100644 index 4b3bfaa..0000000 --- a/src/adpd/ADPD_platform_ops.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "driver/spi_common.h" -#include "driver/spi_master.h" -#include "../pin_config.h" - -#include "lib/ADPD6000/adi_adpd6000.h" - - - - - - -uint8_t check_adpd(spi_device_handle_t handle){ - /// @brief Check ADPD connected - /// @param ADPD number - /// @return - esp_err_t err = 1; - uint16_t ret = 1; - - spi_transaction_t check_adpd = { - .addr = 0x0010, - .length = 16, - .rxlength = 16, - .rx_buffer = &ret, - }; - - err = spi_device_polling_transmit(handle, &check_adpd); - - if (err != ESP_OK) {Serial.print("SPI communication failed: "); Serial.println(esp_err_to_name(err));return 1;} - ret = SPI_SWAP_DATA_RX(ret, 16); - Serial.println(ret); - - return 1; - -} - - -int32_t spi_read(void* user_data, uint8_t *rd_buf, uint32_t rd_len, uint8_t *wr_buf, uint32_t wr_len){ - - spi_device_handle_t handle = * ((spi_device_handle_t*) user_data); - - esp_err_t err; - uint16_t addr = 0x0000; - addr |= wr_buf[0] << 8; - addr |= wr_buf[1]; - - spi_transaction_t spi_read_and_write = { - .addr = addr, - .length = rd_len * 8, - .rxlength = rd_len * 8, - .rx_buffer = rd_buf - }; - err = spi_device_polling_transmit(handle, &spi_read_and_write); - if (err != ESP_OK) Serial.println(esp_err_to_name(err)); - return 0; - -} -int32_t spi_write(void* user_data, uint8_t *wr_buf, uint32_t len){ - - spi_device_handle_t handle = * ((spi_device_handle_t*) user_data); - - esp_err_t err; - uint16_t addr = 0x0000; - addr |= wr_buf[0] << 8; - addr |= wr_buf[1]; - - spi_transaction_t spi_read_and_write = { - .addr = addr, - .length = (len - 2)<<3, - .rxlength = (len - 2)<<3, - .tx_buffer = &wr_buf[2], - .rx_buffer = &wr_buf[2] - }; - err = spi_device_polling_transmit(handle, &spi_read_and_write); - if (err != ESP_OK) Serial.println(esp_err_to_name(err)); - return 0; -} - -int32_t log_print(void* user_data, char *string){ - Serial.println(string); - return 0; -} - diff --git a/src/adpd/lib/ADPD6000/adi_adpd6000.h b/src/adpd/lib/ADPD6000/adi_adpd6000.h deleted file mode 100644 index c734385..0000000 --- a/src/adpd/lib/ADPD6000/adi_adpd6000.h +++ /dev/null @@ -1,1983 +0,0 @@ -/*! - * @brief adi adpd6000 header file - * @copyright Copyright (c) 2021 - Analog Devices Inc. All Rights Reserved. - */ - -/*! - * @addtogroup adi_adpd6000_sdk - * @{ - */ - -#ifndef __ADI_ADPD6000_H__ -#define __ADI_ADPD6000_H__ - -/*============= I N C L U D E S ============*/ -#include -#include -#include -#if ADPD6000_REPORT_VERBOSE > ADPD6000_LOG_NONE_MSG -#include -#include -#endif -#include "adi_adpd6000_bf_reg.h" -#include "adi_adpd6000_config.h" - -/*============= D E F I N E S ==============*/ -/*! - * @brief SDK version macro (major.minor.build, one byte each) - */ -#define ADPD6000_SDK_VER (0x00010300) - -/*! - * @brief Address macros - */ -#define ADPD6000_TIME_SLOT_SPAN ((REG_TS_CTRL_B_ADDR) - (REG_TS_CTRL_A_ADDR)) - - -/*! - * @brief SDK message report macro - */ -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - #define __FUNCTION_NAME__ __func__ -#else - #define __FUNCTION_NAME__ __FUNCTION__ -#endif - -#if ADPD6000_REPORT_VERBOSE > ADPD6000_LOG_NONE_MSG -#define ADPD6000_MSG_REPORT(var, comment) \ - adi_adpd6000_hal_error_report(device, ADPD6000_LOG_VAR_MSG | ADPD6000_LOG_INFO_MSG, __FILE__, __FUNCTION_NAME__, __LINE__, #var, comment) -#define ADPD6000_WARN_REPORT(var, comment) \ - adi_adpd6000_hal_error_report(device, ADPD6000_LOG_VAR_MSG | ADPD6000_LOG_WARN_MSG, __FILE__, __FUNCTION_NAME__, __LINE__, #var, comment) -#define ADPD6000_ERROR_REPORT(var, comment) \ - adi_adpd6000_hal_error_report(device, ADPD6000_LOG_VAR_MSG | ADPD6000_LOG_ERR_MSG, __FILE__, __FUNCTION_NAME__, __LINE__, #var, comment) -#define ADPD6000_LOG_MSG(msg) \ - adi_adpd6000_hal_log_write(device, ADPD6000_LOG_MISC_MSG | ADPD6000_LOG_INFO_MSG, msg) -#define ADPD6000_LOG_WARN(msg) \ - adi_adpd6000_hal_log_write(device, ADPD6000_LOG_MISC_MSG | ADPD6000_LOG_WARN_MSG, msg) -#define ADPD6000_LOG_ERR(msg) \ - adi_adpd6000_hal_log_write(device, ADPD6000_LOG_MISC_MSG | ADPD6000_LOG_ERR_MSG, msg) -#define ADPD6000_LOG_FUNC() \ - adi_adpd6000_hal_log_write(device, ADPD6000_LOG_FUNC_MSG | ADPD6000_LOG_INFO_MSG, "%s(...)", __FUNCTION_NAME__) -#define ADPD6000_LOG_REG(msg, ...) \ - adi_adpd6000_hal_log_write(device, ADPD6000_LOG_REG_MSG | ADPD6000_LOG_INFO_MSG, msg, ##__VA_ARGS__) -#define ADPD6000_LOG_VAR(type, msg, ...) \ - adi_adpd6000_hal_log_write(device, ADPD6000_LOG_VAR_MSG | type, msg, ##__VA_ARGS__) -#else -#define ADPD6000_MSG_REPORT(var, comment) -#define ADPD6000_WARN_REPORT(var, comment) -#define ADPD6000_ERROR_REPORT(var, comment) -#define ADPD6000_LOG_MSG(msg) -#define ADPD6000_LOG_WARN(msg) -#define ADPD6000_LOG_ERR(msg) -#define ADPD6000_LOG_FUNC() -#define ADPD6000_LOG_VAR(type, msg, ...) -#define ADPD6000_LOG_REG(msg, ...) -#endif - -/*! - * @brief error code check macro, return error code if it is not API_ADPD6000_ERROR_OK - */ -#define ADPD6000_ERROR_RETURN(r) \ -{ \ - if ((r) != API_ADPD6000_ERROR_OK) { \ - return r; \ - } \ -} - -/*! - * @brief param check macro, return API_ADPD6000_ERROR_NULL_PARAM if param is NULL - */ -#define ADPD6000_NULL_POINTER_RETURN(p) \ -{ \ - if((p) == NULL) { \ - ADPD6000_ERROR_REPORT(p, "Null pointer passed."); \ - return API_ADPD6000_ERROR_NULL_PARAM; \ - } \ -} - -/*! - * @brief param check macro, return API_ADPD6000_ERROR_INVALID_PARAM if equation is true - */ -#define ADPD6000_INVALID_PARAM_RETURN(r) \ -{ \ - if(r) { \ - ADPD6000_ERROR_REPORT(r, "Invalid param passed."); \ - return API_ADPD6000_ERROR_INVALID_PARAM; \ - } \ -} - -/*! - * @brief param check macro, log warning message if equation is true - */ -#define ADPD6000_INVALID_PARAM_WARN(r) \ -{ \ - if(r) { \ - ADPD6000_WARN_REPORT(r, "Invalid param passed."); \ - } \ -} - -/** - * @brief Platform dependent control port read function. - * - * @param user_data Pointer to customer data if needed, usually handle to a specific spi/i2c/uart - * @param rd_buf Pointer to data returned from read operation - * @param rd_len Length to read, in bytes - * @param wr_buf Pointer to write data required during read operation - * @param wr_len Length to write, in bytes - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -typedef int32_t (*adi_adpd6000_read)(void* user_data, uint8_t *rd_buf, uint32_t rd_len, uint8_t *wr_buf, uint32_t wr_len); - -/** - * @brief Platform dependent control port write function. - * - * @param user_data Pointer to customer data if needed, usually handle to a specific spi/i2c/uart device - * @param wr_buf Pointer to write data required during read operation - * @param len Length to write, in bytes - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -typedef int32_t (*adi_adpd6000_write)(void* user_data, uint8_t *wr_buf, uint32_t len); - -/** - * @brief Platform dependent log write function. - * - * @param user_data Pointer to customer data if needed - * @param string Messsage to log - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -typedef int32_t (*adi_adpd6000_log_write)(void* user_data, char *string); - -/*! - * @brief adi adpd6000 device structure - */ -typedef struct -{ - void* user_data; /*!< User structure */ - adi_adpd6000_read read; /*!< Function Pointer to HAL SPI read function */ - adi_adpd6000_write write; /*!< Function Pointer to HAL SPI write function */ - adi_adpd6000_log_write log_write; /*!< Function Pointer to HAL log write function */ -} adi_adpd6000_device_t; - -/*! - * @brief adpd6000 ppg fifo information - */ -typedef struct -{ - uint8_t ppg_chl2_en; /*!< 0 - Channel2 disabled, 1 - Channel2 enabled */ - uint8_t signal_size; /*!< size of singnal data */ - uint8_t dark_size; /*!< size of dark data */ - uint8_t lit_size; /*!< size of lit data */ -} adi_adpd6000_ppg_fifo_config_t; - -/*! - * @brief adpd6000 ppg fifo data - */ -typedef struct -{ - uint32_t chnl[2]; /*!< Channel 1&2 */ -} adi_adpd6000_ppg_slot_data_t; - -/*! - * @brief adpd6000 sequence fifo information - */ -typedef struct -{ - uint32_t sequence_size; /*!< Size of data in FIFO druing a sequence */ - uint8_t ecg_slot; /*!< ECG slot number, 0 or 1 */ - uint8_t ecg_over_sample; /*!< ECG oversampling rate */ - uint8_t ecg_size; /*!< ECG data size, 3 bytes - status disabled, 4 bytes - status byte enabled */ - uint8_t ppg_slot; /*!< PPG slot number */ - uint8_t ppg_chnl_num; /*!< PPG channel number */ - adi_adpd6000_ppg_fifo_config_t ppg_fifo[12]; /*!< PPG FIFO configuration */ - uint8_t bioz_slot; /*!< BioZ slot number */ -} adi_adpd6000_fifo_config_t; - -/*! - * @brief return value for adi adpd6000 api - */ -typedef enum { - API_ADPD6000_ERROR_OK = 0, /*!< No Error */ - API_ADPD6000_ERROR_ERROR = -1, /*!< General Error */ - API_ADPD6000_ERROR_NULL_PARAM = -2, /*!< Null parameter */ - API_ADPD6000_ERROR_INVALID_PARAM = -3, /*!< Invalid parameter passed */ - API_ADPD6000_ERROR_NOT_SUPPORTED = -4, /*!< Not supported */ - API_ADPD6000_ERROR_REG_ACCESS = -5, /*!< Register access error */ - API_ADPD6000_ERROR_LOG_WRITE = -6, /*!< Log write error */ - API_ADPD6000_ERROR_FUSE_NOT_DONE = -7, /*!< Fuse not done */ -} adi_adpd6000_error_e; - -/*! - * @brief GPIO mode enumuration - */ -typedef enum { - API_ADPD6000_GPIO_MODE_DISABLE = 0, /*!< Disabled (tristate, input buffer off) */ - API_ADPD6000_GPIO_MODE_INPUT = 1, /*!< Enabled Input */ - API_ADPD6000_GPIO_MODE_NORMAL = 2, /*!< Output - Normal */ - API_ADPD6000_GPIO_MODE_INVERTED = 3, /*!< Output - Inverted */ - API_ADPD6000_GPIO_MODE_PD_NORMAL = 4, /*!< Pulldown Only - Normal */ - API_ADPD6000_GPIO_MODE_PD_INVERTED = 5, /*!< Pulldown Only - Inverted */ - API_ADPD6000_GPIO_MODE_PU_NORMAL = 6, /*!< Pullup Only - Normal */ - API_ADPD6000_GPIO_MODE_PU_INVERTED = 7, /*!< Pullup Only - Inverted */ -} adi_adpd6000_gpio_mode_e; - -/*! - * @brief Time slot enumuration -*/ -typedef enum { - API_ADPD6000_SLOT_A = 0, /*!< Timeslot A */ - API_ADPD6000_SLOT_B = 1, /*!< Timeslot B */ - API_ADPD6000_SLOT_C = 2, /*!< Timeslot C */ - API_ADPD6000_SLOT_D = 3, /*!< Timeslot D */ - API_ADPD6000_SLOT_E = 4, /*!< Timeslot E */ - API_ADPD6000_SLOT_F = 5, /*!< Timeslot F */ - API_ADPD6000_SLOT_G = 6, /*!< Timeslot G */ - API_ADPD6000_SLOT_H = 7, /*!< Timeslot H */ - API_ADPD6000_SLOT_I = 8, /*!< Timeslot I */ - API_ADPD6000_SLOT_J = 9, /*!< Timeslot J */ - API_ADPD6000_SLOT_K = 10, /*!< Timeslot K */ - API_ADPD6000_SLOT_L = 11, /*!< Timeslot L */ -} adi_adpd6000_slot_e; - -/*! - * @brief PPG slot mode enumuration -*/ -typedef enum { - API_ADPD6000_PPG_SLOT_NONE = 0, /*!< No PPG timeslot */ - API_ADPD6000_PPG_SLOT_A = 1, /*!< PPG timeslot sequency A only */ - API_ADPD6000_PPG_SLOT_AB = 2, /*!< PPG timeslot sequency AB */ - API_ADPD6000_PPG_SLOT_ABC = 3, /*!< PPG timeslot sequency ABC */ - API_ADPD6000_PPG_SLOT_ABCD = 4, /*!< PPG timeslot sequency ABCD */ - API_ADPD6000_PPG_SLOT_ABCDE = 5, /*!< PPG timeslot sequency ABCDE */ - API_ADPD6000_PPG_SLOT_ABCDEF = 6, /*!< PPG timeslot sequency ABCDEF */ - API_ADPD6000_PPG_SLOT_ABCDEFG = 7, /*!< PPG timeslot sequency ABCDEFG */ - API_ADPD6000_PPG_SLOT_ABCDEFGH = 8, /*!< PPG timeslot sequency ABCDEFGH */ - API_ADPD6000_PPG_SLOT_ABCDEFGHI = 9, /*!< PPG timeslot sequency ABCDEFGHI */ - API_ADPD6000_PPG_SLOT_ABCDEFGHIJ = 10, /*!< PPG timeslot sequency ABCDEFGHIJ */ - API_ADPD6000_PPG_SLOT_ABCDEFGHIJK = 11, /*!< PPG timeslot sequency ABCDEFGHIJK */ - API_ADPD6000_PPG_SLOT_ABCDEFGHIJKL = 12, /*!< PPG timeslot sequency ABCDEFGHIJKL */ -} adi_adpd6000_ppg_slot_mode_e; - -/*! - * @brief PPG input mux enumuration, Odd for input A, Even for input B -*/ -typedef enum { - API_ADPD6000_PPG_INPUT_DISABLE = 0, /*!< Input pair disabled. None to channel 1. None to channel 2 */ - API_ADPD6000_PPG_INPUT_A1 = 1, /*!< Odd input to channel 1. None to channel 2. */ - API_ADPD6000_PPG_INPUT_A2 = 2, /*!< None to channel 1. Odd input to channel 2. */ - API_ADPD6000_PPG_INPUT_B1 = 3, /*!< Even input to channel 1. None to channel 2.*/ - API_ADPD6000_PPG_INPUT_B2 = 4, /*!< None to channel 1. Even input to channel 2. */ - API_ADPD6000_PPG_INPUT_A1_B2 = 5, /*!< Odd input to channel 1. Even input to channel 2. */ - API_ADPD6000_PPG_INPUT_A2_B1 = 6, /*!< Even input to channel 1. Odd input to channel 2. */ - API_ADPD6000_PPG_INPUT_A1_B1 = 7, /*!< Even and odd to channel 1. Single ended or differentially based on pair12. None to channel 2. */ - API_ADPD6000_PPG_INPUT_A2_B2 = 8, /*!< None to channel 1. Even and odd to channel 2. Single ended or differentially based on pair12. */ -} adi_adpd6000_ppg_input_mux_e; - -/*! - * @brief PPG input mux enumuration during sleep -*/ -typedef enum { - API_ADPD6000_PPG_INPUT_SLEEP_BOTH_FLOAT = 0, /*!< Both inputs float */ - API_ADPD6000_PPG_INPUT_SLEEP_SHORT = 1, /*!< Even and odd shorted together (floating short of differential) Only shorts if pair12 is 1. */ - API_ADPD6000_PPG_INPUT_SLEEP_BOTH_CATH1 = 2, /*!< Both connected to Cathode1 (also shorted together if configured as differential pair). */ - API_ADPD6000_PPG_INPUT_SLEEP_BOTH_CATH2 = 3, /*!< Both connected to Cathode2 (also shorted together if configured as differential pair). */ - API_ADPD6000_PPG_INPUT_SLEEP_ODD_CATH1_EVEN_FLOAT = 4, /*!< Odd connected to Cathode1. Even floating. */ - API_ADPD6000_PPG_INPUT_SLEEP_ODD_CATH1_EVEN_CATH2 = 5, /*!< Odd connected to Cathode1. Even connected to Cathode2. */ - API_ADPD6000_PPG_INPUT_SLEEP_ODD_CATH2_EVEN_FLOAT = 6, /*!< Odd connected to Cathode2. Even floating.*/ - API_ADPD6000_PPG_INPUT_SLEEP_ODD_CATH2_EVEN_CATH1 = 7, /*!< Odd connected to Cathode2. Even connected to Cathode1. */ - API_ADPD6000_PPG_INPUT_SLEEP_ODD_FLOAT_EVEN_CATH1 = 8, /*!< Odd floating. Even connected to Cathode1. */ - API_ADPD6000_PPG_INPUT_SLEEP_ODD_FLOAT_EVEN_CATH2 = 9, /*!< Odd floating. Even connected to Cathode2. */ -} adi_adpd6000_ppg_sleep_input_mux_e; - -/*! - * @brief PPG cathode type enumuration during sleep -*/ -typedef enum { - API_ADPD6000_PPG_CATH_VDD = 0, /*!< Cathode set to VDD during sleep. */ - API_ADPD6000_PPG_CATH_GND = 1, /*!< Cathode set to GND during sleep. */ - API_ADPD6000_PPG_CATH_FLOAT = 2, /*!< Cathode floating during sleep. */ -} adi_adpd6000_ppg_cath_type_e; - -/*! - * @brief PPG modulation type enumuration -*/ -typedef enum { - API_ADPD6000_PPG_MOD_TYPE_NONE = 0, /*!< TIA continuously connected to input after precondition. No connection modulation. */ - API_ADPD6000_PPG_MOD_TYPE_FLOAT = 1, /*!< Float type operation. Pulse connection from input to TIA with modulate pulse, floating between pulses. */ - API_ADPD6000_PPG_MOD_TYPE_NONFLOAT = 2, /*!< Non-float type connection modulation. Pulse connection from input to TIA. Connect to precondition value between pulses. */ -} adi_adpd6000_ppg_mod_type_e; - -/*! - * @brief PPG TIA input resistor enumuration -*/ -typedef enum { - API_ADPD6000_PPG_TIA_INPUT_RES_ESD = 0, /*!< ECD resistor only */ - API_ADPD6000_PPG_TIA_INPUT_RES_6K5 = 1, /*!< 6.5K resistor */ -} adi_adpd6000_ppg_tia_input_res_e; - -/*! - * @brief PPG TIA pulse value enumuration -*/ -typedef enum { - API_ADPD6000_PPG_TIA_VREF_1P1385 = 0, /*!< TIA vref is 1.1385V */ - API_ADPD6000_PPG_TIA_VREF_1P012 = 1, /*!< TIA vref is 1.012V */ - API_ADPD6000_PPG_TIA_VREF_0P8855 = 2, /*!< TIA vref is 0.8855V */ - API_ADPD6000_PPG_TIA_VREF_1P265 = 3, /*!< TIA vref is 1.265V */ -} adi_adpd6000_ppg_tia_vref_value_e; - -/*! - * @brief PPG TIA gain resistor value enumuration -*/ -typedef enum { - API_ADPD6000_PPG_TIA_GAIN_RES_400K = 0, /*!< TIA gain resistor is 400K Ohm */ - API_ADPD6000_PPG_TIA_GAIN_RES_200K = 1, /*!< TIA gain resistor is 200K Ohm */ - API_ADPD6000_PPG_TIA_GAIN_RES_100K = 2, /*!< TIA gain resistor is 100K Ohm */ - API_ADPD6000_PPG_TIA_GAIN_RES_50K = 3, /*!< TIA gain resistor is 50K Ohm */ - API_ADPD6000_PPG_TIA_GAIN_RES_25K = 4, /*!< TIA gain resistor is 25K Ohm */ - API_ADPD6000_PPG_TIA_GAIN_RES_12K5 = 5, /*!< TIA gain resistor is 12.5K Ohm */ -} adi_adpd6000_ppg_tia_gain_res_e; - -/*! - * @brief PPG Integrator input gain resistor value enumuration -*/ -typedef enum { - API_ADPD6000_PPG_INTEG_400K_GAIN_1 = 0, /*!< Rin = 400K or Buffer gain = 1 (Rfb/Rin = 200K/200K) */ - API_ADPD6000_PPG_INTEG_200K_GAIN_2 = 1, /*!< Rin = 200K or Buffer gain = 2 (Rfb/Rin = 200K/100K) */ - API_ADPD6000_PPG_INTEG_100K_GAIN_1 = 2, /*!< Rin = 100K or Buffer gain = 1 (Rfb/Rin = 100K/100K) */ - API_ADPD6000_PPG_INTEG_50K_GAIN_2 = 3, /*!< Rin = 50K or Buffer gain = 2 (Rfb/Rin = 100K/500K) */ -} adi_adpd6000_ppg_integ_gain_res_e; - -/*! - * @brief PPG Integrator gain cap value enumuration -*/ -typedef enum { - API_ADPD6000_PPG_INTEG_CAP_6P3 = 0, /*!< Cap is 6.3pF */ - API_ADPD6000_PPG_INTEG_CAP_12P6 = 1, /*!< Cap is 12.6pF */ -} adi_adpd6000_ppg_integ_cap_e; - -/*! - * @brief PPG LED channel enumuration -*/ -typedef enum { - API_ADPD6000_PPG_LED_A = 0, /*!< Driver LED on output A */ - API_ADPD6000_PPG_LED_B = 1, /*!< Driver LED on output B */ -} adi_adpd6000_ppg_led_channel_e; - -/*! - * @brief PPG LED mode enumuration -*/ -typedef enum { - API_ADPD6000_PPG_LED_HIGH_SNR = 0, /*!< High SNR mode */ - API_ADPD6000_PPG_LED_LOW_COMP = 1, /*!< Low compliance mode */ -} adi_adpd6000_ppg_led_mode_e; - -/*! - * @brief VCx selection enumuration -*/ -typedef enum { - API_ADPD6000_PPG_VC_VDD = 0, /*!< VDD */ - API_ADPD6000_PPG_VC_VREF = 1, /*!< AFE_Vref */ - API_ADPD6000_PPG_VC_DELTA = 2, /*!< V_Delta */ - API_ADPD6000_PPG_VC_GND = 3, /*!< GND */ -} adi_adpd6000_ppg_vc_select_e; - -/*! - * @brief VCx pulse enumuration -*/ -typedef enum { - API_ADPD6000_PPG_VC_PULSE_NO = 0, /*!< No pulse */ - API_ADPD6000_PPG_VC_PULSE_ALT = 1, /*!< Alternate odd/even timeslots */ - API_ADPD6000_PPG_VC_PULSE_MODULATED = 2, /*!< Pulse to alternate value using Modulate pulse */ - API_ADPD6000_PPG_VC_PULSE_FLOATING = 3, /*!< Leave VC1 floating */ -} adi_adpd6000_ppg_vc_pulse_e; - -/*! - * @brief Precondition value for enabled inputs enumuration -*/ -typedef enum { - API_ADPD6000_PPG_PRECON_FLOAT = 0, /*!< No pulse */ - API_ADPD6000_PPG_PRECON_VC1 = 1, /*!< Alternate odd/even timeslots */ - API_ADPD6000_PPG_PRECON_VC2 = 2, /*!< Pulse to alternate value using Modulate pulse */ - API_ADPD6000_PPG_PRECON_VICM = 3, /*!< Leave VC1 floating */ - API_ADPD6000_PPG_PRECON_TIA_INPUT = 4, /*!< Leave VC1 floating */ - API_ADPD6000_PPG_PRECON_AFE_VREF = 5, /*!< Leave VC1 floating */ - API_ADPD6000_PPG_PRECON_SHORT_DIFF_PAIR = 6, /*!< Leave VC1 floating */ -} adi_adpd6000_ppg_precon_e; - -/*! - * @brief AFE datapath enumuration -*/ -typedef enum { - API_ADPD6000_PPG_AFE_PATH_TIA_BUF_ADC_2X = 0x20, /*!< TIA + INT/BUF + ADC(2x TIA Gain) */ - API_ADPD6000_PPG_AFE_PATH_TIA_BUF_ADC_1X = 0x28, /*!< TIA + BUF + ADC(1x TIA Gain) */ - API_ADPD6000_PPG_AFE_PATH_TIA_INT_ADC_1X = 0x31, /*!< TIA + INT + ADC(1x TIA Gain) */ - API_ADPD6000_PPG_AFE_PATH_INT_ADC = 0x35, /*!< INT + ADC */ - API_ADPD6000_PPG_AFE_PATH_ADC = 0x41, /*!< ADC */ -} adi_adpd6000_ppg_afe_path_e; - -/*! - * @brief PPG sample type enumuration -*/ -typedef enum { - API_ADPD6000_PPG_SAMPLE_TYPE_NORMAL = 0, /*!< Normal sampling mode */ - API_ADPD6000_PPG_SAMPLE_TYPE_TWO_PHASE = 1, /*!< Two phase normal sampling mode */ - API_ADPD6000_PPG_SAMPLE_TYPE_ONE_REGION = 2, /*!< One region digital mode */ - API_ADPD6000_PPG_SAMPLE_TYPE_TWO_REGION = 3, /*!< Two region digital mode */ - API_ADPD6000_PPG_SAMPLE_TYPE_IMPULSE = 4, /*!< Impulse response mode */ -} adi_adpd6000_ppg_sample_type_e; - -/*! - * @brief Ambient cancellation DAC Control type enumuration -*/ -typedef enum { - API_ADPD6000_PPG_ALC_DISABLE = 0, /*!< Disable the ambient cancellation loop */ - API_ADPD6000_PPG_ALC_COARSE_FINE = 1, /*!< Enable coarse + fine loop */ - API_ADPD6000_PPG_ALC_COARSE = 2, /*!< Enable coarse loop only */ - API_ADPD6000_PPG_ALC_MCU = 3, /*!< Enable MCU control */ -} adi_adpd6000_ppg_alc_type_e; - -/*! - * @brief The time duration for the coarse ambient cancellation loop enumuration -*/ -typedef enum { - API_ADPD6000_PPG_ALC_LOOP_WIDTH_10 = 0, /*!< 10 */ - API_ADPD6000_PPG_ALC_LOOP_WIDTH_20 = 1, /*!< 20 */ -} adi_adpd6000_ppg_alc_loop_width_e; - -/*! - * @brief PPG slot AGC configuration structure -*/ -typedef struct -{ - uint8_t agc_en : 1; /*!< 0 - disable, 1 - enable */ - uint8_t led_chnl : 1; /*!< LED 0 or 1 for AGC */ - uint8_t led_ab : 1; /*!< LED A or B for AGC, 0 for A, 1 for B */ - uint8_t tia_chnl : 1; /*!< 0 - use tia channel 1, 1 - use tia channel 2 */ - uint8_t agc_type : 1; /*!< 0 - continuous, 1 - one shot */ -} adi_adpd6000_ppg_agc_slot_t; - -/*! - * @brief PPG AGC configuration structure -*/ -typedef struct -{ - uint16_t ppg_skip_sample_number; /*!< skip the first few data for process */ - uint16_t ppg_average_sample_number; /*!< average sample count */ - uint8_t power_first_en; /* 0 - use low TIA gain, 1 - use low LED current */ - adi_adpd6000_ppg_agc_slot_t slot[12]; -} adi_adpd6000_ppg_agc_cfg_t; - -/*! - * @brief PPG AGC running data structure -*/ -typedef struct -{ - uint32_t ppg_full_scale; - uint32_t ppg_data_sum; - uint8_t tia_gain; - uint8_t led_current; - uint8_t agc_done; -} adi_adpd6000_ppg_agc_run_t; - -/*! - * @brief adpd6000 bioz fifo data - */ -typedef struct -{ - uint32_t real; /* I */ - uint32_t imag; /* Q */ -} adi_adpd6000_bioz_slot_data_t; - -/*! - * @brief BioZ slot mode enumuration -*/ -typedef enum { - API_ADPD6000_BIOZ_SLOT_NONE = 0, /*!< No BioZ timeslot */ - API_ADPD6000_BIOZ_SLOT_A = 1, /*!< BioZ timeslot sequency A only */ - API_ADPD6000_BIOZ_SLOT_AB = 2, /*!< BioZ timeslot sequency AB */ - API_ADPD6000_BIOZ_SLOT_ABC = 3, /*!< BioZ timeslot sequency ABC */ - API_ADPD6000_BIOZ_SLOT_ABCD = 4, /*!< BioZ timeslot sequency ABCD */ - API_ADPD6000_BIOZ_SLOT_ABCDE = 5, /*!< BioZ timeslot sequency ABCDE */ - API_ADPD6000_BIOZ_SLOT_ABCDEF = 6, /*!< BioZ timeslot sequency ABCDEF */ -} adi_adpd6000_bioz_slot_mode_e; - -/*! - * @brief BioZ TIA Gain Res enumuration -*/ -typedef enum { - API_ADPD6000_BIOZ_TIA_GAIN_1K = 0, /*!< Gain resistor is 1K Ohm */ - API_ADPD6000_BIOZ_TIA_GAIN_2K = 1, /*!< Gain resistor is 2K Ohm */ - API_ADPD6000_BIOZ_TIA_GAIN_3K = 2, /*!< Gain resistor is 3K Ohm */ - API_ADPD6000_BIOZ_TIA_GAIN_4K = 3, /*!< Gain resistor is 4K Ohm */ - API_ADPD6000_BIOZ_TIA_GAIN_6K = 4, /*!< Gain resistor is 6K Ohm */ - API_ADPD6000_BIOZ_TIA_GAIN_8K = 5, /*!< Gain resistor is 8K Ohm */ - API_ADPD6000_BIOZ_TIA_GAIN_10K = 6, /*!< Gain resistor is 10K Ohm */ -} adi_adpd6000_bioz_tia_gain_res_e; - -/*! - * @brief BioZ SINC3 oversample rate enumuration -*/ -typedef enum { - API_ADPD6000_BIOZ_SINC3_OVERSAMPLE_RATE_5 = 0, /*!< Oversampling rate is 5 */ - API_ADPD6000_BIOZ_SINC3_OVERSAMPLE_RATE_4 = 1, /*!< Oversampling rate is 4 */ - API_ADPD6000_BIOZ_SINC3_OVERSAMPLE_RATE_2 = 2, /*!< Oversampling rate is 2 */ - API_ADPD6000_BIOZ_SINC3_OVERSAMPLE_RATE_5_1 = 3, /*!< Oversampling rate is 5 */ -} adi_adpd6000_bioz_sinc3_os_rate_e; - -/*! - * @brief BioZ SINC3 average samples enumuration -*/ -typedef enum { - API_ADPD6000_BIOZ_SINC3_AVERAGE_2 = 0, /*!< 2 ADC samples */ - API_ADPD6000_BIOZ_SINC3_AVERAGE_4 = 1, /*!< 4 ADC samples */ - API_ADPD6000_BIOZ_SINC3_AVERAGE_8 = 2, /*!< 8 ADC samples */ - API_ADPD6000_BIOZ_SINC3_AVERAGE_16 = 3, /*!< 16 ADC samples */ -} adi_adpd6000_bioz_sinc3_avg_e; - -/*! - * @brief BioZ DFT point number enumuration -*/ -typedef enum { - API_ADPD6000_BIOZ_DFT_POINT_4 = 0, /*!< DFT point number is 4 */ - API_ADPD6000_BIOZ_DFT_POINT_8 = 1, /*!< DFT point number is 8 */ - API_ADPD6000_BIOZ_DFT_POINT_16 = 2, /*!< DFT point number is 16 */ - API_ADPD6000_BIOZ_DFT_POINT_32 = 3, /*!< DFT point number is 32 */ - API_ADPD6000_BIOZ_DFT_POINT_64 = 4, /*!< DFT point number is 64 */ - API_ADPD6000_BIOZ_DFT_POINT_128 = 5, /*!< DFT point number is 128 */ - API_ADPD6000_BIOZ_DFT_POINT_256 = 6, /*!< DFT point number is 256 */ - API_ADPD6000_BIOZ_DFT_POINT_512 = 7, /*!< DFT point number is 512 */ - API_ADPD6000_BIOZ_DFT_POINT_1024 = 8, /*!< DFT point number is 1024 */ - API_ADPD6000_BIOZ_DFT_POINT_2048 = 9, /*!< DFT point number is 2048 */ - API_ADPD6000_BIOZ_DFT_POINT_4096 = 10, /*!< DFT point number is 4096 */ - API_ADPD6000_BIOZ_DFT_POINT_8192 = 11, /*!< DFT point number is 8192 */ -} adi_adpd6000_bioz_dft_point_e; - -/*! - * @brief BioZ AMP mode enumuration -*/ -typedef enum { - API_ADPD6000_BIOZ_AMP_MODE_ENABLE_ALL = 0, /*!< 2 ADC samples */ - API_ADPD6000_BIOZ_AMP_MODE_ENABLE_P = 1, /*!< 4 ADC samples */ - API_ADPD6000_BIOZ_AMP_MODE_ENABLE_M = 2, /*!< 8 ADC samples */ - API_ADPD6000_BIOZ_AMP_MODE_ENABLE_P_M = 3, /*!< 16 ADC samples */ -} adi_adpd6000_bioz_amp_mode_e; - -/*! - * @brief BioZ connection enumuration -*/ -typedef enum { - API_ADPD6000_BIOZ_IMPIP = 1, /*!< Connect to biz-impedance to IMPIP */ - API_ADPD6000_BIOZ_IMPIN = 2, /*!< Connect to biz-impedance to IMPIN */ - API_ADPD6000_BIOZ_EXCP = 4, /*!< Connect to biz-impedance to EXCP */ - API_ADPD6000_BIOZ_EXCN = 8, /*!< Connect to biz-impedance to EXCN */ -} adi_adpd6000_bioz_imp_pin_e; - -/*! - * @brief BioZ connection enumuration -*/ -typedef enum { - API_ADPD6000_BIOZ_CONN_EXT_RCAL_VOL = 0, /*!< Connect to external RCal, and get RCal voltage data */ - API_ADPD6000_BIOZ_CONN_EXT_RCAL_CUR = 1, /*!< Connect to external RCal, and get RCal current data */ - API_ADPD6000_BIOZ_CONN_INT_RCAL_VOL = 2, /*!< Connect to internal RCal, and get RCal voltage data */ - API_ADPD6000_BIOZ_CONN_INT_RCAL_CUR = 3, /*!< Connect to internal RCal, and get RCal current data */ - API_ADPD6000_BIOZ_CONN_BIO_IMP_VOL = 4, /*!< Connect to biz-impedance, and get voltage data */ - API_ADPD6000_BIOZ_CONN_BIO_IMP_CUR = 5, /*!< Connect to biz-impedance, and get current data */ -} adi_adpd6000_bioz_slot_connect_e; - -/*! - * @brief ECG ODR value enumuration -*/ -typedef enum { - API_ADPD6000_ECG_ODR_250 = 0, /*!< Output data rate is 250Hz */ - API_ADPD6000_ECG_ODR_500 = 1, /*!< Output data rate is 500Hz */ - API_ADPD6000_ECG_ODR_1000 = 2, /*!< Output data rate is 1KHz */ - API_ADPD6000_ECG_ODR_2000 = 3, /*!< Output data rate is 2KHz */ - API_ADPD6000_ECG_ODR_4000 = 4, /*!< Output data rate is 4KHz */ -} adi_adpd6000_ecg_odr_e; - -/*! - * @brief ECG RLD(right leg drive) output value enumuration -*/ -typedef enum { - API_ADPD6000_ECG_RLD_OUTPUT_GND = 0, /*!< AGND */ - API_ADPD6000_ECG_RLD_OUTPUT_AVDD_3V = 1, /*!< AVDD3V */ - API_ADPD6000_ECG_RLD_OUTPUT_AVDD_1V5 = 2, /*!< AVDD3V/2 */ - API_ADPD6000_ECG_RLD_OUTPUT_CM_INPUT = 3, /*!< Regulated commom-mode input */ -} adi_adpd6000_ecg_rld_output_e; - -/*! - * @brief ECG DC lead off threshold value enumuration -*/ -typedef enum { - API_ADPD6000_ECG_DC_LEADOFF_100MV = 0, /*!< High threshold=AVDD3V-0.1V; low threshold=0.1V. */ - API_ADPD6000_ECG_DC_LEADOFF_200MV = 1, /*!< High threshold=AVDD3V-0.2V; low threshold=0.2V. */ - API_ADPD6000_ECG_DC_LEADOFF_300MV = 2, /*!< High threshold=AVDD3V-0.3V; low threshold=0.3V. */ - API_ADPD6000_ECG_DC_LEADOFF_400MV = 3, /*!< High threshold=AVDD3V-0.4V; low threshold=0.4V. */ -} adi_adpd6000_ecg_dc_leadoff_th_e; - -/*! - * @brief ECG RLD AC lead off current magnitude value enumuration -*/ -typedef enum { - API_ADPD6000_ECG_AC_LEADOFF_0NA = 0, /*!< 0nA */ - API_ADPD6000_ECG_AC_LEADOFF_10NA = 1, /*!< 10nA */ - API_ADPD6000_ECG_AC_LEADOFF_20NA = 2, /*!< 20nA */ - API_ADPD6000_ECG_AC_LEADOFF_30NA = 3, /*!< 30nA */ - API_ADPD6000_ECG_AC_LEADOFF_40NA = 4, /*!< 40nA */ - API_ADPD6000_ECG_AC_LEADOFF_50NA = 5, /*!< 50nA */ - API_ADPD6000_ECG_AC_LEADOFF_60NA = 6, /*!< 60nA */ - API_ADPD6000_ECG_AC_LEADOFF_70NA = 7, /*!< 70nA */ -} adi_adpd6000_ecg_ac_leadoff_mag_e; - -/*! - * @brief ECG RLD DC lead off current magnitude value enumuration -*/ -typedef enum { - API_ADPD6000_ECG_DC_LEADOFF_0NA = 0, /*!< 0nA */ - API_ADPD6000_ECG_DC_LEADOFF_2NA = 8, /*!< 2nA */ - API_ADPD6000_ECG_DC_LEADOFF_4NA = 9, /*!< 4nA */ - API_ADPD6000_ECG_DC_LEADOFF_6NA = 10, /*!< 6nA */ - API_ADPD6000_ECG_DC_LEADOFF_8NA = 11, /*!< 8nA */ - API_ADPD6000_ECG_DC_LEADOFF_10NA = 12, /*!< 10nA */ - API_ADPD6000_ECG_DC_LEADOFF_12NA = 13, /*!< 12nA */ - API_ADPD6000_ECG_DC_LEADOFF_14NA = 14, /*!< 14nA */ - API_ADPD6000_ECG_DC_LEADOFF_16NA = 15, /*!< 16nA */ -} adi_adpd6000_ecg_dc_leadoff_mag_e; - -/*! - * @brief ECG RLD DC lead off current polarity value enumuration -*/ -typedef enum { - API_ADPD6000_ECG_DC_LEADOFF_CURRENT_SINK = 0, /*!< Sink current */ - API_ADPD6000_ECG_DC_LEADOFF_CURRENT_SOURCE = 1, /*!< Sink current */ -} adi_adpd6000_ecg_dc_leadoff_pol_e; - -/*! - * @brief Interrupt type enumuration -*/ -typedef enum { - API_ADPD6000_INTERRUPT_X = 0, /*!< Interrupt X */ - API_ADPD6000_INTERRUPT_Y = 1, /*!< Interrupt Y */ -} adi_adpd6000_interrupt_type_e; - -/*! - * @brief System clock source enumuration -*/ -typedef enum { - API_ADPD6000_SYS_CLOCK_INT_CLK = 0, /*!< Internal clocks used */ - API_ADPD6000_SYS_CLOCK_EXT_LF = 1, /*!< Use gpio for lf osc, 960KHz. Timer clock (tm_clk) will also use this as source */ - API_ADPD6000_SYS_CLOCK_EXT_HF = 2, /*!< Use gpio for hf osc, 32MHz. */ - API_ADPD6000_SYS_CLOCK_EXT_BOTH = 3, /*!< Use gpio for hf osc (32MHz), generate lf (1MHz) from hf. This should only be used when ECG is disabled */ - API_ADPD6000_SYS_CLOCK_EXT_TM = 4, /*!< Use gpio for timer clock, 32KHz or 960KHz */ - -} adi_adpd6000_sys_clock_src_e; - -#ifdef __cplusplus -extern "C" { -#endif - -/*============= E X P O R T S ==============*/ -/** - * @brief HAL register read function. - * - * @param device Pointer to device structure - * @param reg_addr Register address to read - * @param reg_data Pointer to save readback data - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_hal_reg_read(adi_adpd6000_device_t *device, uint32_t reg_addr, uint16_t *reg_data); - -/** - * @brief HAL register write function. - * - * @param device Pointer to device structure - * @param reg_addr Register address to write - * @param reg_data Register value to write - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_hal_reg_write(adi_adpd6000_device_t *device, uint32_t reg_addr, uint16_t reg_data); - -/** - * @brief HAL bit field read function. - * - * @param device Pointer to device structure - * @param reg_addr Register address to read - * @param bf_info Bit field information, byte0 - start bit, byte1 - bits length - * @param bf_val Pointer to bit field read back value - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_hal_bf_read(adi_adpd6000_device_t *device, uint32_t reg_addr, uint32_t bf_info, uint16_t *bf_val); - -/** - * @brief HAL bit field read function. - * - * @param device Pointer to device structure - * @param reg_addr Register address to read - * @param bf_info Bit field information, byte0 - start bit, byte1 - bits length - * @param bf_val Pointer to bit field read back value - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_hal_bf_write(adi_adpd6000_device_t *device, uint32_t reg_addr, uint32_t bf_info, uint16_t bf_val); - -/** - * @brief HAL error report function. - * - * @param device Pointer to device structure - * @param log_type Log type (LARK_LOG_NONE/MSG/WARN/ERROR/ALL) - * @param file_name Source file name where error happens - * @param line_num Source file line number where error happens - * @param func_name Function name where error happens - * @param var_name Variable name where error happens - * @param comment Error message - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_hal_error_report(adi_adpd6000_device_t* device, uint32_t log_type, - const char* file_name, const char* func_name, uint32_t line_num, const char* var_name, const char* comment); - -/** - * @brief HAL log write function. - * - * @param device Pointer to device structure - * @param log_type Log type (LARK_LOG_NONE/MSG/WARN/ERROR/ALL) - * @param comment Log message format - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_hal_log_write(adi_adpd6000_device_t *device, uint32_t log_type, const char* comment, ...); - -/** - * @brief HAL FIFO read function. - * - * @param device Pointer to device structure - * @param reg_addr Register address to read - * @param reg_data Pointer to save readback data - * @param len Data len - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_hal_fifo_read_bytes(adi_adpd6000_device_t *device, uint32_t reg_addr, uint8_t *reg_data, uint32_t len); - -/** - * @brief Get device id and device revision - * - * @param device Pointer to device structure - * @param chip_id Pointer to chip id value - * @param chip_rev Pointer to device revision value - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_get_id(adi_adpd6000_device_t *device, uint8_t *chip_id, uint8_t *chip_rev); - -/** - * @brief Get this sdk revision - * - * @param device Pointer to device structure - * @param rev Pointer to revision value (major.minor.build, one byte each) - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_lark_error_e - */ -int32_t adi_adpd6000_device_get_sdk_rev(adi_adpd6000_device_t *device, uint32_t *rev); - -/** - * @brief Reset this device in soft way - * - * @param device Pointer to device structure - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_sw_reset(adi_adpd6000_device_t *device); - -/** - * @brief Iint device, including trim, calibration... - * - * @param device Pointer to device structure - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_init(adi_adpd6000_device_t *device); - -/** - * @brief Enable slot operation mode - * - * @param device Pointer to device structure - * @param enable true - Go mode, false - standby mode - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_enable_slot_operation_mode_go(adi_adpd6000_device_t *device, bool enable); - -/** - * @brief Get FIFO data size - * - * @param device Pointer to device structure - * @param count Pointer to fifo count - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_get_fifo_count(adi_adpd6000_device_t *device, uint16_t *count); - -/** - * @brief Clear FIFO data - * - * @param device Pointer to device structure - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_clr_fifo(adi_adpd6000_device_t *device); - -/** - * @brief Set FIFO threshold - * - * @param device Pointer to device structure - * @param threshold Threshold - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_set_fifo_threshold(adi_adpd6000_device_t *device, uint16_t threshold); - -/** - * @brief Clear FIFO - * - * @param device Pointer to device structure - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_clr_fifo(adi_adpd6000_device_t *device); - -/** - * @brief Get FIFO count - * - * @param device Pointer to device structure - * @param count FIFO count - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_get_fifo_count(adi_adpd6000_device_t *device, uint16_t *count); - -/** - * @brief Read data from FIFO - * - * @param device Pointer to device structure - * @param data Pointer to fifo data - * @param len Data size - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_fifo_read_bytes(adi_adpd6000_device_t *device, uint8_t *data, uint32_t len); - -/** - * @brief Get FIFO interrupt status - * - * @param device Pointer to device structure - * @param status Pointer to interrupt status, bit0 - threshold int, bit1 - overflow int, bit2 - underflow - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_fifo_get_fifo_int_status(adi_adpd6000_device_t *device, uint8_t *status); - -/** - * @brief Enable/disable FIFO threshold interrupt - * - * @param device Pointer to device structure - * @param type @see adi_adpd6000_interrupt_type_e - * @param enable false - disable fifo threshold interrput, true - enable fifo threshold interrput - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_enable_fifo_thres_interrupt(adi_adpd6000_device_t *device, adi_adpd6000_interrupt_type_e type, bool enable); - -/** - * @brief Enable/disable automatic clearing of the FIFO threshold interrupt each time the FIFO is read. - * - * @param device Pointer to device structure - * @param enable false - disable, true - enable - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_enable_auto_clear_int(adi_adpd6000_device_t *device, bool enable); - -/** - * @brief Get sequence fifo configuration - * @param device Pointer to device structure - * @param fifo @see adi_adpd6000_fifo_config_t - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_get_sequence_fifo_config(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo); - -/** - * @brief Enable internal low frequency OSC - * - * @param device Pointer to device structure - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_enable_internal_osc_960k(adi_adpd6000_device_t *device); - -/** - * @brief Set high/low frequency OSC - * - * @param device Pointer to device structure - * @param high_osc High OSC trim - * @param low_osc Low OSC trim - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_set_osc_trim(adi_adpd6000_device_t *device, uint16_t high_osc, uint16_t low_osc); - -/** - * @brief Config external system clock source - * - * @param device Pointer to device structure - * @param src @see adi_adpd6000_sys_clock_src_e - * @param clk_pin gpio index, 0~2, gpiox for alternate clock - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_config_ext_clock(adi_adpd6000_device_t *device, adi_adpd6000_sys_clock_src_e src, uint8_t clk_pin); - -/** - * @brief Set timeslot frequency - * - * @param device Pointer to device structure - * @param sys_clk System clock - * @param freq timeslot drequency, unit: Hz - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_set_slot_freq(adi_adpd6000_device_t *device, uint32_t sys_clk, uint32_t freq); - -/** - * @brief Enable/disable sleep mode, if sleep mode, chip is in sleep before first timeslot sequence on GO mode - * - * @param device Pointer to device structure - * @param enable true - sleep mode - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_enbale_sleep_mode(adi_adpd6000_device_t *device, bool enable); - -/** - * @brief Read low osc trim code from efuse and write it to 960k osc trim register - * - * @param device Pointer to device structure - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_device_cal_960k_osc(adi_adpd6000_device_t *device); - -/** - * @brief Enable/disable ECG timeslot - * - * @param device Pointer to device structure - * @param enable false - disable, true - enable - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_enable_slot(adi_adpd6000_device_t *device, bool enable); - -/** - * @brief Select inputs of ECG, if short_en is true, internally short the inputs of ECG. The feature can be used for ECG noise measurements. - * - * @param device Pointer to device structure - * @param input_en false - disconnect, true - connect to the ECG inputs - * @param short_en false - disconnect, true - internally short the inputs of ECG - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_set_input_mux(adi_adpd6000_device_t *device, bool input_en, bool short_en); - -/** - * @brief Set ECG output data rate, 250Hz: 500Hz, 1KHz, 2KHz, 4KHz. - * - * @param device Pointer to device structure - * @param odr @see adi_adpd6000_ecg_odr_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_set_odr(adi_adpd6000_device_t *device, adi_adpd6000_ecg_odr_e odr); - -/** - * @brief Set ECG over sample rate, The ratio of ECG ODR to PPG ODR. This field must be configured as oversample = - * ecg_odr_sel/(960KHz/timeslot_period) if using internal lfosc or ratio = ecg_odr_sel/(external sync rate) - * if using external triggers. Result should be rounded up to the nearest integer. - * - * @param device Pointer to device structure - * @param oversample over sample ratio - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_set_oversample(adi_adpd6000_device_t *device, uint8_t oversample); - -/** - * @brief Enable/disable ECG stsus byte in FIFO output, status byte | ECG byte | ECG byte | ECG byte. - * - * @param device Pointer to device structure - * @param enable false - output data doesn't include status byte, true - output data includes status byte - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_enable_statusbyte(adi_adpd6000_device_t *device, bool enable); - -/** - * @brief Get ECG status, for detailed bitfield information, please check datasheet. - * - * @param device Pointer to device structure - * @param status Pointer to status - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_get_ecg_status(adi_adpd6000_device_t *device, uint8_t *status); - -/** - * @brief Clear status, for detailed bitfield information, please check datasheet. - * - * @param device Pointer to device structure - * @param status Status - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_clr_ecg_status(adi_adpd6000_device_t *device, uint8_t status); - -/** - * @brief Config RLD(right leg drive). - * - * @param device Pointer to device structure - * @param enable fasle - disable RLD loop, true - enable RLD loop - * @param rld @see adi_adpd6000_ecg_rld_output_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_set_rld(adi_adpd6000_device_t *device, bool enable, adi_adpd6000_ecg_rld_output_e rld); - -/** - * @brief Enable/disable the DC lead off for the RLD pin and set threshold. - * - * @param device Pointer to device structure - * @param enable false - DC lead off for RLD is disabled., true - DC lead off for RLD is enabled. - * @param th @see adi_adpd6000_ecg_dc_leadoff_th_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_leadoff_set_rld(adi_adpd6000_device_t *device, bool enable, adi_adpd6000_ecg_dc_leadoff_th_e th); - -/** - * @brief Set AC leadoff configuration. - * - * @param device Pointer to device structure - * @param enable Enable/disable the AC lead off detector for the ECG inputs.. - * @param mag AC lead off excite current magnitude.@see adi_adpd6000_ecg_ac_leadoff_mag_e - * @param th AC lead off Threshold selection for the ECG inputs. - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_leadoff_set_ac(adi_adpd6000_device_t *device, bool enable, adi_adpd6000_ecg_ac_leadoff_mag_e mag, uint8_t th); - -/** - * @brief Set DC leadoff configuration. - * - * @param device Pointer to device structure - * @param enable Enable/disable the AC lead off detector for the ECG inputs.. - * @param mag AC lead off excite current magnitude.@see adi_adpd6000_ecg_ac_leadoff_mag_e - * @param th @see adi_adpd6000_ecg_dc_leadoff_th_e. - * @param in_pol DC lead off output current polarity at ECGIP. @see adi_adpd6000_ecg_dc_leadoff_pol_e - * @param ip_pol DC lead off output current polarity at ECGIN. @see adi_adpd6000_ecg_dc_leadoff_pol_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_leadoff_set_dc(adi_adpd6000_device_t *device, bool enable, adi_adpd6000_ecg_dc_leadoff_mag_e mag, - adi_adpd6000_ecg_dc_leadoff_th_e th, adi_adpd6000_ecg_dc_leadoff_pol_e in_pol, adi_adpd6000_ecg_dc_leadoff_pol_e ip_pol); - -/** - * @brief Read raw ECG data from FIFO based on FIFO configuration, please call adi_adpd6000_device_get_sequence_fifo_config() before the function. - * - * @param device Pointer to device structure - * @param fifo @see adi_adpd6000_fifo_config_t - * @param ecg_data Pointer to ECG data - * @param ecg_num Pointer to ECG channel number - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_read_fifo(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, uint32_t *ecg_data, uint8_t *ecg_num); - -/** - * @brief Read ECG data and status from FIFO based on FIFO configuration, please call adi_adpd6000_device_get_sequence_fifo_config() before the function. - * - * @param device Pointer to device structure - * @param fifo @see adi_adpd6000_fifo_config_t - * @param ecg_data Pointer to ECG data - * @param status Pointer to status, if do not want get status, pass NULL - * @param ecg_num Pointer to ECG channel number - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ecg_read_data_status(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, uint32_t *ecg_data, uint8_t *status, uint8_t *ecg_num); - -/** - * @brief Set PPG time slot mode. - * - * @param device Pointer to device structure - * @param mode @see adi_adpd6000_ppg_slot_mode_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_slot_mode(adi_adpd6000_device_t *device, adi_adpd6000_ppg_slot_mode_e mode); - -/** - * @brief Get PPG aviable slot number - * - * @param device Pointer to device structure - * @param slot_num Pointer to slot number - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_get_slot_num(adi_adpd6000_device_t *device, uint8_t *slot_num); - -/** - * @brief Get PPG aviable channels number - * - * @param device Pointer to device structure - * @param channel_num Pointer to channel number - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_get_channel_num(adi_adpd6000_device_t *device, uint8_t *channel_num); - -/** - * @brief Select input resistor, ESD resistor or 6.5K resistor. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param res @see adi_adpd6000_ppg_tia_input_res_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_tia_set_input_res(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_tia_input_res_e res); - -/** - * @brief Set Tia resistor gain setting. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param channel Channel, 0 or 1 - * @param gain @see adi_adpd6000_ppg_tia_gain_res_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_tia_set_gain_res(adi_adpd6000_device_t *device, uint8_t slot, uint8_t channel, adi_adpd6000_ppg_tia_gain_res_e gain); - -/** - * @brief Set voltage trim for ref buffer. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param vref_value @see adi_adpd6000_ppg_tia_vref_value_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_tia_set_vref_value(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_tia_vref_value_e vref_value); - -/** - * @brief Set Vref pulse alternate value. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param alt_value @see adi_adpd6000_ppg_tia_vref_value_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_tia_set_vref_pulse_alt_value(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_tia_vref_value_e alt_value); - -/** - * @brief Enable/disable pulse Vref. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param enable false - No pulsing, true - Pulse Vref based on modulate pulse - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_tia_enable_vref_pulse(adi_adpd6000_device_t *device, uint8_t slot, bool enable); - -/** - * @brief Set input mode, single or diff mode. - * - * @param device Pointer to device structure - * @param pair_index Pair index, 0 for input 1 and 2, 1 for input 3 and 4 - * @param diff_en false - single ended inputs, true - diff ended inputs - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_enable_input_diff_mode(adi_adpd6000_device_t *device, uint8_t pair_index, bool diff_en); - -/** - * @brief Set input mux. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param pair_index Pair index, 0 for input 1 and 2, 1 for input 3 and 4 - * @param mux @see adi_adpd6000_ppg_input_mux_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_input_mux(adi_adpd6000_device_t *device, uint8_t slot, uint8_t pair_index, adi_adpd6000_ppg_input_mux_e mux); - -/** - * @brief Set input input pairs during sleep and when the inputs are not active.. - * - * @param device Pointer to device structure - * @param pair_index Pair index, 0 for input 1 and 2, 1 for input 3 and 4 - * @param mux @see adi_adpd6000_ppg_sleep_input_mux_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_sleep_input_mux(adi_adpd6000_device_t *device, uint8_t pair_index, adi_adpd6000_ppg_sleep_input_mux_e mux); - - -/** - * @brief Set Cathode2 sleep state. - * - * @param device Pointer to device structure - * @param cath1 @see adi_adpd6000_ppg_cath_type_e - * @param cath2 @see adi_adpd6000_ppg_cath_type_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_cathode(adi_adpd6000_device_t *device, adi_adpd6000_ppg_cath_type_e cath1, adi_adpd6000_ppg_cath_type_e cath2); - -/** - * @brief Config modulation. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param type @see adi_adpd6000_ppg_mod_type_e - * @param width Modulation pulse width - * @param offset Modulation pulse offset - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_cfg_modulator(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_mod_type_e type, uint8_t width, uint8_t offset); - -/** - * @brief Enable/disable amplifier. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param channel Channel, 0 or 1 - * @param enable false - disable amplifier, true - enable amplifier - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_enable_amp(adi_adpd6000_device_t *device, uint8_t slot, uint8_t channel, bool enable); - -/** - * @brief Set integrator resistor or buffer gain. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param channel Channel, 0 or 1 - * @param gain @see adi_adpd6000_ppg_integ_gain_res_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_integ_set_gain(adi_adpd6000_device_t *device, uint8_t slot, uint8_t channel, adi_adpd6000_ppg_integ_gain_res_e gain); - -/** - * @brief Set integrator capacitor. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param channel Channel, 0 or 1 - * @param cap @see adi_adpd6000_ppg_integ_cap_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_integ_select_cap(adi_adpd6000_device_t *device, uint8_t slot, uint8_t channel, adi_adpd6000_ppg_integ_cap_e cap); - -/** - * @brief Set Integrator clock width. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param width Integrator clock width - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_integ_set_width(adi_adpd6000_device_t *device, uint8_t slot, uint8_t width); - -/** - * @brief Set Integrator clock offset. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param offset Integrator clock offset - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_integ_set_offset(adi_adpd6000_device_t *device, uint8_t slot, uint16_t offset); - -/** - * @brief Skip the second intergrator clock or not. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param enable false - Use both generated integrator clocks, true - Skip the second integrator clock - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_integ_enable_single_clk(adi_adpd6000_device_t *device, uint8_t slot, bool enable); - -/** - * @brief Set current for LED1 or 2, Set to 0 to disable. Output current varies monotonically from 3ma to 200ma for values between 1 and 0x7F.. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param led_idx LED index, 0 or 1 - * @param current current - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_led_set_current(adi_adpd6000_device_t *device, uint8_t slot, uint8_t led_idx, uint8_t current); - -/** - * @brief Set LED output as A or B. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param led_idx LED index, 0 or 1 - * @param chl @see adi_adpd6000_ppg_led_channel_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_led_set_channel(adi_adpd6000_device_t *device, uint8_t slot, uint8_t led_idx, adi_adpd6000_ppg_led_channel_e chl); - -/** - * @brief Set LED operation mode, high SNR mode or low compliance mode. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param led_idx LED index, 0 or 1 - * @param mode @see adi_adpd6000_ppg_led_mode_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_led_set_mode(adi_adpd6000_device_t *device, uint8_t slot, uint8_t led_idx, adi_adpd6000_ppg_led_mode_e mode); - -/** - * @brief Set LED pulse width. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param width width - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_led_set_width(adi_adpd6000_device_t *device, uint8_t slot, uint8_t width); - -/** - * @brief Set LED pulse offset. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param offset LED pulse offset - * @param sec_offset LED pulse offset for the second LED phase - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_led_set_offset(adi_adpd6000_device_t *device, uint8_t slot, uint8_t offset, uint8_t sec_offset); - -/** - * @brief Set LED pulse count. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param num_int Number of integrates/adc or acquisition width. 0 not allowed. Number of analog integration cycles per adc conversion or the acquistion width for digital integration and impulse mod - * @param num_repeat Number of sequence repeats. 0 not allowed. Total number of pulses = num_int * num_repeat. - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_led_set_count(adi_adpd6000_device_t *device, uint8_t slot, uint16_t num_int, uint16_t num_repeat); - -/** - * @brief Set adjustment to ADC value. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param adjust1 This value is subtracted from the ADC value for channel 1 - * @param adjust2 This value is subtracted from the ADC value for channel 2 - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_adc_adjust(adi_adpd6000_device_t *device, uint8_t slot, uint16_t adjust1, uint16_t adjust2); -/** - * @brief Set Minimum Period for pulse repetition. Override for the automatically calculated period. - * Used in Float type operations to set the float time of second and subsequent floats using the formula float=min_period-mod_width.. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param min_period Minimum Period - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_minperiod(adi_adpd6000_device_t *device, uint8_t slot, uint16_t min_period); - -/** - * @brief Set Precondition value for enabled inputs during this timeslot. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param sel @see adi_adpd6000_ppg_precon_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_sel_precon(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_precon_e sel); - -/** - * @brief Bypass and input mux select. Integrator will be either an integrator or buffer based on mode and afe_int_c_buf for the active timeslot. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param sel @see adi_adpd6000_ppg_afe_path_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_sel_afe_path(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_afe_path_e sel); - -/** - * @brief Config VC. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param vc_index VC index, 0 or 1 - * @param vc_sel VC1 active state, @see adi_adpd6000_ppg_vc_select_e - * @param vc_alt VC1 alternate pulsed state, @see adi_adpd6000_ppg_vc_select_e - * @param vc_pulse VC1 pulse control, @see adi_adpd6000_ppg_vc_pulse_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_config_vc(adi_adpd6000_device_t *device, uint8_t slot, uint8_t vc_index, adi_adpd6000_ppg_vc_select_e vc_sel, - adi_adpd6000_ppg_vc_select_e vc_alt, adi_adpd6000_ppg_vc_pulse_e vc_pulse); - -/** - * @brief Enable/disable channel2. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param enable false - disable channels, true - enable channel2 - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_enable_channe2(adi_adpd6000_device_t *device, uint8_t slot, bool enable); - -/** - * @brief Set decimate Sample Divider (minus 1). - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param enable true - subsample using decimate_factor - * @param decimate decimate value, minus value is 1 - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_decimate(adi_adpd6000_device_t *device, uint8_t slot, bool enable, uint8_t decimate); - -/** - * @brief Set ambient cancellation DAC code, from 0 to 300uA with 0.6uA/LSB, if current is 10, it means 0.6 * 10 = 6uA. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param channel Channel 0 or 1 - * @param current Current code, current = current code * 0.6uA - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_ambientdac(adi_adpd6000_device_t *device, uint8_t slot, uint8_t channel, uint16_t current); - -/** - * @brief Set LED DC offset cancellation DAC code, from 0 to 190uA with 1.5uA/LSB. Set to 0 to disable. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param channel Channel 0 or 1 - * @param current Current code, current = current code * 1.5uA - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_dcdac(adi_adpd6000_device_t *device, uint8_t slot, uint8_t channel, uint8_t current); - -/** - * @brief Set timeslot data size. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param signal_size Signal size - * @param lit_size Lit size - * @param dark_size Dark size - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_data_size(adi_adpd6000_device_t *device, uint8_t slot, uint8_t signal_size, uint8_t lit_size, uint8_t dark_size); - -/** - * @brief Set timeslot data shift. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param signal_offset Signal shift - * @param lit_offset Lit shift - * @param dark_offset Dark shift - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_window_offset(adi_adpd6000_device_t *device, uint8_t slot, uint8_t signal_offset, uint8_t lit_offset, uint8_t dark_offset); - -/** - * @brief Set timeslot ambient callelation mode. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param alc_type @see adi_adpd6000_ppg_alc_type_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_alctype(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_alc_type_e alc_type); - -/** - * @brief Set timeslot ambient_callelation coarse loop width. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param alc_loop_width @see adi_adpd6000_ppg_alc_loop_width_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_alc_loop_width(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_alc_loop_width_e alc_loop_width); - -/** - * @brief Set timeslot sample mode. - * - * @param device Pointer to device structure - * @param slot Time slot index - * @param type @see adi_adpd6000_ppg_sample_type_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_set_sample_type(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_sample_type_e type); - -/** - * @brief Read PPG data from FIFO based on FIFO configuration - * - * @param device Pointer to device structure - * @param fifo @see adi_adpd6000_fifo_config_t - * @param signal_data Pointer to PPG signal data, if null, do not output the data - * @param dark_data Pointer to PPG dark data, if null, do not output the data - * @param lit_data Pointer to PPG lit data, if null, do not output the data - * @param ppg_num PPG channel number - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_read_fifo(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, uint32_t *signal_data, uint32_t *dark_data, uint32_t *lit_data, uint8_t *ppg_num); - -/** - * @brief Read PPG data from FIFO based on FIFO configuration - * - * @param device Pointer to device structure - * @param fifo @see adi_adpd6000_fifo_config_t - * @param signal_data Pointer to PPG signal data, if null, do not output the data - * @param dark_data Pointer to PPG dark data, if null, do not output the data - * @param lit_data Pointer to PPG lit data, if null, do not output the data - * @param slot_num PPG slot number - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_read_struct_fifo(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, adi_adpd6000_ppg_slot_data_t *signal_data, adi_adpd6000_ppg_slot_data_t *dark_data, adi_adpd6000_ppg_slot_data_t *lit_data, uint8_t *slot_num); - -/** - * @brief Init AGC configuration, fifo and ppg_cfg should be initilized before calling the function - * - * @param device Pointer to device structure - * @param fifo @see adi_adpd6000_fifo_config_t - * @param ppg_cfg @see adi_adpd6000_ppg_agc_cfg_t - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_agc_init(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, adi_adpd6000_ppg_agc_cfg_t *ppg_cfg); - -/** - * @brief AGC process function, fifo and ppg_cfg should be initilized before calling the function - * - * @param device Pointer to device structure - * @param fifo @see adi_adpd6000_fifo_config_t - * @param ppg_cfg @see adi_adpd6000_ppg_agc_cfg_t - * @param signal_data Pointer to signal data - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_ppg_agc_process(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, adi_adpd6000_ppg_agc_cfg_t *ppg_cfg, uint32_t *signal_data); - -/** - * @brief Set BioZ time slot mode. - * - * @param device Pointer to device structure - * @param mode @see adi_adpd6000_bioz_slot_mode_e - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -// int32_t adi_adpd6000_bioz_set_slot_mode(adi_adpd6000_device_t *device, adi_adpd6000_bioz_slot_mode_e mode); - -// /** -// * @brief Get BioZ aviable channels number -// * -// * @param device Pointer to device structure -// * @param slot_num Pointer to channel number -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_get_slot_num(adi_adpd6000_device_t *device, uint8_t *slot_num); - -// /** -// * @brief Set BioZ timeslot offset -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param offset Offset data -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_set_timeslot_offset(adi_adpd6000_device_t *device, uint8_t slot, uint16_t offset); - -// /** -// * @brief Config sinusoid amplitude, drequency, phase and offset -// * To get accurate DFT result and avoid spectral leakage, recommend freq/(DFT_FS/N) to be integer. N means the number of DFT input data, DFT_FS means DFT input data rate. DFT_FS can be different due to different input data sources. -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param amp Sinusoid Scales -// * @param freq Sinusoid Frequency, unit in Hz -// * @param phase Sinusoid offset, unit in degree -// * @param offset Sinusoid Phase Offset, unit in degree -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_cfg_wave(adi_adpd6000_device_t *device, uint8_t slot, uint16_t amp, uint32_t freq, uint16_t phase, uint16_t offset); - -// /** -// * @brief Config HPTIA feedback resistor and cap. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param res resistor, @see adi_adpd6000_bioz_tia_gain_res_e -// * @param cap cap value is from 0 to 15, 0 means off, unit in pf -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_tia_set_gain(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_tia_gain_res_e res, uint8_t cap); - -// /** -// * @brief Connect/disconnect Rcal to excbuf. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param enable false - disable tia low power mode, true - enable tia low power mode. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_tia_enable_lowpower(adi_adpd6000_device_t *device, uint8_t slot, bool enable); - -// /** -// * @brief Set DAC gain. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param gain DAC Gain Correction Factor. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_dac_set_gain(adi_adpd6000_device_t *device, uint8_t slot, uint16_t gain); - -// /** -// * @brief Bypass SINC3 filter. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param en_bypass true - Bypass SINC3 filter. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_bypass_sinc3_filter(adi_adpd6000_device_t *device, uint8_t slot, bool en_bypass); - -// /** -// * @brief Set SINC3 filter oversample rate. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param rate @see adi_adpd6000_bioz_sinc3_os_rate_e. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_sinc3_set_sample_rate(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_sinc3_os_rate_e rate); - -// /** -// * @brief Enable/disable average function. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param enable false - disable average, true - enable average. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_enable_average(adi_adpd6000_device_t *device, uint8_t slot, bool enable); - -// /** -// * @brief Sets number of samples used by averaging function. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param sample @see adi_adpd6000_bioz_sinc3_avg_e. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_set_average_sample(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_sinc3_avg_e sample); - -// /** -// * @brief Set DFT phase offset. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param phase unit in degree. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_dft_set_phase(adi_adpd6000_device_t *device, uint8_t slot, uint16_t phase); - -// /** -// * @brief Set DFT pointer number. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param num @see adi_adpd6000_bioz_dft_point_e. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_dft_set_point_number(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_dft_point_e num); - -// /** -// * @brief Enable/disable hanning window. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param enable false - disable hanning window, true - enable hanning winodw. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_dft_enable_hanning(adi_adpd6000_device_t *device, uint8_t slot, bool enable); - -// /** -// * @brief Enable/disable DAC reference. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param enable false - disable DAC reference, true - enable DAC reference. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_enable_dac_ref(adi_adpd6000_device_t *device, uint8_t slot, bool enable); - -// /** -// * @brief Enable/disable High Power TIA. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param enable false - disable High Power TIA, true - enable High Power TIA. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_enable_tia(adi_adpd6000_device_t *device, uint8_t slot, bool enable); - -// /** -// * @brief Enable/disable Excitation Buffer to drive the resistance under measurement.. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param enable false - disable Excitation Buffer, true - enable Excitation Buffer. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_enable_exbuf(adi_adpd6000_device_t *device, uint8_t slot, bool enable); - -// /** -// * @brief Power up/down PGA. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param enable false - Power down PGA, true - Power up PGA. -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_enable_pga(adi_adpd6000_device_t *device, uint8_t slot, bool enable); - -// /** -// * @brief Set front buffer mode. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param mode @see adi_adpd6000_bioz_amp_mode_e -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_set_amp_mode(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_amp_mode_e mode); - -// /** -// * @brief Set timeslot connection. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param mode @see adi_adpd6000_bioz_slot_connect_e -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_set_slot_connection(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_slot_connect_e mode); - -// /** -// * @brief Set bio-impedance exbuf input. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param input @see adi_adpd6000_bioz_imp_pin_e -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_sel_exbuf_input(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_imp_pin_e input); - -// /** -// * @brief Set bio-impedance hptia input. -// * -// * @param device Pointer to device structure -// * @param slot Time slot index -// * @param input @see adi_adpd6000_bioz_imp_pin_e -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_sel_tia_input(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_imp_pin_e input); - -// /** -// * @brief Read BioZ data from FIFO based on FIFO configuration -// * -// * @param device Pointer to device structure -// * @param fifo @see adi_adpd6000_fifo_config_t -// * @param bioz_real Pointer to BioZ real data -// * @param bioz_imag Pointer to BioZ image data -// * @param bioz_num BioZ channel number -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_read_fifo(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, uint32_t *bioz_real, uint32_t *bioz_imag, uint8_t *bioz_num); - -// /** -// * @brief Read BioZ data from FIFO based on FIFO configuration -// * -// * @param device Pointer to device structure -// * @param fifo @see adi_adpd6000_fifo_config_t -// * @param bioz @see adi_adpd6000_bioz_slot_data_t -// * @param bioz_num BioZ channel number -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_read_fifo_struct(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, adi_adpd6000_bioz_slot_data_t *bioz, uint8_t *bioz_num); - -// /** -// * @brief Cal amp and phase based on 6 timeslts mode -// * -// * @param Real Real data pointer -// * @param Image Image data pointer -// * @param amp Pointer to amp -// * @param phase Pointer to phase -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_cal_ms6(uint32_t Real[6], uint32_t Image[6], float *amp, float *phase); - -// /** -// * @brief Cal amp and phase based on 4 timeslts mode -// * -// * @param Real Real data pointer -// * @param Image Image data pointer -// * @param amp Pointer to amp -// * @param phase Pointer to phase -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_cal_ms4(uint32_t Real[4], uint32_t image[4], float *amp, float *phase); - -// /** -// * @brief Set cal resistor value, default value of the resistor is 2K -// * -// * @param rcal Resistor value -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_set_rcal(float rcal); - -// /** -// * @brief Get current cal resistor value -// * -// * @param rcal Pointer to resistor value -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_get_rcal(float *rcal); - -// /** -// * @brief Get internal cal resistor value, cal the function when sequence is stopped -// * -// * @param rcal Pointer to resistor value -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -// int32_t adi_adpd6000_bioz_get_internal_rcal(adi_adpd6000_device_t *device, float *rcal); - -// /** -// * @brief Set GPIO mode -// * -// * @param device Pointer to device structure -// * @param index Gpio index from 0 ~ 3 -// * @param mode @see adi_adpd6000_gpio_mode_e -// * -// * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e -// */ -int32_t adi_adpd6000_gpio_set_mode(adi_adpd6000_device_t *device, uint8_t index, adi_adpd6000_gpio_mode_e mode); - -/** - * @brief Set GPIO output mode - * - * @param device Pointer to device structure - * @param index Gpio index from 0 ~ 3 - * @param output GPIO output select, please refer to datasheet - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_gpio_set_output(adi_adpd6000_device_t *device, uint8_t index, uint8_t output); - -/** - * @brief Get GPIO input - * - * @param device Pointer to device structure - * @param index Gpio index from 0 ~ 3 - * @param input Pointer to input - * - * @return API_ADPD6000_ERROR_OK for success, @see adi_adpd6000_error_e - */ -int32_t adi_adpd6000_gpio_get_input(adi_adpd6000_device_t *device, uint8_t index, uint8_t *input); - -#ifdef __cplusplus -} -#endif - -#endif /*__ADI_ADPD6000_H__*/ -/*! @} */ diff --git a/src/adpd/lib/ADPD6000/adi_adpd6000_bf_reg.h b/src/adpd/lib/ADPD6000/adi_adpd6000_bf_reg.h deleted file mode 100644 index 7e04f64..0000000 --- a/src/adpd/lib/ADPD6000/adi_adpd6000_bf_reg.h +++ /dev/null @@ -1,2353 +0,0 @@ -/*! - * @brief SPI Register Definition Header File, automatically generated by - * yoda2h v1.3.7 at 6/1/2021 9:54:02 AM. - * - * @copyright copyright(c) 2018 - Analog Devices Inc.All Rights Reserved. - * This software is proprietary to Analog Devices, Inc. and its - * licensor. By using this software you agree to the terms of the - * associated analog devices software license agreement. - */ - -/*! - * @addtogroup ADPD6000_BF - * @{ - */ -#ifndef __ADI_ADPD6000_BF_REG_H__ -#define __ADI_ADPD6000_BF_REG_H__ - -/*============= D E F I N E S ==============*/ -#define REG_FIFO_STATUS_ADDR 0x00000000 -#define BF_FIFO_BYTE_COUNT_INFO 0x00000000, 0x00000B00 -#define BF_FIFO_INIT_DONE_STATUS_INFO 0x00000000, 0x0000010B -#define BF_INT_FIFO_TH_INFO 0x00000000, 0x0000010C -#define BF_INT_FIFO_OFLOW_INFO 0x00000000, 0x0000010D -#define BF_INT_FIFO_UFLOW_INFO 0x00000000, 0x0000010E -#define BF_CLEAR_FIFO_INFO 0x00000000, 0x0000010F - -#define REG_INT_STATUS_TS1_ADDR 0x00000001 -#define BF_INT_TSAT1_A_INFO 0x00000001, 0x00000100 -#define BF_INT_TSAT1_B_INFO 0x00000001, 0x00000101 -#define BF_INT_TSAT1_C_INFO 0x00000001, 0x00000102 -#define BF_INT_TSAT1_D_INFO 0x00000001, 0x00000103 -#define BF_INT_TSAT1_E_INFO 0x00000001, 0x00000104 -#define BF_INT_TSAT1_F_INFO 0x00000001, 0x00000105 -#define BF_INT_TSAT1_G_INFO 0x00000001, 0x00000106 -#define BF_INT_TSAT1_H_INFO 0x00000001, 0x00000107 -#define BF_INT_TSAT1_I_INFO 0x00000001, 0x00000108 -#define BF_INT_TSAT1_J_INFO 0x00000001, 0x00000109 -#define BF_INT_TSAT1_K_INFO 0x00000001, 0x0000010A -#define BF_INT_TSAT1_L_INFO 0x00000001, 0x0000010B - -#define REG_INT_STATUS_TS2_ADDR 0x00000002 -#define BF_INT_TSAT2_A_INFO 0x00000002, 0x00000100 -#define BF_INT_TSAT2_B_INFO 0x00000002, 0x00000101 -#define BF_INT_TSAT2_C_INFO 0x00000002, 0x00000102 -#define BF_INT_TSAT2_D_INFO 0x00000002, 0x00000103 -#define BF_INT_TSAT2_E_INFO 0x00000002, 0x00000104 -#define BF_INT_TSAT2_F_INFO 0x00000002, 0x00000105 -#define BF_INT_TSAT2_G_INFO 0x00000002, 0x00000106 -#define BF_INT_TSAT2_H_INFO 0x00000002, 0x00000107 -#define BF_INT_TSAT2_I_INFO 0x00000002, 0x00000108 -#define BF_INT_TSAT2_J_INFO 0x00000002, 0x00000109 -#define BF_INT_TSAT2_K_INFO 0x00000002, 0x0000010A -#define BF_INT_TSAT2_L_INFO 0x00000002, 0x0000010B - -#define REG_INT_STATUS_BIOZ_ADDR 0x00000003 -#define BF_INT_BIOZ_SAT_A_INFO 0x00000003, 0x00000100 -#define BF_INT_BIOZ_SAT_B_INFO 0x00000003, 0x00000101 -#define BF_INT_BIOZ_SAT_C_INFO 0x00000003, 0x00000102 -#define BF_INT_BIOZ_SAT_D_INFO 0x00000003, 0x00000103 -#define BF_INT_BIOZ_SAT_E_INFO 0x00000003, 0x00000104 -#define BF_INT_BIOZ_SAT_F_INFO 0x00000003, 0x00000105 - -#define REG_ECG_STATUS_ADDR 0x00000004 -#define BF_ECG_ADC_SAT_STATUS_INFO 0x00000004, 0x00000100 -#define BF_ECG_PGA_SAT_STATUS_INFO 0x00000004, 0x00000101 -#define BF_ECG_DCLO_STATUS_INFO 0x00000004, 0x00000102 -#define BF_ECG_RLD_DCLO_LO_STATUS_INFO 0x00000004, 0x00000103 -#define BF_ECG_RLD_DCLO_HI_STATUS_INFO 0x00000004, 0x00000104 -#define BF_ECGIP_DCLO_HI_STATUS_INFO 0x00000004, 0x00000105 -#define BF_ECGIN_DCLO_HI_STATUS_INFO 0x00000004, 0x00000106 -#define BF_ECG_ACLO_STATUS_INFO 0x00000004, 0x00000107 - -#define REG_GLOBAL_STATUS_ADDR 0x00000005 -#define BF_INVALID_CFG_STATUS_INFO 0x00000005, 0x00000100 - -#define REG_FIFO_TH_ADDR 0x00000006 -#define BF_FIFO_TH_INFO 0x00000006, 0x00000A00 - -#define REG_INT_ACLEAR_ADDR 0x00000007 -#define BF_INT_ACLEAR_FIFO_INFO 0x00000007, 0x0000010F - -#define REG_CHIP_ID_ADDR 0x00000008 -#define BF_CHIP_ID_INFO 0x00000008, 0x00000800 -#define BF_VERSION_INFO 0x00000008, 0x00000808 - -#define REG_OSC32M_ADDR 0x00000009 -#define BF_OSC_32M_FREQ_ADJ_INFO 0x00000009, 0x00000800 - -#define REG_OSC32M_CAL_ADDR 0x0000000A -#define BF_OSC_32M_CAL_COUNT_INFO 0x0000000A, 0x00000F00 -#define BF_OSC_32M_CAL_START_INFO 0x0000000A, 0x0000010F - -#define REG_OSC960K_ADDR 0x0000000B -#define BF_OSC_960K_FREQ_ADJ_INFO 0x0000000B, 0x00000A00 -#define BF_OSC_CAL_ENABLE_INFO 0x0000000B, 0x0000010A -#define BF_CAPTURE_TIMESTAMP_INFO 0x0000000B, 0x0000010F - -#define REG_TS_FREQ_ADDR 0x0000000D -#define BF_TIMESLOT_PERIOD_L_INFO 0x0000000D, 0x00001000 - -#define REG_TS_FREQH_ADDR 0x0000000E -#define BF_TIMESLOT_PERIOD_H_INFO 0x0000000E, 0x00000700 - -#define REG_SYS_CTL_ADDR 0x0000000F -#define BF_OSC_960K_EN_INFO 0x0000000F, 0x00000101 -#define BF_TM_CLK_GPIO_SEL_INFO 0x0000000F, 0x00000102 -#define BF_RANDOM_SLEEP_INFO 0x0000000F, 0x00000103 -#define BF_GO_SLEEP_INFO 0x0000000F, 0x00000104 -#define BF_ALT_CLK_GPIO_INFO 0x0000000F, 0x00000206 -#define BF_ALT_CLOCKS_INFO 0x0000000F, 0x00000308 -#define BF_SW_RESET_INFO 0x0000000F, 0x0000010F - -#define REG_OPMODE_ADDR 0x00000010 -#define BF_OP_MODE_INFO 0x00000010, 0x00000300 -#define BF_PPG_TIMESLOT_EN_INFO 0x00000010, 0x00000408 -#define BF_BIOZ_TIMESLOT_EN_INFO 0x00000010, 0x0000030C -#define BF_ECG_TIMESLOT_EN_INFO 0x00000010, 0x0000010F - -#define REG_STAMP_L_ADDR 0x00000011 -#define BF_TIMESTAMP_COUNT_L_INFO 0x00000011, 0x00001000 - -#define REG_STAMP_H_ADDR 0x00000012 -#define BF_TIMESTAMP_COUNT_H_INFO 0x00000012, 0x00001000 - -#define REG_STAMPDELTA_ADDR 0x00000013 -#define BF_TIMESTAMP_SLOT_DELTA_INFO 0x00000013, 0x00001000 - -#define REG_INT_ENABLE_XD_ADDR 0x00000014 -#define BF_INTX_EN_FIFO_OFLOW_INFO 0x00000014, 0x0000010D -#define BF_INTX_EN_FIFO_UFLOW_INFO 0x00000014, 0x0000010E -#define BF_INTX_EN_FIFO_TH_INFO 0x00000014, 0x0000010F - -#define REG_INT_ENABLE_YD_ADDR 0x00000015 -#define BF_INTY_EN_FIFO_OFLOW_INFO 0x00000015, 0x0000010D -#define BF_INTY_EN_FIFO_UFLOW_INFO 0x00000015, 0x0000010E -#define BF_INTY_EN_FIFO_TH_INFO 0x00000015, 0x0000010F - -#define REG_INT_ENABLE_XT1_ADDR 0x00000016 -#define BF_INTX_EN_TSAT1_A_INFO 0x00000016, 0x00000100 -#define BF_INTX_EN_TSAT1_B_INFO 0x00000016, 0x00000101 -#define BF_INTX_EN_TSAT1_C_INFO 0x00000016, 0x00000102 -#define BF_INTX_EN_TSAT1_D_INFO 0x00000016, 0x00000103 -#define BF_INTX_EN_TSAT1_E_INFO 0x00000016, 0x00000104 -#define BF_INTX_EN_TSAT1_F_INFO 0x00000016, 0x00000105 -#define BF_INTX_EN_TSAT1_G_INFO 0x00000016, 0x00000106 -#define BF_INTX_EN_TSAT1_H_INFO 0x00000016, 0x00000107 -#define BF_INTX_EN_TSAT1_I_INFO 0x00000016, 0x00000108 -#define BF_INTX_EN_TSAT1_J_INFO 0x00000016, 0x00000109 -#define BF_INTX_EN_TSAT1_K_INFO 0x00000016, 0x0000010A -#define BF_INTX_EN_TSAT1_L_INFO 0x00000016, 0x0000010B - -#define REG_INT_ENABLE_XT2_ADDR 0x00000017 -#define BF_INTX_EN_TSAT2_A_INFO 0x00000017, 0x00000100 -#define BF_INTX_EN_TSAT2_B_INFO 0x00000017, 0x00000101 -#define BF_INTX_EN_TSAT2_C_INFO 0x00000017, 0x00000102 -#define BF_INTX_EN_TSAT2_D_INFO 0x00000017, 0x00000103 -#define BF_INTX_EN_TSAT2_E_INFO 0x00000017, 0x00000104 -#define BF_INTX_EN_TSAT2_F_INFO 0x00000017, 0x00000105 -#define BF_INTX_EN_TSAT2_G_INFO 0x00000017, 0x00000106 -#define BF_INTX_EN_TSAT2_H_INFO 0x00000017, 0x00000107 -#define BF_INTX_EN_TSAT2_I_INFO 0x00000017, 0x00000108 -#define BF_INTX_EN_TSAT2_J_INFO 0x00000017, 0x00000109 -#define BF_INTX_EN_TSAT2_K_INFO 0x00000017, 0x0000010A -#define BF_INTX_EN_TSAT2_L_INFO 0x00000017, 0x0000010B - -#define REG_INT_ENABLE_YT1_ADDR 0x00000018 -#define BF_INTY_EN_TSAT1_A_INFO 0x00000018, 0x00000100 -#define BF_INTY_EN_TSAT1_B_INFO 0x00000018, 0x00000101 -#define BF_INTY_EN_TSAT1_C_INFO 0x00000018, 0x00000102 -#define BF_INTY_EN_TSAT1_D_INFO 0x00000018, 0x00000103 -#define BF_INTY_EN_TSAT1_E_INFO 0x00000018, 0x00000104 -#define BF_INTY_EN_TSAT1_F_INFO 0x00000018, 0x00000105 -#define BF_INTY_EN_TSAT1_G_INFO 0x00000018, 0x00000106 -#define BF_INTY_EN_TSAT1_H_INFO 0x00000018, 0x00000107 -#define BF_INTY_EN_TSAT1_I_INFO 0x00000018, 0x00000108 -#define BF_INTY_EN_TSAT1_J_INFO 0x00000018, 0x00000109 -#define BF_INTY_EN_TSAT1_K_INFO 0x00000018, 0x0000010A -#define BF_INTY_EN_TSAT1_L_INFO 0x00000018, 0x0000010B - -#define REG_INT_ENABLE_YT2_ADDR 0x00000019 -#define BF_INTY_EN_TSAT2_A_INFO 0x00000019, 0x00000100 -#define BF_INTY_EN_TSAT2_B_INFO 0x00000019, 0x00000101 -#define BF_INTY_EN_TSAT2_C_INFO 0x00000019, 0x00000102 -#define BF_INTY_EN_TSAT2_D_INFO 0x00000019, 0x00000103 -#define BF_INTY_EN_TSAT2_E_INFO 0x00000019, 0x00000104 -#define BF_INTY_EN_TSAT2_F_INFO 0x00000019, 0x00000105 -#define BF_INTY_EN_TSAT2_G_INFO 0x00000019, 0x00000106 -#define BF_INTY_EN_TSAT2_H_INFO 0x00000019, 0x00000107 -#define BF_INTY_EN_TSAT2_I_INFO 0x00000019, 0x00000108 -#define BF_INTY_EN_TSAT2_J_INFO 0x00000019, 0x00000109 -#define BF_INTY_EN_TSAT2_K_INFO 0x00000019, 0x0000010A -#define BF_INTY_EN_TSAT2_L_INFO 0x00000019, 0x0000010B - -#define REG_INT_ENABLE_X_BIOZ_ADDR 0x0000001A -#define BF_INTX_EN_BIOZ_SAT_A_INFO 0x0000001A, 0x00000100 -#define BF_INTX_EN_BIOZ_SAT_B_INFO 0x0000001A, 0x00000101 -#define BF_INTX_EN_BIOZ_SAT_C_INFO 0x0000001A, 0x00000102 -#define BF_INTX_EN_BIOZ_SAT_D_INFO 0x0000001A, 0x00000103 -#define BF_INTX_EN_BIOZ_SAT_E_INFO 0x0000001A, 0x00000104 -#define BF_INTX_EN_BIOZ_SAT_F_INFO 0x0000001A, 0x00000105 - -#define REG_INT_ENABLE_Y_BIOZ_ADDR 0x0000001B -#define BF_INTY_EN_BIOZ_SAT_A_INFO 0x0000001B, 0x00000100 -#define BF_INTY_EN_BIOZ_SAT_B_INFO 0x0000001B, 0x00000101 -#define BF_INTY_EN_BIOZ_SAT_C_INFO 0x0000001B, 0x00000102 -#define BF_INTY_EN_BIOZ_SAT_D_INFO 0x0000001B, 0x00000103 -#define BF_INTY_EN_BIOZ_SAT_E_INFO 0x0000001B, 0x00000104 -#define BF_INTY_EN_BIOZ_SAT_F_INFO 0x0000001B, 0x00000105 - -#define REG_FIFO_STATUS_BYTES_ADDR 0x0000001E -#define BF_ENA_STAT_TS1_INFO 0x0000001E, 0x00000106 -#define BF_ENA_STAT_TS2_INFO 0x0000001E, 0x00000107 -#define BF_ENA_STAT_TSX_INFO 0x0000001E, 0x00000108 -#define BF_ENA_STAT_ECG_INFO 0x0000001E, 0x00000109 - -#define REG_INPUT_SLEEP_ADDR 0x00000020 -#define BF_INP_SLEEP_12_INFO 0x00000020, 0x00000400 -#define BF_INP_SLEEP_34_INFO 0x00000020, 0x00000404 - -#define REG_INPUT_CFG_ADDR 0x00000021 -#define BF_PAIR12_INFO 0x00000021, 0x00000100 -#define BF_PAIR34_INFO 0x00000021, 0x00000101 -#define BF_VC1_SLEEP_INFO 0x00000021, 0x00000204 -#define BF_VC2_SLEEP_INFO 0x00000021, 0x00000206 - -#define REG_GPIO_CFG_ADDR 0x00000022 -#define BF_GPIO_PIN_CFG0_INFO 0x00000022, 0x00000300 -#define BF_GPIO_PIN_CFG1_INFO 0x00000022, 0x00000303 -#define BF_GPIO_PIN_CFG2_INFO 0x00000022, 0x00000306 -#define BF_GPIO_DRV_INFO 0x00000022, 0x0000020C -#define BF_GPIO_SLEW_INFO 0x00000022, 0x0000020E - -#define REG_GPIO01_ADDR 0x00000023 -#define BF_GPIOOUT0_INFO 0x00000023, 0x00000800 -#define BF_GPIOOUT1_INFO 0x00000023, 0x00000808 - -#define REG_GPIO23_ADDR 0x00000024 -#define BF_GPIOOUT2_INFO 0x00000024, 0x00000800 - -#define REG_GPIO_IN_ADDR 0x00000025 -#define BF_GPIO_INPUT_INFO 0x00000025, 0x00000400 - -#define REG_GPIO_EXT_ADDR 0x00000026 -#define BF_EXT_SYNC_GPIO_INFO 0x00000026, 0x00000200 -#define BF_EXT_SYNC_EN_INFO 0x00000026, 0x00000102 -#define BF_TIMESTAMP_GPIO_INFO 0x00000026, 0x00000204 -#define BF_TIMESTAMP_ALWAYS_EN_INFO 0x00000026, 0x00000106 -#define BF_TIMESTAMP_INV_INFO 0x00000026, 0x00000107 -#define BF_GOUT_SLEEP_INFO 0x00000026, 0x00000108 - -#define REG_FIFO_DATA_ADDR 0x0000002F -#define BF_FIFO_DATA_INFO 0x0000002F, 0x00001000 - -#define REG_SIGNAL1_L_A_ADDR 0x00000030 -#define BF_SIGNAL1_L_A_INFO 0x00000030, 0x00001000 - -#define REG_SIGNAL1_H_A_ADDR 0x00000031 -#define BF_SIGNAL1_H_A_INFO 0x00000031, 0x00001000 - -#define REG_SIGNAL2_L_A_ADDR 0x00000032 -#define BF_SIGNAL2_L_A_INFO 0x00000032, 0x00001000 - -#define REG_SIGNAL2_H_A_ADDR 0x00000033 -#define BF_SIGNAL2_H_A_INFO 0x00000033, 0x00001000 - -#define REG_DARK1_L_A_ADDR 0x00000034 -#define BF_DARK1_L_A_INFO 0x00000034, 0x00001000 - -#define REG_DARK1_H_A_ADDR 0x00000035 -#define BF_DARK1_H_A_INFO 0x00000035, 0x00001000 - -#define REG_DARK2_L_A_ADDR 0x00000036 -#define BF_DARK2_L_A_INFO 0x00000036, 0x00001000 - -#define REG_DARK2_H_A_ADDR 0x00000037 -#define BF_DARK2_H_A_INFO 0x00000037, 0x00001000 - -#define REG_SIGNAL1_L_B_ADDR 0x00000038 -#define BF_SIGNAL1_L_B_INFO 0x00000038, 0x00001000 - -#define REG_SIGNAL1_H_B_ADDR 0x00000039 -#define BF_SIGNAL1_H_B_INFO 0x00000039, 0x00001000 - -#define REG_SIGNAL2_L_B_ADDR 0x0000003A -#define BF_SIGNAL2_L_B_INFO 0x0000003A, 0x00001000 - -#define REG_SIGNAL2_H_B_ADDR 0x0000003B -#define BF_SIGNAL2_H_B_INFO 0x0000003B, 0x00001000 - -#define REG_DARK1_L_B_ADDR 0x0000003C -#define BF_DARK1_L_B_INFO 0x0000003C, 0x00001000 - -#define REG_DARK1_H_B_ADDR 0x0000003D -#define BF_DARK1_H_B_INFO 0x0000003D, 0x00001000 - -#define REG_DARK2_L_B_ADDR 0x0000003E -#define BF_DARK2_L_B_INFO 0x0000003E, 0x00001000 - -#define REG_DARK2_H_B_ADDR 0x0000003F -#define BF_DARK2_H_B_INFO 0x0000003F, 0x00001000 - - - - - - - - - - - - - - - - - - - - - - - - -#define REG_IO_ADJUST_ADDR 0x00000057 -#define BF_SPI_DRV_INFO 0x00000057, 0x00000200 -#define BF_SPI_SLEW_INFO 0x00000057, 0x00000202 -#define BF_LOW_IOVDD_EN_INFO 0x00000057, 0x00000106 - - -#define REG_I2C_KEY_ADDR 0x00000059 -#define BF_I2C_KEY_INFO 0x00000059, 0x00000C00 -#define BF_I2C_KEY_MATCH_INFO 0x00000059, 0x0000040C - -#define REG_I2C_ADDR_ADDR 0x0000005A -#define BF_I2C_SLAVE_ADDR_INFO 0x0000005A, 0x00000701 -#define BF_I2C_SLAVE_KEY2_INFO 0x0000005A, 0x00000808 - - -#define REG_ECG_ANA_CTRL_ADDR 0x00000100 -#define BF_ECG_RLD_EN_INFO 0x00000100, 0x00000100 -#define BF_ECG_RLD_SAT_THRESHOLD_INFO 0x00000100, 0x00000201 -#define BF_ECG_RLD_SAT_EN_INFO 0x00000100, 0x00000103 -#define BF_ECG_RLD_OUT_SEL_INFO 0x00000100, 0x00000204 -#define BF_ECG_RLD_OUT_DISCONNECT_INFO 0x00000100, 0x00000106 -#define BF_ECG_CGA_GAIN_INFO 0x00000100, 0x00000108 -#define BF_ECG_SHORT_INFO 0x00000100, 0x00000109 -#define BF_ECG_INP_CONNECT_INFO 0x00000100, 0x0000010A - -#define REG_ECG_LEADOFF_CTRL_ADDR 0x00000101 -#define BF_ECG_DCLO_THRESHOLD_INFO 0x00000101, 0x00000200 -#define BF_ECG_DCLO_POLARITY_IP_INFO 0x00000101, 0x00000102 -#define BF_ECG_DCLO_POLARITY_IN_INFO 0x00000101, 0x00000103 -#define BF_ECG_DCLO_MAG_INFO 0x00000101, 0x00000404 -#define BF_ECG_DCLO_EN_INFO 0x00000101, 0x00000108 -#define BF_ECG_ACLO_THRESHOLD_INFO 0x00000101, 0x00000309 -#define BF_ECG_ACLO_MAG_INFO 0x00000101, 0x0000030C -#define BF_ECG_ACLO_EN_INFO 0x00000101, 0x0000010F - -#define REG_ECG_DIG_CTRL1_ADDR 0x00000102 -#define BF_ECG_ODR_SEL_INFO 0x00000102, 0x00000300 -#define BF_ECG_OVERSAMPLING_RATIO_INFO 0x00000102, 0x00000603 - -#define REG_ECG_DIG_CTRL2_ADDR 0x00000103 -#define BF_ECG_CAL_GAIN_INFO 0x00000103, 0x00000400 -#define BF_ECG_BYPASS_EQLZR_INFO 0x00000103, 0x00000104 -#define BF_ECG_ACLO_INV_INFO 0x00000103, 0x00000105 - - - - - - - - - - - - - - - - - - - - - - -#define REG_TS_CTRL_A_ADDR 0x00000120 -#define BF_TIMESLOT_OFFSET_A_INFO 0x00000120, 0x00000A00 -#define BF_INPUT_R_SELECT_A_INFO 0x00000120, 0x0000010A -#define BF_SAMPLE_TYPE_A_INFO 0x00000120, 0x0000030B -#define BF_CH2_EN_A_INFO 0x00000120, 0x0000010E -#define BF_SUBSAMPLE_A_INFO 0x00000120, 0x0000010F - -#define REG_COUNTS_A_ADDR 0x0000012A -#define BF_NUM_REPEAT_A_INFO 0x0000012A, 0x00000800 -#define BF_NUM_INT_A_INFO 0x0000012A, 0x00000808 - -#define REG_TS_CTRL_F_ADDR 0x000001C0 -#define BF_TIMESLOT_OFFSET_F_INFO 0x000001C0, 0x00000A00 -#define BF_INPUT_R_SELECT_F_INFO 0x000001C0, 0x0000010A -#define BF_SAMPLE_TYPE_F_INFO 0x000001C0, 0x0000030B -#define BF_CH2_EN_F_INFO 0x000001C0, 0x0000010E -#define BF_SUBSAMPLE_F_INFO 0x000001C0, 0x0000010F - -#define REG_TS_PATH_F_ADDR 0x000001C1 -#define BF_AFE_PATH_CFG_F_INFO 0x000001C1, 0x00000700 -#define BF_AFE_INT_C_BUF_F_INFO 0x000001C1, 0x00000108 -#define BF_GOUT_F_INFO 0x000001C1, 0x00000109 -#define BF_AMBIENT_CANCELLATION_F_INFO 0x000001C1, 0x0000020A -#define BF_PRE_WIDTH_F_INFO 0x000001C1, 0x0000040C - -#define REG_INPUTS_F_ADDR 0x000001C2 -#define BF_INP12_F_INFO 0x000001C2, 0x00000400 -#define BF_INP34_F_INFO 0x000001C2, 0x00000404 - -#define REG_CATHODE_F_ADDR 0x000001C3 -#define BF_VC1_SEL_F_INFO 0x000001C3, 0x00000200 -#define BF_VC1_ALT_F_INFO 0x000001C3, 0x00000202 -#define BF_VC1_PULSE_F_INFO 0x000001C3, 0x00000204 -#define BF_VC2_SEL_F_INFO 0x000001C3, 0x00000206 -#define BF_VC2_ALT_F_INFO 0x000001C3, 0x00000208 -#define BF_VC2_PULSE_F_INFO 0x000001C3, 0x0000020A -#define BF_PRECON_F_INFO 0x000001C3, 0x0000030C - -#define REG_AFE_TRIM1_F_ADDR 0x000001C4 -#define BF_TIA_GAIN_CH1_F_INFO 0x000001C4, 0x00000300 -#define BF_TIA_GAIN_CH2_F_INFO 0x000001C4, 0x00000303 -#define BF_VREF_PULSE_VAL_F_INFO 0x000001C4, 0x00000206 -#define BF_AFE_TRIM_VREF_F_INFO 0x000001C4, 0x00000208 -#define BF_VREF_PULSE_F_INFO 0x000001C4, 0x0000010A -#define BF_AFE_TRIM_INT1_F_INFO 0x000001C4, 0x0000020B -#define BF_AFE_TRIM_INT2_F_INFO 0x000001C4, 0x0000020D -#define BF_AFE_TIA_SAT_DETECT_EN_F_INFO 0x000001C4, 0x0000010F - -#define REG_AFE_TRIM2_F_ADDR 0x000001C5 -#define BF_AFE_TRIM_INT1_CAP_F_INFO 0x000001C5, 0x0000010C -#define BF_AFE_TRIM_INT2_CAP_F_INFO 0x000001C5, 0x0000010D - -#define REG_AFE_DAC1_F_ADDR 0x000001C6 -#define BF_DAC_LED_DC_CH1_F_INFO 0x000001C6, 0x00000700 -#define BF_DAC_AMBIENT_CH1_F_INFO 0x000001C6, 0x00000907 - -#define REG_AFE_DAC2_F_ADDR 0x000001C7 -#define BF_DAC_LED_DC_CH2_F_INFO 0x000001C7, 0x00000700 -#define BF_DAC_AMBIENT_CH2_F_INFO 0x000001C7, 0x00000907 - -#define REG_LED_POW12_F_ADDR 0x000001C8 -#define BF_LED_CURRENT1_F_INFO 0x000001C8, 0x00000700 -#define BF_LED_DRIVESIDE1_F_INFO 0x000001C8, 0x00000107 -#define BF_LED_CURRENT2_F_INFO 0x000001C8, 0x00000708 -#define BF_LED_DRIVESIDE2_F_INFO 0x000001C8, 0x0000010F - -#define REG_LED_MODE_F_ADDR 0x000001C9 -#define BF_LED_MODE1_F_INFO 0x000001C9, 0x00000100 -#define BF_LED_MODE2_F_INFO 0x000001C9, 0x00000101 - -#define REG_COUNTS_F_ADDR 0x000001CA -#define BF_NUM_REPEAT_F_INFO 0x000001CA, 0x00000800 -#define BF_NUM_INT_F_INFO 0x000001CA, 0x00000808 - -#define REG_PERIOD_F_ADDR 0x000001CB -#define BF_MIN_PERIOD_F_INFO 0x000001CB, 0x00000A00 -#define BF_MOD_TYPE_F_INFO 0x000001CB, 0x0000020C -#define BF_COARSE_LOOP_WIDTH_F_INFO 0x000001CB, 0x0000010E - -#define REG_LED_PULSE1_F_ADDR 0x000001CC -#define BF_LED_OFFSET_F_INFO 0x000001CC, 0x00000800 -#define BF_LED_WIDTH_F_INFO 0x000001CC, 0x00000808 - -#define REG_LED_PULSE2_F_ADDR 0x000001CD -#define BF_LED_SECOND_OFFSET_F_INFO 0x000001CD, 0x00000800 - -#define REG_INTEG_WIDTH_F_ADDR 0x000001CE -#define BF_INTEG_WIDTH_F_INFO 0x000001CE, 0x00000500 -#define BF_ADC_COUNT_F_INFO 0x000001CE, 0x00000205 -#define BF_CH1_AMP_DISABLE_F_INFO 0x000001CE, 0x00000207 -#define BF_CH2_AMP_DISABLE_F_INFO 0x000001CE, 0x0000020B -#define BF_SINGLE_INTEG_F_INFO 0x000001CE, 0x0000010F - -#define REG_INTEG_OFFSET_F_ADDR 0x000001CF -#define BF_INTEG_FINE_OFFSET_F_INFO 0x000001CF, 0x00000500 -#define BF_INTEG_OFFSET_F_INFO 0x000001CF, 0x00000805 - -#define REG_PERIOD_A_ADDR 0x0000012B -#define BF_MIN_PERIOD_A_INFO 0x0000012B, 0x00000A00 -#define BF_MOD_TYPE_A_INFO 0x0000012B, 0x0000020C -#define BF_COARSE_LOOP_WIDTH_A_INFO 0x0000012B, 0x0000010E - -#define REG_MOD_PULSE_F_ADDR 0x000001D0 -#define BF_MOD_OFFSET_F_INFO 0x000001D0, 0x00000800 -#define BF_MOD_WIDTH_F_INFO 0x000001D0, 0x00000808 - -#define REG_PATTERN1_F_ADDR 0x000001D1 -#define BF_REVERSE_INTEG_F_INFO 0x000001D1, 0x00000400 -#define BF_SUBTRACT_F_INFO 0x000001D1, 0x00000404 -#define BF_MOD_DISABLE_F_INFO 0x000001D1, 0x00000408 -#define BF_LED_DISABLE_F_INFO 0x000001D1, 0x0000040C - - -#define REG_ADC_OFF1_F_ADDR 0x000001D3 -#define BF_CH1_ADC_ADJUST_F_INFO 0x000001D3, 0x00000E00 - -#define REG_ADC_OFF2_F_ADDR 0x000001D4 -#define BF_CH2_ADC_ADJUST_F_INFO 0x000001D4, 0x00000E00 - -#define REG_DATA1_F_ADDR 0x000001D5 -#define BF_SIGNAL_SIZE_F_INFO 0x000001D5, 0x00000300 -#define BF_SIGNAL_SHIFT_F_INFO 0x000001D5, 0x00000503 -#define BF_DARK_SIZE_F_INFO 0x000001D5, 0x00000308 -#define BF_DARK_SHIFT_F_INFO 0x000001D5, 0x0000050B - -#define REG_DATA2_F_ADDR 0x000001D6 -#define BF_LIT_SIZE_F_INFO 0x000001D6, 0x00000300 -#define BF_LIT_SHIFT_F_INFO 0x000001D6, 0x00000503 - -#define REG_DECIMATE_F_ADDR 0x000001D7 -#define BF_DECIMATE_FACTOR_F_INFO 0x000001D7, 0x00000704 - -#define REG_DIGINT_LIT_F_ADDR 0x000001D8 -#define BF_LIT_OFFSET_F_INFO 0x000001D8, 0x00000900 - -#define REG_DIGINT_DARK_F_ADDR 0x000001D9 -#define BF_DARK1_OFFSET_F_INFO 0x000001D9, 0x00000700 -#define BF_DARK2_OFFSET_F_INFO 0x000001D9, 0x00000907 - -#define REG_LED_PULSE1_A_ADDR 0x0000012C -#define BF_LED_OFFSET_A_INFO 0x0000012C, 0x00000800 -#define BF_LED_WIDTH_A_INFO 0x0000012C, 0x00000808 - -#define REG_TS_CTRL_G_ADDR 0x000001E0 -#define BF_TIMESLOT_OFFSET_G_INFO 0x000001E0, 0x00000A00 -#define BF_INPUT_R_SELECT_G_INFO 0x000001E0, 0x0000010A -#define BF_SAMPLE_TYPE_G_INFO 0x000001E0, 0x0000030B -#define BF_CH2_EN_G_INFO 0x000001E0, 0x0000010E -#define BF_SUBSAMPLE_G_INFO 0x000001E0, 0x0000010F - -#define REG_TS_PATH_G_ADDR 0x000001E1 -#define BF_AFE_PATH_CFG_G_INFO 0x000001E1, 0x00000700 -#define BF_AFE_INT_C_BUF_G_INFO 0x000001E1, 0x00000108 -#define BF_GOUT_G_INFO 0x000001E1, 0x00000109 -#define BF_AMBIENT_CANCELLATION_G_INFO 0x000001E1, 0x0000020A -#define BF_PRE_WIDTH_G_INFO 0x000001E1, 0x0000040C - -#define REG_INPUTS_G_ADDR 0x000001E2 -#define BF_INP12_G_INFO 0x000001E2, 0x00000400 -#define BF_INP34_G_INFO 0x000001E2, 0x00000404 - -#define REG_CATHODE_G_ADDR 0x000001E3 -#define BF_VC1_SEL_G_INFO 0x000001E3, 0x00000200 -#define BF_VC1_ALT_G_INFO 0x000001E3, 0x00000202 -#define BF_VC1_PULSE_G_INFO 0x000001E3, 0x00000204 -#define BF_VC2_SEL_G_INFO 0x000001E3, 0x00000206 -#define BF_VC2_ALT_G_INFO 0x000001E3, 0x00000208 -#define BF_VC2_PULSE_G_INFO 0x000001E3, 0x0000020A -#define BF_PRECON_G_INFO 0x000001E3, 0x0000030C - -#define REG_AFE_TRIM1_G_ADDR 0x000001E4 -#define BF_TIA_GAIN_CH1_G_INFO 0x000001E4, 0x00000300 -#define BF_TIA_GAIN_CH2_G_INFO 0x000001E4, 0x00000303 -#define BF_VREF_PULSE_VAL_G_INFO 0x000001E4, 0x00000206 -#define BF_AFE_TRIM_VREF_G_INFO 0x000001E4, 0x00000208 -#define BF_VREF_PULSE_G_INFO 0x000001E4, 0x0000010A -#define BF_AFE_TRIM_INT1_G_INFO 0x000001E4, 0x0000020B -#define BF_AFE_TRIM_INT2_G_INFO 0x000001E4, 0x0000020D -#define BF_AFE_TIA_SAT_DETECT_EN_G_INFO 0x000001E4, 0x0000010F - -#define REG_AFE_TRIM2_G_ADDR 0x000001E5 -#define BF_AFE_TRIM_INT1_CAP_G_INFO 0x000001E5, 0x0000010C -#define BF_AFE_TRIM_INT2_CAP_G_INFO 0x000001E5, 0x0000010D - -#define REG_AFE_DAC1_G_ADDR 0x000001E6 -#define BF_DAC_LED_DC_CH1_G_INFO 0x000001E6, 0x00000700 -#define BF_DAC_AMBIENT_CH1_G_INFO 0x000001E6, 0x00000907 - -#define REG_AFE_DAC2_G_ADDR 0x000001E7 -#define BF_DAC_LED_DC_CH2_G_INFO 0x000001E7, 0x00000700 -#define BF_DAC_AMBIENT_CH2_G_INFO 0x000001E7, 0x00000907 - -#define REG_LED_POW12_G_ADDR 0x000001E8 -#define BF_LED_CURRENT1_G_INFO 0x000001E8, 0x00000700 -#define BF_LED_DRIVESIDE1_G_INFO 0x000001E8, 0x00000107 -#define BF_LED_CURRENT2_G_INFO 0x000001E8, 0x00000708 -#define BF_LED_DRIVESIDE2_G_INFO 0x000001E8, 0x0000010F - -#define REG_LED_MODE_G_ADDR 0x000001E9 -#define BF_LED_MODE1_G_INFO 0x000001E9, 0x00000100 -#define BF_LED_MODE2_G_INFO 0x000001E9, 0x00000101 - -#define REG_COUNTS_G_ADDR 0x000001EA -#define BF_NUM_REPEAT_G_INFO 0x000001EA, 0x00000800 -#define BF_NUM_INT_G_INFO 0x000001EA, 0x00000808 - -#define REG_PERIOD_G_ADDR 0x000001EB -#define BF_MIN_PERIOD_G_INFO 0x000001EB, 0x00000A00 -#define BF_MOD_TYPE_G_INFO 0x000001EB, 0x0000020C -#define BF_COARSE_LOOP_WIDTH_G_INFO 0x000001EB, 0x0000010E - -#define REG_LED_PULSE1_G_ADDR 0x000001EC -#define BF_LED_OFFSET_G_INFO 0x000001EC, 0x00000800 -#define BF_LED_WIDTH_G_INFO 0x000001EC, 0x00000808 - -#define REG_LED_PULSE2_G_ADDR 0x000001ED -#define BF_LED_SECOND_OFFSET_G_INFO 0x000001ED, 0x00000800 - -#define REG_INTEG_WIDTH_G_ADDR 0x000001EE -#define BF_INTEG_WIDTH_G_INFO 0x000001EE, 0x00000500 -#define BF_ADC_COUNT_G_INFO 0x000001EE, 0x00000205 -#define BF_CH1_AMP_DISABLE_G_INFO 0x000001EE, 0x00000207 -#define BF_CH2_AMP_DISABLE_G_INFO 0x000001EE, 0x0000020B -#define BF_SINGLE_INTEG_G_INFO 0x000001EE, 0x0000010F - -#define REG_INTEG_OFFSET_G_ADDR 0x000001EF -#define BF_INTEG_FINE_OFFSET_G_INFO 0x000001EF, 0x00000500 -#define BF_INTEG_OFFSET_G_INFO 0x000001EF, 0x00000805 - -#define REG_LED_PULSE2_A_ADDR 0x0000012D -#define BF_LED_SECOND_OFFSET_A_INFO 0x0000012D, 0x00000800 - -#define REG_MOD_PULSE_G_ADDR 0x000001F0 -#define BF_MOD_OFFSET_G_INFO 0x000001F0, 0x00000800 -#define BF_MOD_WIDTH_G_INFO 0x000001F0, 0x00000808 - -#define REG_PATTERN1_G_ADDR 0x000001F1 -#define BF_REVERSE_INTEG_G_INFO 0x000001F1, 0x00000400 -#define BF_SUBTRACT_G_INFO 0x000001F1, 0x00000404 -#define BF_MOD_DISABLE_G_INFO 0x000001F1, 0x00000408 -#define BF_LED_DISABLE_G_INFO 0x000001F1, 0x0000040C - - -#define REG_ADC_OFF1_G_ADDR 0x000001F3 -#define BF_CH1_ADC_ADJUST_G_INFO 0x000001F3, 0x00000E00 - -#define REG_ADC_OFF2_G_ADDR 0x000001F4 -#define BF_CH2_ADC_ADJUST_G_INFO 0x000001F4, 0x00000E00 - -#define REG_DATA1_G_ADDR 0x000001F5 -#define BF_SIGNAL_SIZE_G_INFO 0x000001F5, 0x00000300 -#define BF_SIGNAL_SHIFT_G_INFO 0x000001F5, 0x00000503 -#define BF_DARK_SIZE_G_INFO 0x000001F5, 0x00000308 -#define BF_DARK_SHIFT_G_INFO 0x000001F5, 0x0000050B - -#define REG_DATA2_G_ADDR 0x000001F6 -#define BF_LIT_SIZE_G_INFO 0x000001F6, 0x00000300 -#define BF_LIT_SHIFT_G_INFO 0x000001F6, 0x00000503 - -#define REG_DECIMATE_G_ADDR 0x000001F7 -#define BF_DECIMATE_FACTOR_G_INFO 0x000001F7, 0x00000704 - -#define REG_DIGINT_LIT_G_ADDR 0x000001F8 -#define BF_LIT_OFFSET_G_INFO 0x000001F8, 0x00000900 - -#define REG_DIGINT_DARK_G_ADDR 0x000001F9 -#define BF_DARK1_OFFSET_G_INFO 0x000001F9, 0x00000700 -#define BF_DARK2_OFFSET_G_INFO 0x000001F9, 0x00000907 - -#define REG_INTEG_WIDTH_A_ADDR 0x0000012E -#define BF_INTEG_WIDTH_A_INFO 0x0000012E, 0x00000500 -#define BF_ADC_COUNT_A_INFO 0x0000012E, 0x00000205 -#define BF_CH1_AMP_DISABLE_A_INFO 0x0000012E, 0x00000207 -#define BF_CH2_AMP_DISABLE_A_INFO 0x0000012E, 0x0000020B -#define BF_SINGLE_INTEG_A_INFO 0x0000012E, 0x0000010F - -#define REG_TS_CTRL_H_ADDR 0x00000200 -#define BF_TIMESLOT_OFFSET_H_INFO 0x00000200, 0x00000A00 -#define BF_INPUT_R_SELECT_H_INFO 0x00000200, 0x0000010A -#define BF_SAMPLE_TYPE_H_INFO 0x00000200, 0x0000030B -#define BF_CH2_EN_H_INFO 0x00000200, 0x0000010E -#define BF_SUBSAMPLE_H_INFO 0x00000200, 0x0000010F - -#define REG_TS_PATH_H_ADDR 0x00000201 -#define BF_AFE_PATH_CFG_H_INFO 0x00000201, 0x00000700 -#define BF_AFE_INT_C_BUF_H_INFO 0x00000201, 0x00000108 -#define BF_GOUT_H_INFO 0x00000201, 0x00000109 -#define BF_AMBIENT_CANCELLATION_H_INFO 0x00000201, 0x0000020A -#define BF_PRE_WIDTH_H_INFO 0x00000201, 0x0000040C - -#define REG_INPUTS_H_ADDR 0x00000202 -#define BF_INP12_H_INFO 0x00000202, 0x00000400 -#define BF_INP34_H_INFO 0x00000202, 0x00000404 - -#define REG_CATHODE_H_ADDR 0x00000203 -#define BF_VC1_SEL_H_INFO 0x00000203, 0x00000200 -#define BF_VC1_ALT_H_INFO 0x00000203, 0x00000202 -#define BF_VC1_PULSE_H_INFO 0x00000203, 0x00000204 -#define BF_VC2_SEL_H_INFO 0x00000203, 0x00000206 -#define BF_VC2_ALT_H_INFO 0x00000203, 0x00000208 -#define BF_VC2_PULSE_H_INFO 0x00000203, 0x0000020A -#define BF_PRECON_H_INFO 0x00000203, 0x0000030C - -#define REG_AFE_TRIM1_H_ADDR 0x00000204 -#define BF_TIA_GAIN_CH1_H_INFO 0x00000204, 0x00000300 -#define BF_TIA_GAIN_CH2_H_INFO 0x00000204, 0x00000303 -#define BF_VREF_PULSE_VAL_H_INFO 0x00000204, 0x00000206 -#define BF_AFE_TRIM_VREF_H_INFO 0x00000204, 0x00000208 -#define BF_VREF_PULSE_H_INFO 0x00000204, 0x0000010A -#define BF_AFE_TRIM_INT1_H_INFO 0x00000204, 0x0000020B -#define BF_AFE_TRIM_INT2_H_INFO 0x00000204, 0x0000020D -#define BF_AFE_TIA_SAT_DETECT_EN_H_INFO 0x00000204, 0x0000010F - -#define REG_AFE_TRIM2_H_ADDR 0x00000205 -#define BF_AFE_TRIM_INT1_CAP_H_INFO 0x00000205, 0x0000010C -#define BF_AFE_TRIM_INT2_CAP_H_INFO 0x00000205, 0x0000010D - -#define REG_AFE_DAC1_H_ADDR 0x00000206 -#define BF_DAC_LED_DC_CH1_H_INFO 0x00000206, 0x00000700 -#define BF_DAC_AMBIENT_CH1_H_INFO 0x00000206, 0x00000907 - -#define REG_AFE_DAC2_H_ADDR 0x00000207 -#define BF_DAC_LED_DC_CH2_H_INFO 0x00000207, 0x00000700 -#define BF_DAC_AMBIENT_CH2_H_INFO 0x00000207, 0x00000907 - -#define REG_LED_POW12_H_ADDR 0x00000208 -#define BF_LED_CURRENT1_H_INFO 0x00000208, 0x00000700 -#define BF_LED_DRIVESIDE1_H_INFO 0x00000208, 0x00000107 -#define BF_LED_CURRENT2_H_INFO 0x00000208, 0x00000708 -#define BF_LED_DRIVESIDE2_H_INFO 0x00000208, 0x0000010F - -#define REG_LED_MODE_H_ADDR 0x00000209 -#define BF_LED_MODE1_H_INFO 0x00000209, 0x00000100 -#define BF_LED_MODE2_H_INFO 0x00000209, 0x00000101 - -#define REG_COUNTS_H_ADDR 0x0000020A -#define BF_NUM_REPEAT_H_INFO 0x0000020A, 0x00000800 -#define BF_NUM_INT_H_INFO 0x0000020A, 0x00000808 - -#define REG_PERIOD_H_ADDR 0x0000020B -#define BF_MIN_PERIOD_H_INFO 0x0000020B, 0x00000A00 -#define BF_MOD_TYPE_H_INFO 0x0000020B, 0x0000020C -#define BF_COARSE_LOOP_WIDTH_H_INFO 0x0000020B, 0x0000010E - -#define REG_LED_PULSE1_H_ADDR 0x0000020C -#define BF_LED_OFFSET_H_INFO 0x0000020C, 0x00000800 -#define BF_LED_WIDTH_H_INFO 0x0000020C, 0x00000808 - -#define REG_LED_PULSE2_H_ADDR 0x0000020D -#define BF_LED_SECOND_OFFSET_H_INFO 0x0000020D, 0x00000800 - -#define REG_INTEG_WIDTH_H_ADDR 0x0000020E -#define BF_INTEG_WIDTH_H_INFO 0x0000020E, 0x00000500 -#define BF_ADC_COUNT_H_INFO 0x0000020E, 0x00000205 -#define BF_CH1_AMP_DISABLE_H_INFO 0x0000020E, 0x00000207 -#define BF_CH2_AMP_DISABLE_H_INFO 0x0000020E, 0x0000020B -#define BF_SINGLE_INTEG_H_INFO 0x0000020E, 0x0000010F - -#define REG_INTEG_OFFSET_H_ADDR 0x0000020F -#define BF_INTEG_FINE_OFFSET_H_INFO 0x0000020F, 0x00000500 -#define BF_INTEG_OFFSET_H_INFO 0x0000020F, 0x00000805 - -#define REG_INTEG_OFFSET_A_ADDR 0x0000012F -#define BF_INTEG_FINE_OFFSET_A_INFO 0x0000012F, 0x00000500 -#define BF_INTEG_OFFSET_A_INFO 0x0000012F, 0x00000805 - -#define REG_MOD_PULSE_H_ADDR 0x00000210 -#define BF_MOD_OFFSET_H_INFO 0x00000210, 0x00000800 -#define BF_MOD_WIDTH_H_INFO 0x00000210, 0x00000808 - -#define REG_PATTERN1_H_ADDR 0x00000211 -#define BF_REVERSE_INTEG_H_INFO 0x00000211, 0x00000400 -#define BF_SUBTRACT_H_INFO 0x00000211, 0x00000404 -#define BF_MOD_DISABLE_H_INFO 0x00000211, 0x00000408 -#define BF_LED_DISABLE_H_INFO 0x00000211, 0x0000040C - - -#define REG_ADC_OFF1_H_ADDR 0x00000213 -#define BF_CH1_ADC_ADJUST_H_INFO 0x00000213, 0x00000E00 - -#define REG_ADC_OFF2_H_ADDR 0x00000214 -#define BF_CH2_ADC_ADJUST_H_INFO 0x00000214, 0x00000E00 - -#define REG_DATA1_H_ADDR 0x00000215 -#define BF_SIGNAL_SIZE_H_INFO 0x00000215, 0x00000300 -#define BF_SIGNAL_SHIFT_H_INFO 0x00000215, 0x00000503 -#define BF_DARK_SIZE_H_INFO 0x00000215, 0x00000308 -#define BF_DARK_SHIFT_H_INFO 0x00000215, 0x0000050B - -#define REG_DATA2_H_ADDR 0x00000216 -#define BF_LIT_SIZE_H_INFO 0x00000216, 0x00000300 -#define BF_LIT_SHIFT_H_INFO 0x00000216, 0x00000503 - -#define REG_DECIMATE_H_ADDR 0x00000217 -#define BF_DECIMATE_FACTOR_H_INFO 0x00000217, 0x00000704 - -#define REG_DIGINT_LIT_H_ADDR 0x00000218 -#define BF_LIT_OFFSET_H_INFO 0x00000218, 0x00000900 - -#define REG_DIGINT_DARK_H_ADDR 0x00000219 -#define BF_DARK1_OFFSET_H_INFO 0x00000219, 0x00000700 -#define BF_DARK2_OFFSET_H_INFO 0x00000219, 0x00000907 - -#define REG_TS_PATH_A_ADDR 0x00000121 -#define BF_AFE_PATH_CFG_A_INFO 0x00000121, 0x00000700 -#define BF_AFE_INT_C_BUF_A_INFO 0x00000121, 0x00000108 -#define BF_GOUT_A_INFO 0x00000121, 0x00000109 -#define BF_AMBIENT_CANCELLATION_A_INFO 0x00000121, 0x0000020A -#define BF_PRE_WIDTH_A_INFO 0x00000121, 0x0000040C - -#define REG_BIOZ_AFECON_B_ADDR 0x000002C0 -#define BF_BIOZ_DACREFEN_B_INFO 0x000002C0, 0x00000100 -#define BF_BIOZ_TIAEN_B_INFO 0x000002C0, 0x00000101 -#define BF_BIOZ_TIMESLOT_OFFSET_B_INFO 0x000002C0, 0x00000A02 - -#define REG_BIOZ_WGFCW_LOW_B_ADDR 0x000002C1 -#define BF_BIOZ_SINEFCW_L_B_INFO 0x000002C1, 0x00001000 - -#define REG_BIOZ_WGFCW_HI_B_ADDR 0x000002C2 -#define BF_BIOZ_SINEFCW_H_B_INFO 0x000002C2, 0x00000400 - -#define REG_BIOZ_WGPHASE_LOW_B_ADDR 0x000002C3 -#define BF_BIOZ_SINE_PHASE_OFFSET_L_B_INFO 0x000002C3, 0x00001000 - -#define REG_BIOZ_WGPHASE_HI_B_ADDR 0x000002C4 -#define BF_BIOZ_SINE_PHASE_OFFSET_H_B_INFO 0x000002C4, 0x00000400 - -#define REG_BIOZ_WGOFFSET_B_ADDR 0x000002C5 -#define BF_BIOZ_SINE_OFFSET_B_INFO 0x000002C5, 0x00000C00 - -#define REG_BIOZ_WGAMPLITUDE_B_ADDR 0x000002C6 -#define BF_BIOZ_SINEAMPLITUDE_B_INFO 0x000002C6, 0x00000B00 - -#define REG_BIOZ_DACCON_B_ADDR 0x000002C7 -#define BF_BIOZ_RATE_DIV_B_INFO 0x000002C7, 0x00000800 -#define BF_BIOZ_BW50KEN_B_INFO 0x000002C7, 0x00000108 -#define BF_BIOZ_BW200KEN_B_INFO 0x000002C7, 0x00000109 -#define BF_BIOZ_DACBUFBW_B_INFO 0x000002C7, 0x0000030A -#define BF_BIOZ_EXBUFEN_B_INFO 0x000002C7, 0x0000010D - -#define REG_BIOZ_DACGAIN_B_ADDR 0x000002C8 -#define BF_BIOZ_DAC_GAIN_B_INFO 0x000002C8, 0x00000C00 -#define BF_BIOZ_DACGAIN_EN_B_INFO 0x000002C8, 0x0000010C - -#define REG_BIOZ_DACOFFSET_B_ADDR 0x000002C9 -#define BF_BIOZ_DAC_OFFSET_B_INFO 0x000002C9, 0x00000C00 -#define BF_BIOZ_DACOFFSET_EN_B_INFO 0x000002C9, 0x0000010C - -#define REG_BIOZ_ANA_CTRL1_B_ADDR 0x000002CA -#define BF_BIOZ_DAC_RCF_LOWBW_EN_B_INFO 0x000002CA, 0x00000100 -#define BF_BIOZ_PGA_PD_B_INFO 0x000002CA, 0x00000101 -#define BF_BIOZ_AMP_OFF_B_INFO 0x000002CA, 0x00000202 -#define BF_BIOZ_CHOP_OFF_IN_B_INFO 0x000002CA, 0x00000204 -#define BF_BIOZ_EXCBUF_LPMODE_B_INFO 0x000002CA, 0x00000106 -#define BF_BIOZ_TIA_LPMODE_B_INFO 0x000002CA, 0x00000107 -#define BF_BIOZ_RX_LPMODE_B_INFO 0x000002CA, 0x00000108 -#define BF_BIOZ_TIA_CGAIN_B_INFO 0x000002CA, 0x00000409 -#define BF_BIOZ_TIA_RGAIN_B_INFO 0x000002CA, 0x0000030D - -#define REG_BIOZ_ANA_CTRL2_B_ADDR 0x000002CB -#define BF_BIOZ_DRSW_B_INFO 0x000002CB, 0x00000100 -#define BF_BIOZ_TRSW_B_INFO 0x000002CB, 0x00000101 -#define BF_BIOZ_DSW_B_INFO 0x000002CB, 0x00000402 -#define BF_BIOZ_TSW_B_INFO 0x000002CB, 0x00000406 -#define BF_BIOZ_PCHAN_B_INFO 0x000002CB, 0x0000020A -#define BF_BIOZ_NCHAN_B_INFO 0x000002CB, 0x0000020C -#define BF_BIOZ_CM_SW_B_INFO 0x000002CB, 0x0000010E - -#define REG_BIOZ_ANA_CTRL3_B_ADDR 0x000002CC -#define BF_BIOZ_RINT_SW_B_INFO 0x000002CC, 0x00000200 - -#define REG_BIOZ_ADCFILTERCON_B_ADDR 0x000002CD -#define BF_BIOZ_SINC3OSR_B_INFO 0x000002CD, 0x00000200 -#define BF_BIOZ_SINC3BYP_B_INFO 0x000002CD, 0x00000102 -#define BF_BIOZ_AVRGEN_B_INFO 0x000002CD, 0x00000103 -#define BF_BIOZ_AVRGNUM_B_INFO 0x000002CD, 0x00000204 - -#define REG_BIOZ_DFTCON_B_ADDR 0x000002CE -#define BF_BIOZ_DFTNUM_B_INFO 0x000002CE, 0x00000400 -#define BF_BIOZ_HANNINGEN_B_INFO 0x000002CE, 0x00000104 -#define BF_BIOZ_WG_DFT_DIFF_PHASE_EN_B_INFO 0x000002CE, 0x00000105 - -#define REG_BIOZ_ADC_CONV_DLY_B_ADDR 0x000002CF -#define BF_BIOZ_ADC_CONV_DLY_B_INFO 0x000002CF, 0x00000300 - -#define REG_BIOZ_DFTPHASE_LOW_B_ADDR 0x000002D0 -#define BF_BIOZ_DFT_PHASE_OFFSET_L_B_INFO 0x000002D0, 0x00001000 - -#define REG_BIOZ_DFTPHASE_HI_B_ADDR 0x000002D1 -#define BF_BIOZ_DFT_PHASE_OFFSET_H_B_INFO 0x000002D1, 0x00000400 - -#define REG_BIOZ_AFECON_C_ADDR 0x000002E0 -#define BF_BIOZ_DACREFEN_C_INFO 0x000002E0, 0x00000100 -#define BF_BIOZ_TIAEN_C_INFO 0x000002E0, 0x00000101 -#define BF_BIOZ_TIMESLOT_OFFSET_C_INFO 0x000002E0, 0x00000A02 - -#define REG_BIOZ_WGFCW_LOW_C_ADDR 0x000002E1 -#define BF_BIOZ_SINEFCW_L_C_INFO 0x000002E1, 0x00001000 - -#define REG_BIOZ_WGFCW_HI_C_ADDR 0x000002E2 -#define BF_BIOZ_SINEFCW_H_C_INFO 0x000002E2, 0x00000400 - -#define REG_BIOZ_WGPHASE_LOW_C_ADDR 0x000002E3 -#define BF_BIOZ_SINE_PHASE_OFFSET_L_C_INFO 0x000002E3, 0x00001000 - -#define REG_BIOZ_WGPHASE_HI_C_ADDR 0x000002E4 -#define BF_BIOZ_SINE_PHASE_OFFSET_H_C_INFO 0x000002E4, 0x00000400 - -#define REG_BIOZ_WGOFFSET_C_ADDR 0x000002E5 -#define BF_BIOZ_SINE_OFFSET_C_INFO 0x000002E5, 0x00000C00 - -#define REG_BIOZ_WGAMPLITUDE_C_ADDR 0x000002E6 -#define BF_BIOZ_SINEAMPLITUDE_C_INFO 0x000002E6, 0x00000B00 - -#define REG_BIOZ_DACCON_C_ADDR 0x000002E7 -#define BF_BIOZ_RATE_DIV_C_INFO 0x000002E7, 0x00000800 -#define BF_BIOZ_BW50KEN_C_INFO 0x000002E7, 0x00000108 -#define BF_BIOZ_BW200KEN_C_INFO 0x000002E7, 0x00000109 -#define BF_BIOZ_DACBUFBW_C_INFO 0x000002E7, 0x0000030A -#define BF_BIOZ_EXBUFEN_C_INFO 0x000002E7, 0x0000010D - -#define REG_BIOZ_DACGAIN_C_ADDR 0x000002E8 -#define BF_BIOZ_DAC_GAIN_C_INFO 0x000002E8, 0x00000C00 -#define BF_BIOZ_DACGAIN_EN_C_INFO 0x000002E8, 0x0000010C - -#define REG_BIOZ_DACOFFSET_C_ADDR 0x000002E9 -#define BF_BIOZ_DAC_OFFSET_C_INFO 0x000002E9, 0x00000C00 -#define BF_BIOZ_DACOFFSET_EN_C_INFO 0x000002E9, 0x0000010C - -#define REG_BIOZ_ANA_CTRL1_C_ADDR 0x000002EA -#define BF_BIOZ_DAC_RCF_LOWBW_EN_C_INFO 0x000002EA, 0x00000100 -#define BF_BIOZ_PGA_PD_C_INFO 0x000002EA, 0x00000101 -#define BF_BIOZ_AMP_OFF_C_INFO 0x000002EA, 0x00000202 -#define BF_BIOZ_CHOP_OFF_IN_C_INFO 0x000002EA, 0x00000204 -#define BF_BIOZ_EXCBUF_LPMODE_C_INFO 0x000002EA, 0x00000106 -#define BF_BIOZ_TIA_LPMODE_C_INFO 0x000002EA, 0x00000107 -#define BF_BIOZ_RX_LPMODE_C_INFO 0x000002EA, 0x00000108 -#define BF_BIOZ_TIA_CGAIN_C_INFO 0x000002EA, 0x00000409 -#define BF_BIOZ_TIA_RGAIN_C_INFO 0x000002EA, 0x0000030D - -#define REG_BIOZ_ANA_CTRL2_C_ADDR 0x000002EB -#define BF_BIOZ_DRSW_C_INFO 0x000002EB, 0x00000100 -#define BF_BIOZ_TRSW_C_INFO 0x000002EB, 0x00000101 -#define BF_BIOZ_DSW_C_INFO 0x000002EB, 0x00000402 -#define BF_BIOZ_TSW_C_INFO 0x000002EB, 0x00000406 -#define BF_BIOZ_PCHAN_C_INFO 0x000002EB, 0x0000020A -#define BF_BIOZ_NCHAN_C_INFO 0x000002EB, 0x0000020C -#define BF_BIOZ_CM_SW_C_INFO 0x000002EB, 0x0000010E - -#define REG_BIOZ_ANA_CTRL3_C_ADDR 0x000002EC -#define BF_BIOZ_RINT_SW_C_INFO 0x000002EC, 0x00000200 - -#define REG_BIOZ_ADCFILTERCON_C_ADDR 0x000002ED -#define BF_BIOZ_SINC3OSR_C_INFO 0x000002ED, 0x00000200 -#define BF_BIOZ_SINC3BYP_C_INFO 0x000002ED, 0x00000102 -#define BF_BIOZ_AVRGEN_C_INFO 0x000002ED, 0x00000103 -#define BF_BIOZ_AVRGNUM_C_INFO 0x000002ED, 0x00000204 - -#define REG_BIOZ_DFTCON_C_ADDR 0x000002EE -#define BF_BIOZ_DFTNUM_C_INFO 0x000002EE, 0x00000400 -#define BF_BIOZ_HANNINGEN_C_INFO 0x000002EE, 0x00000104 -#define BF_BIOZ_WG_DFT_DIFF_PHASE_EN_C_INFO 0x000002EE, 0x00000105 - -#define REG_BIOZ_ADC_CONV_DLY_C_ADDR 0x000002EF -#define BF_BIOZ_ADC_CONV_DLY_C_INFO 0x000002EF, 0x00000300 - -#define REG_BIOZ_DFTPHASE_LOW_C_ADDR 0x000002F0 -#define BF_BIOZ_DFT_PHASE_OFFSET_L_C_INFO 0x000002F0, 0x00001000 - -#define REG_BIOZ_DFTPHASE_HI_C_ADDR 0x000002F1 -#define BF_BIOZ_DFT_PHASE_OFFSET_H_C_INFO 0x000002F1, 0x00000400 - -#define REG_BIOZ_AFECON_D_ADDR 0x00000300 -#define BF_BIOZ_DACREFEN_D_INFO 0x00000300, 0x00000100 -#define BF_BIOZ_TIAEN_D_INFO 0x00000300, 0x00000101 -#define BF_BIOZ_TIMESLOT_OFFSET_D_INFO 0x00000300, 0x00000A02 - -#define REG_BIOZ_WGFCW_LOW_D_ADDR 0x00000301 -#define BF_BIOZ_SINEFCW_L_D_INFO 0x00000301, 0x00001000 - -#define REG_BIOZ_WGFCW_HI_D_ADDR 0x00000302 -#define BF_BIOZ_SINEFCW_H_D_INFO 0x00000302, 0x00000400 - -#define REG_BIOZ_WGPHASE_LOW_D_ADDR 0x00000303 -#define BF_BIOZ_SINE_PHASE_OFFSET_L_D_INFO 0x00000303, 0x00001000 - -#define REG_BIOZ_WGPHASE_HI_D_ADDR 0x00000304 -#define BF_BIOZ_SINE_PHASE_OFFSET_H_D_INFO 0x00000304, 0x00000400 - -#define REG_BIOZ_WGOFFSET_D_ADDR 0x00000305 -#define BF_BIOZ_SINE_OFFSET_D_INFO 0x00000305, 0x00000C00 - -#define REG_BIOZ_WGAMPLITUDE_D_ADDR 0x00000306 -#define BF_BIOZ_SINEAMPLITUDE_D_INFO 0x00000306, 0x00000B00 - -#define REG_BIOZ_DACCON_D_ADDR 0x00000307 -#define BF_BIOZ_RATE_DIV_D_INFO 0x00000307, 0x00000800 -#define BF_BIOZ_BW50KEN_D_INFO 0x00000307, 0x00000108 -#define BF_BIOZ_BW200KEN_D_INFO 0x00000307, 0x00000109 -#define BF_BIOZ_DACBUFBW_D_INFO 0x00000307, 0x0000030A -#define BF_BIOZ_EXBUFEN_D_INFO 0x00000307, 0x0000010D - -#define REG_BIOZ_DACGAIN_D_ADDR 0x00000308 -#define BF_BIOZ_DAC_GAIN_D_INFO 0x00000308, 0x00000C00 -#define BF_BIOZ_DACGAIN_EN_D_INFO 0x00000308, 0x0000010C - -#define REG_BIOZ_DACOFFSET_D_ADDR 0x00000309 -#define BF_BIOZ_DAC_OFFSET_D_INFO 0x00000309, 0x00000C00 -#define BF_BIOZ_DACOFFSET_EN_D_INFO 0x00000309, 0x0000010C - -#define REG_BIOZ_ANA_CTRL1_D_ADDR 0x0000030A -#define BF_BIOZ_DAC_RCF_LOWBW_EN_D_INFO 0x0000030A, 0x00000100 -#define BF_BIOZ_PGA_PD_D_INFO 0x0000030A, 0x00000101 -#define BF_BIOZ_AMP_OFF_D_INFO 0x0000030A, 0x00000202 -#define BF_BIOZ_CHOP_OFF_IN_D_INFO 0x0000030A, 0x00000204 -#define BF_BIOZ_EXCBUF_LPMODE_D_INFO 0x0000030A, 0x00000106 -#define BF_BIOZ_TIA_LPMODE_D_INFO 0x0000030A, 0x00000107 -#define BF_BIOZ_RX_LPMODE_D_INFO 0x0000030A, 0x00000108 -#define BF_BIOZ_TIA_CGAIN_D_INFO 0x0000030A, 0x00000409 -#define BF_BIOZ_TIA_RGAIN_D_INFO 0x0000030A, 0x0000030D - -#define REG_BIOZ_ANA_CTRL2_D_ADDR 0x0000030B -#define BF_BIOZ_DRSW_D_INFO 0x0000030B, 0x00000100 -#define BF_BIOZ_TRSW_D_INFO 0x0000030B, 0x00000101 -#define BF_BIOZ_DSW_D_INFO 0x0000030B, 0x00000402 -#define BF_BIOZ_TSW_D_INFO 0x0000030B, 0x00000406 -#define BF_BIOZ_PCHAN_D_INFO 0x0000030B, 0x0000020A -#define BF_BIOZ_NCHAN_D_INFO 0x0000030B, 0x0000020C -#define BF_BIOZ_CM_SW_D_INFO 0x0000030B, 0x0000010E - -#define REG_BIOZ_ANA_CTRL3_D_ADDR 0x0000030C -#define BF_BIOZ_RINT_SW_D_INFO 0x0000030C, 0x00000200 - -#define REG_BIOZ_ADCFILTERCON_D_ADDR 0x0000030D -#define BF_BIOZ_SINC3OSR_D_INFO 0x0000030D, 0x00000200 -#define BF_BIOZ_SINC3BYP_D_INFO 0x0000030D, 0x00000102 -#define BF_BIOZ_AVRGEN_D_INFO 0x0000030D, 0x00000103 -#define BF_BIOZ_AVRGNUM_D_INFO 0x0000030D, 0x00000204 - -#define REG_BIOZ_DFTCON_D_ADDR 0x0000030E -#define BF_BIOZ_DFTNUM_D_INFO 0x0000030E, 0x00000400 -#define BF_BIOZ_HANNINGEN_D_INFO 0x0000030E, 0x00000104 -#define BF_BIOZ_WG_DFT_DIFF_PHASE_EN_D_INFO 0x0000030E, 0x00000105 - -#define REG_BIOZ_ADC_CONV_DLY_D_ADDR 0x0000030F -#define BF_BIOZ_ADC_CONV_DLY_D_INFO 0x0000030F, 0x00000300 - -#define REG_BIOZ_DFTPHASE_LOW_D_ADDR 0x00000310 -#define BF_BIOZ_DFT_PHASE_OFFSET_L_D_INFO 0x00000310, 0x00001000 - -#define REG_BIOZ_DFTPHASE_HI_D_ADDR 0x00000311 -#define BF_BIOZ_DFT_PHASE_OFFSET_H_D_INFO 0x00000311, 0x00000400 - -#define REG_INPUTS_A_ADDR 0x00000122 -#define BF_INP12_A_INFO 0x00000122, 0x00000400 -#define BF_INP34_A_INFO 0x00000122, 0x00000404 - -#define REG_COUNTS_B_ADDR 0x0000014A -#define BF_NUM_REPEAT_B_INFO 0x0000014A, 0x00000800 -#define BF_NUM_INT_B_INFO 0x0000014A, 0x00000808 - -#define REG_PERIOD_B_ADDR 0x0000014B -#define BF_MIN_PERIOD_B_INFO 0x0000014B, 0x00000A00 -#define BF_MOD_TYPE_B_INFO 0x0000014B, 0x0000020C -#define BF_COARSE_LOOP_WIDTH_B_INFO 0x0000014B, 0x0000010E - -#define REG_LED_PULSE1_B_ADDR 0x0000014C -#define BF_LED_OFFSET_B_INFO 0x0000014C, 0x00000800 -#define BF_LED_WIDTH_B_INFO 0x0000014C, 0x00000808 - -#define REG_LED_PULSE2_B_ADDR 0x0000014D -#define BF_LED_SECOND_OFFSET_B_INFO 0x0000014D, 0x00000800 - -#define REG_INTEG_WIDTH_B_ADDR 0x0000014E -#define BF_INTEG_WIDTH_B_INFO 0x0000014E, 0x00000500 -#define BF_ADC_COUNT_B_INFO 0x0000014E, 0x00000205 -#define BF_CH1_AMP_DISABLE_B_INFO 0x0000014E, 0x00000207 -#define BF_CH2_AMP_DISABLE_B_INFO 0x0000014E, 0x0000020B -#define BF_SINGLE_INTEG_B_INFO 0x0000014E, 0x0000010F - -#define REG_INTEG_OFFSET_B_ADDR 0x0000014F -#define BF_INTEG_FINE_OFFSET_B_INFO 0x0000014F, 0x00000500 -#define BF_INTEG_OFFSET_B_INFO 0x0000014F, 0x00000805 - -#define REG_CATHODE_A_ADDR 0x00000123 -#define BF_VC1_SEL_A_INFO 0x00000123, 0x00000200 -#define BF_VC1_ALT_A_INFO 0x00000123, 0x00000202 -#define BF_VC1_PULSE_A_INFO 0x00000123, 0x00000204 -#define BF_VC2_SEL_A_INFO 0x00000123, 0x00000206 -#define BF_VC2_ALT_A_INFO 0x00000123, 0x00000208 -#define BF_VC2_PULSE_A_INFO 0x00000123, 0x0000020A -#define BF_PRECON_A_INFO 0x00000123, 0x0000030C - -#define REG_AFE_TRIM1_A_ADDR 0x00000124 -#define BF_TIA_GAIN_CH1_A_INFO 0x00000124, 0x00000300 -#define BF_TIA_GAIN_CH2_A_INFO 0x00000124, 0x00000303 -#define BF_VREF_PULSE_VAL_A_INFO 0x00000124, 0x00000206 -#define BF_AFE_TRIM_VREF_A_INFO 0x00000124, 0x00000208 -#define BF_VREF_PULSE_A_INFO 0x00000124, 0x0000010A -#define BF_AFE_TRIM_INT1_A_INFO 0x00000124, 0x0000020B -#define BF_AFE_TRIM_INT2_A_INFO 0x00000124, 0x0000020D -#define BF_AFE_TIA_SAT_DETECT_EN_A_INFO 0x00000124, 0x0000010F - -#define REG_COUNTS_C_ADDR 0x0000016A -#define BF_NUM_REPEAT_C_INFO 0x0000016A, 0x00000800 -#define BF_NUM_INT_C_INFO 0x0000016A, 0x00000808 - -#define REG_PERIOD_C_ADDR 0x0000016B -#define BF_MIN_PERIOD_C_INFO 0x0000016B, 0x00000A00 -#define BF_MOD_TYPE_C_INFO 0x0000016B, 0x0000020C -#define BF_COARSE_LOOP_WIDTH_C_INFO 0x0000016B, 0x0000010E - -#define REG_LED_PULSE1_C_ADDR 0x0000016C -#define BF_LED_OFFSET_C_INFO 0x0000016C, 0x00000800 -#define BF_LED_WIDTH_C_INFO 0x0000016C, 0x00000808 - -#define REG_LED_PULSE2_C_ADDR 0x0000016D -#define BF_LED_SECOND_OFFSET_C_INFO 0x0000016D, 0x00000800 - -#define REG_INTEG_WIDTH_C_ADDR 0x0000016E -#define BF_INTEG_WIDTH_C_INFO 0x0000016E, 0x00000500 -#define BF_ADC_COUNT_C_INFO 0x0000016E, 0x00000205 -#define BF_CH1_AMP_DISABLE_C_INFO 0x0000016E, 0x00000207 -#define BF_CH2_AMP_DISABLE_C_INFO 0x0000016E, 0x0000020B -#define BF_SINGLE_INTEG_C_INFO 0x0000016E, 0x0000010F - -#define REG_INTEG_OFFSET_C_ADDR 0x0000016F -#define BF_INTEG_FINE_OFFSET_C_INFO 0x0000016F, 0x00000500 -#define BF_INTEG_OFFSET_C_INFO 0x0000016F, 0x00000805 - -#define REG_AFE_TRIM2_A_ADDR 0x00000125 -#define BF_AFE_TRIM_INT1_CAP_A_INFO 0x00000125, 0x0000010C -#define BF_AFE_TRIM_INT2_CAP_A_INFO 0x00000125, 0x0000010D - -#define REG_AFE_DAC1_A_ADDR 0x00000126 -#define BF_DAC_LED_DC_CH1_A_INFO 0x00000126, 0x00000700 -#define BF_DAC_AMBIENT_CH1_A_INFO 0x00000126, 0x00000907 - -#define REG_COUNTS_D_ADDR 0x0000018A -#define BF_NUM_REPEAT_D_INFO 0x0000018A, 0x00000800 -#define BF_NUM_INT_D_INFO 0x0000018A, 0x00000808 - -#define REG_PERIOD_D_ADDR 0x0000018B -#define BF_MIN_PERIOD_D_INFO 0x0000018B, 0x00000A00 -#define BF_MOD_TYPE_D_INFO 0x0000018B, 0x0000020C -#define BF_COARSE_LOOP_WIDTH_D_INFO 0x0000018B, 0x0000010E - -#define REG_LED_PULSE1_D_ADDR 0x0000018C -#define BF_LED_OFFSET_D_INFO 0x0000018C, 0x00000800 -#define BF_LED_WIDTH_D_INFO 0x0000018C, 0x00000808 - -#define REG_LED_PULSE2_D_ADDR 0x0000018D -#define BF_LED_SECOND_OFFSET_D_INFO 0x0000018D, 0x00000800 - -#define REG_INTEG_WIDTH_D_ADDR 0x0000018E -#define BF_INTEG_WIDTH_D_INFO 0x0000018E, 0x00000500 -#define BF_ADC_COUNT_D_INFO 0x0000018E, 0x00000205 -#define BF_CH1_AMP_DISABLE_D_INFO 0x0000018E, 0x00000207 -#define BF_CH2_AMP_DISABLE_D_INFO 0x0000018E, 0x0000020B -#define BF_SINGLE_INTEG_D_INFO 0x0000018E, 0x0000010F - -#define REG_INTEG_OFFSET_D_ADDR 0x0000018F -#define BF_INTEG_FINE_OFFSET_D_INFO 0x0000018F, 0x00000500 -#define BF_INTEG_OFFSET_D_INFO 0x0000018F, 0x00000805 - -#define REG_AFE_DAC2_A_ADDR 0x00000127 -#define BF_DAC_LED_DC_CH2_A_INFO 0x00000127, 0x00000700 -#define BF_DAC_AMBIENT_CH2_A_INFO 0x00000127, 0x00000907 - -#define REG_LED_POW12_A_ADDR 0x00000128 -#define BF_LED_CURRENT1_A_INFO 0x00000128, 0x00000700 -#define BF_LED_DRIVESIDE1_A_INFO 0x00000128, 0x00000107 -#define BF_LED_CURRENT2_A_INFO 0x00000128, 0x00000708 -#define BF_LED_DRIVESIDE2_A_INFO 0x00000128, 0x0000010F - -#define REG_COUNTS_E_ADDR 0x000001AA -#define BF_NUM_REPEAT_E_INFO 0x000001AA, 0x00000800 -#define BF_NUM_INT_E_INFO 0x000001AA, 0x00000808 - -#define REG_PERIOD_E_ADDR 0x000001AB -#define BF_MIN_PERIOD_E_INFO 0x000001AB, 0x00000A00 -#define BF_MOD_TYPE_E_INFO 0x000001AB, 0x0000020C -#define BF_COARSE_LOOP_WIDTH_E_INFO 0x000001AB, 0x0000010E - -#define REG_LED_PULSE1_E_ADDR 0x000001AC -#define BF_LED_OFFSET_E_INFO 0x000001AC, 0x00000800 -#define BF_LED_WIDTH_E_INFO 0x000001AC, 0x00000808 - -#define REG_LED_PULSE2_E_ADDR 0x000001AD -#define BF_LED_SECOND_OFFSET_E_INFO 0x000001AD, 0x00000800 - -#define REG_INTEG_WIDTH_E_ADDR 0x000001AE -#define BF_INTEG_WIDTH_E_INFO 0x000001AE, 0x00000500 -#define BF_ADC_COUNT_E_INFO 0x000001AE, 0x00000205 -#define BF_CH1_AMP_DISABLE_E_INFO 0x000001AE, 0x00000207 -#define BF_CH2_AMP_DISABLE_E_INFO 0x000001AE, 0x0000020B -#define BF_SINGLE_INTEG_E_INFO 0x000001AE, 0x0000010F - -#define REG_INTEG_OFFSET_E_ADDR 0x000001AF -#define BF_INTEG_FINE_OFFSET_E_INFO 0x000001AF, 0x00000500 -#define BF_INTEG_OFFSET_E_INFO 0x000001AF, 0x00000805 - -#define REG_LED_MODE_A_ADDR 0x00000129 -#define BF_LED_MODE1_A_INFO 0x00000129, 0x00000100 -#define BF_LED_MODE2_A_INFO 0x00000129, 0x00000101 - -#define REG_MOD_PULSE_A_ADDR 0x00000130 -#define BF_MOD_OFFSET_A_INFO 0x00000130, 0x00000800 -#define BF_MOD_WIDTH_A_INFO 0x00000130, 0x00000808 - -#define REG_COUNTS_I_ADDR 0x0000022A -#define BF_NUM_REPEAT_I_INFO 0x0000022A, 0x00000800 -#define BF_NUM_INT_I_INFO 0x0000022A, 0x00000808 - -#define REG_PERIOD_I_ADDR 0x0000022B -#define BF_MIN_PERIOD_I_INFO 0x0000022B, 0x00000A00 -#define BF_MOD_TYPE_I_INFO 0x0000022B, 0x0000020C -#define BF_COARSE_LOOP_WIDTH_I_INFO 0x0000022B, 0x0000010E - -#define REG_LED_PULSE1_I_ADDR 0x0000022C -#define BF_LED_OFFSET_I_INFO 0x0000022C, 0x00000800 -#define BF_LED_WIDTH_I_INFO 0x0000022C, 0x00000808 - -#define REG_LED_PULSE2_I_ADDR 0x0000022D -#define BF_LED_SECOND_OFFSET_I_INFO 0x0000022D, 0x00000800 - -#define REG_INTEG_WIDTH_I_ADDR 0x0000022E -#define BF_INTEG_WIDTH_I_INFO 0x0000022E, 0x00000500 -#define BF_ADC_COUNT_I_INFO 0x0000022E, 0x00000205 -#define BF_CH1_AMP_DISABLE_I_INFO 0x0000022E, 0x00000207 -#define BF_CH2_AMP_DISABLE_I_INFO 0x0000022E, 0x0000020B -#define BF_SINGLE_INTEG_I_INFO 0x0000022E, 0x0000010F - -#define REG_INTEG_OFFSET_I_ADDR 0x0000022F -#define BF_INTEG_FINE_OFFSET_I_INFO 0x0000022F, 0x00000500 -#define BF_INTEG_OFFSET_I_INFO 0x0000022F, 0x00000805 - -#define REG_PATTERN1_A_ADDR 0x00000131 -#define BF_REVERSE_INTEG_A_INFO 0x00000131, 0x00000400 -#define BF_SUBTRACT_A_INFO 0x00000131, 0x00000404 -#define BF_MOD_DISABLE_A_INFO 0x00000131, 0x00000408 -#define BF_LED_DISABLE_A_INFO 0x00000131, 0x0000040C - - -#define REG_COUNTS_J_ADDR 0x0000024A -#define BF_NUM_REPEAT_J_INFO 0x0000024A, 0x00000800 -#define BF_NUM_INT_J_INFO 0x0000024A, 0x00000808 - -#define REG_PERIOD_J_ADDR 0x0000024B -#define BF_MIN_PERIOD_J_INFO 0x0000024B, 0x00000A00 -#define BF_MOD_TYPE_J_INFO 0x0000024B, 0x0000020C -#define BF_COARSE_LOOP_WIDTH_J_INFO 0x0000024B, 0x0000010E - -#define REG_LED_PULSE1_J_ADDR 0x0000024C -#define BF_LED_OFFSET_J_INFO 0x0000024C, 0x00000800 -#define BF_LED_WIDTH_J_INFO 0x0000024C, 0x00000808 - -#define REG_LED_PULSE2_J_ADDR 0x0000024D -#define BF_LED_SECOND_OFFSET_J_INFO 0x0000024D, 0x00000800 - -#define REG_INTEG_WIDTH_J_ADDR 0x0000024E -#define BF_INTEG_WIDTH_J_INFO 0x0000024E, 0x00000500 -#define BF_ADC_COUNT_J_INFO 0x0000024E, 0x00000205 -#define BF_CH1_AMP_DISABLE_J_INFO 0x0000024E, 0x00000207 -#define BF_CH2_AMP_DISABLE_J_INFO 0x0000024E, 0x0000020B -#define BF_SINGLE_INTEG_J_INFO 0x0000024E, 0x0000010F - -#define REG_INTEG_OFFSET_J_ADDR 0x0000024F -#define BF_INTEG_FINE_OFFSET_J_INFO 0x0000024F, 0x00000500 -#define BF_INTEG_OFFSET_J_INFO 0x0000024F, 0x00000805 - -#define REG_ADC_OFF1_A_ADDR 0x00000133 -#define BF_CH1_ADC_ADJUST_A_INFO 0x00000133, 0x00000E00 - -#define REG_ADC_OFF2_A_ADDR 0x00000134 -#define BF_CH2_ADC_ADJUST_A_INFO 0x00000134, 0x00000E00 - -#define REG_COUNTS_K_ADDR 0x0000026A -#define BF_NUM_REPEAT_K_INFO 0x0000026A, 0x00000800 -#define BF_NUM_INT_K_INFO 0x0000026A, 0x00000808 - -#define REG_PERIOD_K_ADDR 0x0000026B -#define BF_MIN_PERIOD_K_INFO 0x0000026B, 0x00000A00 -#define BF_MOD_TYPE_K_INFO 0x0000026B, 0x0000020C -#define BF_COARSE_LOOP_WIDTH_K_INFO 0x0000026B, 0x0000010E - -#define REG_LED_PULSE1_K_ADDR 0x0000026C -#define BF_LED_OFFSET_K_INFO 0x0000026C, 0x00000800 -#define BF_LED_WIDTH_K_INFO 0x0000026C, 0x00000808 - -#define REG_LED_PULSE2_K_ADDR 0x0000026D -#define BF_LED_SECOND_OFFSET_K_INFO 0x0000026D, 0x00000800 - -#define REG_INTEG_WIDTH_K_ADDR 0x0000026E -#define BF_INTEG_WIDTH_K_INFO 0x0000026E, 0x00000500 -#define BF_ADC_COUNT_K_INFO 0x0000026E, 0x00000205 -#define BF_CH1_AMP_DISABLE_K_INFO 0x0000026E, 0x00000207 -#define BF_CH2_AMP_DISABLE_K_INFO 0x0000026E, 0x0000020B -#define BF_SINGLE_INTEG_K_INFO 0x0000026E, 0x0000010F - -#define REG_INTEG_OFFSET_K_ADDR 0x0000026F -#define BF_INTEG_FINE_OFFSET_K_INFO 0x0000026F, 0x00000500 -#define BF_INTEG_OFFSET_K_INFO 0x0000026F, 0x00000805 - -#define REG_DATA1_A_ADDR 0x00000135 -#define BF_SIGNAL_SIZE_A_INFO 0x00000135, 0x00000300 -#define BF_SIGNAL_SHIFT_A_INFO 0x00000135, 0x00000503 -#define BF_DARK_SIZE_A_INFO 0x00000135, 0x00000308 -#define BF_DARK_SHIFT_A_INFO 0x00000135, 0x0000050B - -#define REG_DATA2_A_ADDR 0x00000136 -#define BF_LIT_SIZE_A_INFO 0x00000136, 0x00000300 -#define BF_LIT_SHIFT_A_INFO 0x00000136, 0x00000503 - -#define REG_COUNTS_L_ADDR 0x0000028A -#define BF_NUM_REPEAT_L_INFO 0x0000028A, 0x00000800 -#define BF_NUM_INT_L_INFO 0x0000028A, 0x00000808 - -#define REG_PERIOD_L_ADDR 0x0000028B -#define BF_MIN_PERIOD_L_INFO 0x0000028B, 0x00000A00 -#define BF_MOD_TYPE_L_INFO 0x0000028B, 0x0000020C -#define BF_COARSE_LOOP_WIDTH_L_INFO 0x0000028B, 0x0000010E - -#define REG_LED_PULSE1_L_ADDR 0x0000028C -#define BF_LED_OFFSET_L_INFO 0x0000028C, 0x00000800 -#define BF_LED_WIDTH_L_INFO 0x0000028C, 0x00000808 - -#define REG_LED_PULSE2_L_ADDR 0x0000028D -#define BF_LED_SECOND_OFFSET_L_INFO 0x0000028D, 0x00000800 - -#define REG_INTEG_WIDTH_L_ADDR 0x0000028E -#define BF_INTEG_WIDTH_L_INFO 0x0000028E, 0x00000500 -#define BF_ADC_COUNT_L_INFO 0x0000028E, 0x00000205 -#define BF_CH1_AMP_DISABLE_L_INFO 0x0000028E, 0x00000207 -#define BF_CH2_AMP_DISABLE_L_INFO 0x0000028E, 0x0000020B -#define BF_SINGLE_INTEG_L_INFO 0x0000028E, 0x0000010F - -#define REG_INTEG_OFFSET_L_ADDR 0x0000028F -#define BF_INTEG_FINE_OFFSET_L_INFO 0x0000028F, 0x00000500 -#define BF_INTEG_OFFSET_L_INFO 0x0000028F, 0x00000805 - -#define REG_DECIMATE_A_ADDR 0x00000137 -#define BF_DECIMATE_FACTOR_A_INFO 0x00000137, 0x00000704 - -#define REG_DIGINT_LIT_A_ADDR 0x00000138 -#define BF_LIT_OFFSET_A_INFO 0x00000138, 0x00000900 - -#define REG_BIOZ_ANA_CTRL1_A_ADDR 0x000002AA -#define BF_BIOZ_DAC_RCF_LOWBW_EN_A_INFO 0x000002AA, 0x00000100 -#define BF_BIOZ_PGA_PD_A_INFO 0x000002AA, 0x00000101 -#define BF_BIOZ_AMP_OFF_A_INFO 0x000002AA, 0x00000202 -#define BF_BIOZ_CHOP_OFF_IN_A_INFO 0x000002AA, 0x00000204 -#define BF_BIOZ_EXCBUF_LPMODE_A_INFO 0x000002AA, 0x00000106 -#define BF_BIOZ_TIA_LPMODE_A_INFO 0x000002AA, 0x00000107 -#define BF_BIOZ_RX_LPMODE_A_INFO 0x000002AA, 0x00000108 -#define BF_BIOZ_TIA_CGAIN_A_INFO 0x000002AA, 0x00000409 -#define BF_BIOZ_TIA_RGAIN_A_INFO 0x000002AA, 0x0000030D - -#define REG_BIOZ_ANA_CTRL2_A_ADDR 0x000002AB -#define BF_BIOZ_DRSW_A_INFO 0x000002AB, 0x00000100 -#define BF_BIOZ_TRSW_A_INFO 0x000002AB, 0x00000101 -#define BF_BIOZ_DSW_A_INFO 0x000002AB, 0x00000402 -#define BF_BIOZ_TSW_A_INFO 0x000002AB, 0x00000406 -#define BF_BIOZ_PCHAN_A_INFO 0x000002AB, 0x0000020A -#define BF_BIOZ_NCHAN_A_INFO 0x000002AB, 0x0000020C -#define BF_BIOZ_CM_SW_A_INFO 0x000002AB, 0x0000010E - -#define REG_BIOZ_ANA_CTRL3_A_ADDR 0x000002AC -#define BF_BIOZ_RINT_SW_A_INFO 0x000002AC, 0x00000200 - -#define REG_BIOZ_ADCFILTERCON_A_ADDR 0x000002AD -#define BF_BIOZ_SINC3OSR_A_INFO 0x000002AD, 0x00000200 -#define BF_BIOZ_SINC3BYP_A_INFO 0x000002AD, 0x00000102 -#define BF_BIOZ_AVRGEN_A_INFO 0x000002AD, 0x00000103 -#define BF_BIOZ_AVRGNUM_A_INFO 0x000002AD, 0x00000204 - -#define REG_BIOZ_DFTCON_A_ADDR 0x000002AE -#define BF_BIOZ_DFTNUM_A_INFO 0x000002AE, 0x00000400 -#define BF_BIOZ_HANNINGEN_A_INFO 0x000002AE, 0x00000104 -#define BF_BIOZ_WG_DFT_DIFF_PHASE_EN_A_INFO 0x000002AE, 0x00000105 - -#define REG_BIOZ_ADC_CONV_DLY_A_ADDR 0x000002AF -#define BF_BIOZ_ADC_CONV_DLY_A_INFO 0x000002AF, 0x00000300 - -#define REG_DIGINT_DARK_A_ADDR 0x00000139 -#define BF_DARK1_OFFSET_A_INFO 0x00000139, 0x00000700 -#define BF_DARK2_OFFSET_A_INFO 0x00000139, 0x00000907 - -#define REG_TS_CTRL_B_ADDR 0x00000140 -#define BF_TIMESLOT_OFFSET_B_INFO 0x00000140, 0x00000A00 -#define BF_INPUT_R_SELECT_B_INFO 0x00000140, 0x0000010A -#define BF_SAMPLE_TYPE_B_INFO 0x00000140, 0x0000030B -#define BF_CH2_EN_B_INFO 0x00000140, 0x0000010E -#define BF_SUBSAMPLE_B_INFO 0x00000140, 0x0000010F - -#define REG_BIOZ_ANA_CTRL1_E_ADDR 0x0000032A -#define BF_BIOZ_DAC_RCF_LOWBW_EN_E_INFO 0x0000032A, 0x00000100 -#define BF_BIOZ_PGA_PD_E_INFO 0x0000032A, 0x00000101 -#define BF_BIOZ_AMP_OFF_E_INFO 0x0000032A, 0x00000202 -#define BF_BIOZ_CHOP_OFF_IN_E_INFO 0x0000032A, 0x00000204 -#define BF_BIOZ_EXCBUF_LPMODE_E_INFO 0x0000032A, 0x00000106 -#define BF_BIOZ_TIA_LPMODE_E_INFO 0x0000032A, 0x00000107 -#define BF_BIOZ_RX_LPMODE_E_INFO 0x0000032A, 0x00000108 -#define BF_BIOZ_TIA_CGAIN_E_INFO 0x0000032A, 0x00000409 -#define BF_BIOZ_TIA_RGAIN_E_INFO 0x0000032A, 0x0000030D - -#define REG_BIOZ_ANA_CTRL2_E_ADDR 0x0000032B -#define BF_BIOZ_DRSW_E_INFO 0x0000032B, 0x00000100 -#define BF_BIOZ_TRSW_E_INFO 0x0000032B, 0x00000101 -#define BF_BIOZ_DSW_E_INFO 0x0000032B, 0x00000402 -#define BF_BIOZ_TSW_E_INFO 0x0000032B, 0x00000406 -#define BF_BIOZ_PCHAN_E_INFO 0x0000032B, 0x0000020A -#define BF_BIOZ_NCHAN_E_INFO 0x0000032B, 0x0000020C -#define BF_BIOZ_CM_SW_E_INFO 0x0000032B, 0x0000010E - -#define REG_BIOZ_ANA_CTRL3_E_ADDR 0x0000032C -#define BF_BIOZ_RINT_SW_E_INFO 0x0000032C, 0x00000200 - -#define REG_BIOZ_ADCFILTERCON_E_ADDR 0x0000032D -#define BF_BIOZ_SINC3OSR_E_INFO 0x0000032D, 0x00000200 -#define BF_BIOZ_SINC3BYP_E_INFO 0x0000032D, 0x00000102 -#define BF_BIOZ_AVRGEN_E_INFO 0x0000032D, 0x00000103 -#define BF_BIOZ_AVRGNUM_E_INFO 0x0000032D, 0x00000204 - -#define REG_BIOZ_DFTCON_E_ADDR 0x0000032E -#define BF_BIOZ_DFTNUM_E_INFO 0x0000032E, 0x00000400 -#define BF_BIOZ_HANNINGEN_E_INFO 0x0000032E, 0x00000104 -#define BF_BIOZ_WG_DFT_DIFF_PHASE_EN_E_INFO 0x0000032E, 0x00000105 - -#define REG_BIOZ_ADC_CONV_DLY_E_ADDR 0x0000032F -#define BF_BIOZ_ADC_CONV_DLY_E_INFO 0x0000032F, 0x00000300 - -#define REG_TS_PATH_B_ADDR 0x00000141 -#define BF_AFE_PATH_CFG_B_INFO 0x00000141, 0x00000700 -#define BF_AFE_INT_C_BUF_B_INFO 0x00000141, 0x00000108 -#define BF_GOUT_B_INFO 0x00000141, 0x00000109 -#define BF_AMBIENT_CANCELLATION_B_INFO 0x00000141, 0x0000020A -#define BF_PRE_WIDTH_B_INFO 0x00000141, 0x0000040C - -#define REG_INPUTS_B_ADDR 0x00000142 -#define BF_INP12_B_INFO 0x00000142, 0x00000400 -#define BF_INP34_B_INFO 0x00000142, 0x00000404 - -#define REG_BIOZ_ANA_CTRL1_F_ADDR 0x0000034A -#define BF_BIOZ_DAC_RCF_LOWBW_EN_F_INFO 0x0000034A, 0x00000100 -#define BF_BIOZ_PGA_PD_F_INFO 0x0000034A, 0x00000101 -#define BF_BIOZ_AMP_OFF_F_INFO 0x0000034A, 0x00000202 -#define BF_BIOZ_CHOP_OFF_IN_F_INFO 0x0000034A, 0x00000204 -#define BF_BIOZ_EXCBUF_LPMODE_F_INFO 0x0000034A, 0x00000106 -#define BF_BIOZ_TIA_LPMODE_F_INFO 0x0000034A, 0x00000107 -#define BF_BIOZ_RX_LPMODE_F_INFO 0x0000034A, 0x00000108 -#define BF_BIOZ_TIA_CGAIN_F_INFO 0x0000034A, 0x00000409 -#define BF_BIOZ_TIA_RGAIN_F_INFO 0x0000034A, 0x0000030D - -#define REG_BIOZ_ANA_CTRL2_F_ADDR 0x0000034B -#define BF_BIOZ_DRSW_F_INFO 0x0000034B, 0x00000100 -#define BF_BIOZ_TRSW_F_INFO 0x0000034B, 0x00000101 -#define BF_BIOZ_DSW_F_INFO 0x0000034B, 0x00000402 -#define BF_BIOZ_TSW_F_INFO 0x0000034B, 0x00000406 -#define BF_BIOZ_PCHAN_F_INFO 0x0000034B, 0x0000020A -#define BF_BIOZ_NCHAN_F_INFO 0x0000034B, 0x0000020C -#define BF_BIOZ_CM_SW_F_INFO 0x0000034B, 0x0000010E - -#define REG_BIOZ_ANA_CTRL3_F_ADDR 0x0000034C -#define BF_BIOZ_RINT_SW_F_INFO 0x0000034C, 0x00000200 - -#define REG_BIOZ_ADCFILTERCON_F_ADDR 0x0000034D -#define BF_BIOZ_SINC3OSR_F_INFO 0x0000034D, 0x00000200 -#define BF_BIOZ_SINC3BYP_F_INFO 0x0000034D, 0x00000102 -#define BF_BIOZ_AVRGEN_F_INFO 0x0000034D, 0x00000103 -#define BF_BIOZ_AVRGNUM_F_INFO 0x0000034D, 0x00000204 - -#define REG_BIOZ_DFTCON_F_ADDR 0x0000034E -#define BF_BIOZ_DFTNUM_F_INFO 0x0000034E, 0x00000400 -#define BF_BIOZ_HANNINGEN_F_INFO 0x0000034E, 0x00000104 -#define BF_BIOZ_WG_DFT_DIFF_PHASE_EN_F_INFO 0x0000034E, 0x00000105 - -#define REG_BIOZ_ADC_CONV_DLY_F_ADDR 0x0000034F -#define BF_BIOZ_ADC_CONV_DLY_F_INFO 0x0000034F, 0x00000300 - -#define REG_CATHODE_B_ADDR 0x00000143 -#define BF_VC1_SEL_B_INFO 0x00000143, 0x00000200 -#define BF_VC1_ALT_B_INFO 0x00000143, 0x00000202 -#define BF_VC1_PULSE_B_INFO 0x00000143, 0x00000204 -#define BF_VC2_SEL_B_INFO 0x00000143, 0x00000206 -#define BF_VC2_ALT_B_INFO 0x00000143, 0x00000208 -#define BF_VC2_PULSE_B_INFO 0x00000143, 0x0000020A -#define BF_PRECON_B_INFO 0x00000143, 0x0000030C - -#define REG_AFE_TRIM1_B_ADDR 0x00000144 -#define BF_TIA_GAIN_CH1_B_INFO 0x00000144, 0x00000300 -#define BF_TIA_GAIN_CH2_B_INFO 0x00000144, 0x00000303 -#define BF_VREF_PULSE_VAL_B_INFO 0x00000144, 0x00000206 -#define BF_AFE_TRIM_VREF_B_INFO 0x00000144, 0x00000208 -#define BF_VREF_PULSE_B_INFO 0x00000144, 0x0000010A -#define BF_AFE_TRIM_INT1_B_INFO 0x00000144, 0x0000020B -#define BF_AFE_TRIM_INT2_B_INFO 0x00000144, 0x0000020D -#define BF_AFE_TIA_SAT_DETECT_EN_B_INFO 0x00000144, 0x0000010F - -#define REG_AFE_TRIM2_B_ADDR 0x00000145 -#define BF_AFE_TRIM_INT1_CAP_B_INFO 0x00000145, 0x0000010C -#define BF_AFE_TRIM_INT2_CAP_B_INFO 0x00000145, 0x0000010D - -#define REG_AFE_DAC1_B_ADDR 0x00000146 -#define BF_DAC_LED_DC_CH1_B_INFO 0x00000146, 0x00000700 -#define BF_DAC_AMBIENT_CH1_B_INFO 0x00000146, 0x00000907 - -#define REG_AFE_DAC2_B_ADDR 0x00000147 -#define BF_DAC_LED_DC_CH2_B_INFO 0x00000147, 0x00000700 -#define BF_DAC_AMBIENT_CH2_B_INFO 0x00000147, 0x00000907 - -#define REG_LED_POW12_B_ADDR 0x00000148 -#define BF_LED_CURRENT1_B_INFO 0x00000148, 0x00000700 -#define BF_LED_DRIVESIDE1_B_INFO 0x00000148, 0x00000107 -#define BF_LED_CURRENT2_B_INFO 0x00000148, 0x00000708 -#define BF_LED_DRIVESIDE2_B_INFO 0x00000148, 0x0000010F - -#define REG_LED_MODE_B_ADDR 0x00000149 -#define BF_LED_MODE1_B_INFO 0x00000149, 0x00000100 -#define BF_LED_MODE2_B_INFO 0x00000149, 0x00000101 - -#define REG_MOD_PULSE_B_ADDR 0x00000150 -#define BF_MOD_OFFSET_B_INFO 0x00000150, 0x00000800 -#define BF_MOD_WIDTH_B_INFO 0x00000150, 0x00000808 - -#define REG_PATTERN1_B_ADDR 0x00000151 -#define BF_REVERSE_INTEG_B_INFO 0x00000151, 0x00000400 -#define BF_SUBTRACT_B_INFO 0x00000151, 0x00000404 -#define BF_MOD_DISABLE_B_INFO 0x00000151, 0x00000408 -#define BF_LED_DISABLE_B_INFO 0x00000151, 0x0000040C - - -#define REG_ADC_OFF1_B_ADDR 0x00000153 -#define BF_CH1_ADC_ADJUST_B_INFO 0x00000153, 0x00000E00 - -#define REG_ADC_OFF2_B_ADDR 0x00000154 -#define BF_CH2_ADC_ADJUST_B_INFO 0x00000154, 0x00000E00 - -#define REG_DATA1_B_ADDR 0x00000155 -#define BF_SIGNAL_SIZE_B_INFO 0x00000155, 0x00000300 -#define BF_SIGNAL_SHIFT_B_INFO 0x00000155, 0x00000503 -#define BF_DARK_SIZE_B_INFO 0x00000155, 0x00000308 -#define BF_DARK_SHIFT_B_INFO 0x00000155, 0x0000050B - -#define REG_DATA2_B_ADDR 0x00000156 -#define BF_LIT_SIZE_B_INFO 0x00000156, 0x00000300 -#define BF_LIT_SHIFT_B_INFO 0x00000156, 0x00000503 - -#define REG_DECIMATE_B_ADDR 0x00000157 -#define BF_DECIMATE_FACTOR_B_INFO 0x00000157, 0x00000704 - -#define REG_DIGINT_LIT_B_ADDR 0x00000158 -#define BF_LIT_OFFSET_B_INFO 0x00000158, 0x00000900 - -#define REG_DIGINT_DARK_B_ADDR 0x00000159 -#define BF_DARK1_OFFSET_B_INFO 0x00000159, 0x00000700 -#define BF_DARK2_OFFSET_B_INFO 0x00000159, 0x00000907 - -#define REG_TS_CTRL_C_ADDR 0x00000160 -#define BF_TIMESLOT_OFFSET_C_INFO 0x00000160, 0x00000A00 -#define BF_INPUT_R_SELECT_C_INFO 0x00000160, 0x0000010A -#define BF_SAMPLE_TYPE_C_INFO 0x00000160, 0x0000030B -#define BF_CH2_EN_C_INFO 0x00000160, 0x0000010E -#define BF_SUBSAMPLE_C_INFO 0x00000160, 0x0000010F - -#define REG_TS_PATH_C_ADDR 0x00000161 -#define BF_AFE_PATH_CFG_C_INFO 0x00000161, 0x00000700 -#define BF_AFE_INT_C_BUF_C_INFO 0x00000161, 0x00000108 -#define BF_GOUT_C_INFO 0x00000161, 0x00000109 -#define BF_AMBIENT_CANCELLATION_C_INFO 0x00000161, 0x0000020A -#define BF_PRE_WIDTH_C_INFO 0x00000161, 0x0000040C - -#define REG_INPUTS_C_ADDR 0x00000162 -#define BF_INP12_C_INFO 0x00000162, 0x00000400 -#define BF_INP34_C_INFO 0x00000162, 0x00000404 - -#define REG_CATHODE_C_ADDR 0x00000163 -#define BF_VC1_SEL_C_INFO 0x00000163, 0x00000200 -#define BF_VC1_ALT_C_INFO 0x00000163, 0x00000202 -#define BF_VC1_PULSE_C_INFO 0x00000163, 0x00000204 -#define BF_VC2_SEL_C_INFO 0x00000163, 0x00000206 -#define BF_VC2_ALT_C_INFO 0x00000163, 0x00000208 -#define BF_VC2_PULSE_C_INFO 0x00000163, 0x0000020A -#define BF_PRECON_C_INFO 0x00000163, 0x0000030C - -#define REG_AFE_TRIM1_C_ADDR 0x00000164 -#define BF_TIA_GAIN_CH1_C_INFO 0x00000164, 0x00000300 -#define BF_TIA_GAIN_CH2_C_INFO 0x00000164, 0x00000303 -#define BF_VREF_PULSE_VAL_C_INFO 0x00000164, 0x00000206 -#define BF_AFE_TRIM_VREF_C_INFO 0x00000164, 0x00000208 -#define BF_VREF_PULSE_C_INFO 0x00000164, 0x0000010A -#define BF_AFE_TRIM_INT1_C_INFO 0x00000164, 0x0000020B -#define BF_AFE_TRIM_INT2_C_INFO 0x00000164, 0x0000020D -#define BF_AFE_TIA_SAT_DETECT_EN_C_INFO 0x00000164, 0x0000010F - -#define REG_AFE_TRIM2_C_ADDR 0x00000165 -#define BF_AFE_TRIM_INT1_CAP_C_INFO 0x00000165, 0x0000010C -#define BF_AFE_TRIM_INT2_CAP_C_INFO 0x00000165, 0x0000010D - -#define REG_AFE_DAC1_C_ADDR 0x00000166 -#define BF_DAC_LED_DC_CH1_C_INFO 0x00000166, 0x00000700 -#define BF_DAC_AMBIENT_CH1_C_INFO 0x00000166, 0x00000907 - -#define REG_AFE_DAC2_C_ADDR 0x00000167 -#define BF_DAC_LED_DC_CH2_C_INFO 0x00000167, 0x00000700 -#define BF_DAC_AMBIENT_CH2_C_INFO 0x00000167, 0x00000907 - -#define REG_LED_POW12_C_ADDR 0x00000168 -#define BF_LED_CURRENT1_C_INFO 0x00000168, 0x00000700 -#define BF_LED_DRIVESIDE1_C_INFO 0x00000168, 0x00000107 -#define BF_LED_CURRENT2_C_INFO 0x00000168, 0x00000708 -#define BF_LED_DRIVESIDE2_C_INFO 0x00000168, 0x0000010F - -#define REG_LED_MODE_C_ADDR 0x00000169 -#define BF_LED_MODE1_C_INFO 0x00000169, 0x00000100 -#define BF_LED_MODE2_C_INFO 0x00000169, 0x00000101 - -#define REG_MOD_PULSE_C_ADDR 0x00000170 -#define BF_MOD_OFFSET_C_INFO 0x00000170, 0x00000800 -#define BF_MOD_WIDTH_C_INFO 0x00000170, 0x00000808 - -#define REG_PATTERN1_C_ADDR 0x00000171 -#define BF_REVERSE_INTEG_C_INFO 0x00000171, 0x00000400 -#define BF_SUBTRACT_C_INFO 0x00000171, 0x00000404 -#define BF_MOD_DISABLE_C_INFO 0x00000171, 0x00000408 -#define BF_LED_DISABLE_C_INFO 0x00000171, 0x0000040C - - -#define REG_ADC_OFF1_C_ADDR 0x00000173 -#define BF_CH1_ADC_ADJUST_C_INFO 0x00000173, 0x00000E00 - -#define REG_ADC_OFF2_C_ADDR 0x00000174 -#define BF_CH2_ADC_ADJUST_C_INFO 0x00000174, 0x00000E00 - -#define REG_DATA1_C_ADDR 0x00000175 -#define BF_SIGNAL_SIZE_C_INFO 0x00000175, 0x00000300 -#define BF_SIGNAL_SHIFT_C_INFO 0x00000175, 0x00000503 -#define BF_DARK_SIZE_C_INFO 0x00000175, 0x00000308 -#define BF_DARK_SHIFT_C_INFO 0x00000175, 0x0000050B - -#define REG_DATA2_C_ADDR 0x00000176 -#define BF_LIT_SIZE_C_INFO 0x00000176, 0x00000300 -#define BF_LIT_SHIFT_C_INFO 0x00000176, 0x00000503 - -#define REG_DECIMATE_C_ADDR 0x00000177 -#define BF_DECIMATE_FACTOR_C_INFO 0x00000177, 0x00000704 - -#define REG_DIGINT_LIT_C_ADDR 0x00000178 -#define BF_LIT_OFFSET_C_INFO 0x00000178, 0x00000900 - -#define REG_DIGINT_DARK_C_ADDR 0x00000179 -#define BF_DARK1_OFFSET_C_INFO 0x00000179, 0x00000700 -#define BF_DARK2_OFFSET_C_INFO 0x00000179, 0x00000907 - -#define REG_TS_CTRL_D_ADDR 0x00000180 -#define BF_TIMESLOT_OFFSET_D_INFO 0x00000180, 0x00000A00 -#define BF_INPUT_R_SELECT_D_INFO 0x00000180, 0x0000010A -#define BF_SAMPLE_TYPE_D_INFO 0x00000180, 0x0000030B -#define BF_CH2_EN_D_INFO 0x00000180, 0x0000010E -#define BF_SUBSAMPLE_D_INFO 0x00000180, 0x0000010F - -#define REG_TS_PATH_D_ADDR 0x00000181 -#define BF_AFE_PATH_CFG_D_INFO 0x00000181, 0x00000700 -#define BF_AFE_INT_C_BUF_D_INFO 0x00000181, 0x00000108 -#define BF_GOUT_D_INFO 0x00000181, 0x00000109 -#define BF_AMBIENT_CANCELLATION_D_INFO 0x00000181, 0x0000020A -#define BF_PRE_WIDTH_D_INFO 0x00000181, 0x0000040C - -#define REG_INPUTS_D_ADDR 0x00000182 -#define BF_INP12_D_INFO 0x00000182, 0x00000400 -#define BF_INP34_D_INFO 0x00000182, 0x00000404 - -#define REG_CATHODE_D_ADDR 0x00000183 -#define BF_VC1_SEL_D_INFO 0x00000183, 0x00000200 -#define BF_VC1_ALT_D_INFO 0x00000183, 0x00000202 -#define BF_VC1_PULSE_D_INFO 0x00000183, 0x00000204 -#define BF_VC2_SEL_D_INFO 0x00000183, 0x00000206 -#define BF_VC2_ALT_D_INFO 0x00000183, 0x00000208 -#define BF_VC2_PULSE_D_INFO 0x00000183, 0x0000020A -#define BF_PRECON_D_INFO 0x00000183, 0x0000030C - -#define REG_AFE_TRIM1_D_ADDR 0x00000184 -#define BF_TIA_GAIN_CH1_D_INFO 0x00000184, 0x00000300 -#define BF_TIA_GAIN_CH2_D_INFO 0x00000184, 0x00000303 -#define BF_VREF_PULSE_VAL_D_INFO 0x00000184, 0x00000206 -#define BF_AFE_TRIM_VREF_D_INFO 0x00000184, 0x00000208 -#define BF_VREF_PULSE_D_INFO 0x00000184, 0x0000010A -#define BF_AFE_TRIM_INT1_D_INFO 0x00000184, 0x0000020B -#define BF_AFE_TRIM_INT2_D_INFO 0x00000184, 0x0000020D -#define BF_AFE_TIA_SAT_DETECT_EN_D_INFO 0x00000184, 0x0000010F - -#define REG_AFE_TRIM2_D_ADDR 0x00000185 -#define BF_AFE_TRIM_INT1_CAP_D_INFO 0x00000185, 0x0000010C -#define BF_AFE_TRIM_INT2_CAP_D_INFO 0x00000185, 0x0000010D - -#define REG_AFE_DAC1_D_ADDR 0x00000186 -#define BF_DAC_LED_DC_CH1_D_INFO 0x00000186, 0x00000700 -#define BF_DAC_AMBIENT_CH1_D_INFO 0x00000186, 0x00000907 - -#define REG_AFE_DAC2_D_ADDR 0x00000187 -#define BF_DAC_LED_DC_CH2_D_INFO 0x00000187, 0x00000700 -#define BF_DAC_AMBIENT_CH2_D_INFO 0x00000187, 0x00000907 - -#define REG_LED_POW12_D_ADDR 0x00000188 -#define BF_LED_CURRENT1_D_INFO 0x00000188, 0x00000700 -#define BF_LED_DRIVESIDE1_D_INFO 0x00000188, 0x00000107 -#define BF_LED_CURRENT2_D_INFO 0x00000188, 0x00000708 -#define BF_LED_DRIVESIDE2_D_INFO 0x00000188, 0x0000010F - -#define REG_LED_MODE_D_ADDR 0x00000189 -#define BF_LED_MODE1_D_INFO 0x00000189, 0x00000100 -#define BF_LED_MODE2_D_INFO 0x00000189, 0x00000101 - -#define REG_MOD_PULSE_D_ADDR 0x00000190 -#define BF_MOD_OFFSET_D_INFO 0x00000190, 0x00000800 -#define BF_MOD_WIDTH_D_INFO 0x00000190, 0x00000808 - -#define REG_PATTERN1_D_ADDR 0x00000191 -#define BF_REVERSE_INTEG_D_INFO 0x00000191, 0x00000400 -#define BF_SUBTRACT_D_INFO 0x00000191, 0x00000404 -#define BF_MOD_DISABLE_D_INFO 0x00000191, 0x00000408 -#define BF_LED_DISABLE_D_INFO 0x00000191, 0x0000040C - - -#define REG_ADC_OFF1_D_ADDR 0x00000193 -#define BF_CH1_ADC_ADJUST_D_INFO 0x00000193, 0x00000E00 - -#define REG_ADC_OFF2_D_ADDR 0x00000194 -#define BF_CH2_ADC_ADJUST_D_INFO 0x00000194, 0x00000E00 - -#define REG_DATA1_D_ADDR 0x00000195 -#define BF_SIGNAL_SIZE_D_INFO 0x00000195, 0x00000300 -#define BF_SIGNAL_SHIFT_D_INFO 0x00000195, 0x00000503 -#define BF_DARK_SIZE_D_INFO 0x00000195, 0x00000308 -#define BF_DARK_SHIFT_D_INFO 0x00000195, 0x0000050B - -#define REG_DATA2_D_ADDR 0x00000196 -#define BF_LIT_SIZE_D_INFO 0x00000196, 0x00000300 -#define BF_LIT_SHIFT_D_INFO 0x00000196, 0x00000503 - -#define REG_DECIMATE_D_ADDR 0x00000197 -#define BF_DECIMATE_FACTOR_D_INFO 0x00000197, 0x00000704 - -#define REG_DIGINT_LIT_D_ADDR 0x00000198 -#define BF_LIT_OFFSET_D_INFO 0x00000198, 0x00000900 - -#define REG_DIGINT_DARK_D_ADDR 0x00000199 -#define BF_DARK1_OFFSET_D_INFO 0x00000199, 0x00000700 -#define BF_DARK2_OFFSET_D_INFO 0x00000199, 0x00000907 - -#define REG_TS_CTRL_E_ADDR 0x000001A0 -#define BF_TIMESLOT_OFFSET_E_INFO 0x000001A0, 0x00000A00 -#define BF_INPUT_R_SELECT_E_INFO 0x000001A0, 0x0000010A -#define BF_SAMPLE_TYPE_E_INFO 0x000001A0, 0x0000030B -#define BF_CH2_EN_E_INFO 0x000001A0, 0x0000010E -#define BF_SUBSAMPLE_E_INFO 0x000001A0, 0x0000010F - -#define REG_TS_PATH_E_ADDR 0x000001A1 -#define BF_AFE_PATH_CFG_E_INFO 0x000001A1, 0x00000700 -#define BF_AFE_INT_C_BUF_E_INFO 0x000001A1, 0x00000108 -#define BF_GOUT_E_INFO 0x000001A1, 0x00000109 -#define BF_AMBIENT_CANCELLATION_E_INFO 0x000001A1, 0x0000020A -#define BF_PRE_WIDTH_E_INFO 0x000001A1, 0x0000040C - -#define REG_INPUTS_E_ADDR 0x000001A2 -#define BF_INP12_E_INFO 0x000001A2, 0x00000400 -#define BF_INP34_E_INFO 0x000001A2, 0x00000404 - -#define REG_CATHODE_E_ADDR 0x000001A3 -#define BF_VC1_SEL_E_INFO 0x000001A3, 0x00000200 -#define BF_VC1_ALT_E_INFO 0x000001A3, 0x00000202 -#define BF_VC1_PULSE_E_INFO 0x000001A3, 0x00000204 -#define BF_VC2_SEL_E_INFO 0x000001A3, 0x00000206 -#define BF_VC2_ALT_E_INFO 0x000001A3, 0x00000208 -#define BF_VC2_PULSE_E_INFO 0x000001A3, 0x0000020A -#define BF_PRECON_E_INFO 0x000001A3, 0x0000030C - -#define REG_AFE_TRIM1_E_ADDR 0x000001A4 -#define BF_TIA_GAIN_CH1_E_INFO 0x000001A4, 0x00000300 -#define BF_TIA_GAIN_CH2_E_INFO 0x000001A4, 0x00000303 -#define BF_VREF_PULSE_VAL_E_INFO 0x000001A4, 0x00000206 -#define BF_AFE_TRIM_VREF_E_INFO 0x000001A4, 0x00000208 -#define BF_VREF_PULSE_E_INFO 0x000001A4, 0x0000010A -#define BF_AFE_TRIM_INT1_E_INFO 0x000001A4, 0x0000020B -#define BF_AFE_TRIM_INT2_E_INFO 0x000001A4, 0x0000020D -#define BF_AFE_TIA_SAT_DETECT_EN_E_INFO 0x000001A4, 0x0000010F - -#define REG_AFE_TRIM2_E_ADDR 0x000001A5 -#define BF_AFE_TRIM_INT1_CAP_E_INFO 0x000001A5, 0x0000010C -#define BF_AFE_TRIM_INT2_CAP_E_INFO 0x000001A5, 0x0000010D - -#define REG_AFE_DAC1_E_ADDR 0x000001A6 -#define BF_DAC_LED_DC_CH1_E_INFO 0x000001A6, 0x00000700 -#define BF_DAC_AMBIENT_CH1_E_INFO 0x000001A6, 0x00000907 - -#define REG_AFE_DAC2_E_ADDR 0x000001A7 -#define BF_DAC_LED_DC_CH2_E_INFO 0x000001A7, 0x00000700 -#define BF_DAC_AMBIENT_CH2_E_INFO 0x000001A7, 0x00000907 - -#define REG_LED_POW12_E_ADDR 0x000001A8 -#define BF_LED_CURRENT1_E_INFO 0x000001A8, 0x00000700 -#define BF_LED_DRIVESIDE1_E_INFO 0x000001A8, 0x00000107 -#define BF_LED_CURRENT2_E_INFO 0x000001A8, 0x00000708 -#define BF_LED_DRIVESIDE2_E_INFO 0x000001A8, 0x0000010F - -#define REG_LED_MODE_E_ADDR 0x000001A9 -#define BF_LED_MODE1_E_INFO 0x000001A9, 0x00000100 -#define BF_LED_MODE2_E_INFO 0x000001A9, 0x00000101 - -#define REG_MOD_PULSE_E_ADDR 0x000001B0 -#define BF_MOD_OFFSET_E_INFO 0x000001B0, 0x00000800 -#define BF_MOD_WIDTH_E_INFO 0x000001B0, 0x00000808 - -#define REG_PATTERN1_E_ADDR 0x000001B1 -#define BF_REVERSE_INTEG_E_INFO 0x000001B1, 0x00000400 -#define BF_SUBTRACT_E_INFO 0x000001B1, 0x00000404 -#define BF_MOD_DISABLE_E_INFO 0x000001B1, 0x00000408 -#define BF_LED_DISABLE_E_INFO 0x000001B1, 0x0000040C - - -#define REG_ADC_OFF1_E_ADDR 0x000001B3 -#define BF_CH1_ADC_ADJUST_E_INFO 0x000001B3, 0x00000E00 - -#define REG_ADC_OFF2_E_ADDR 0x000001B4 -#define BF_CH2_ADC_ADJUST_E_INFO 0x000001B4, 0x00000E00 - -#define REG_DATA1_E_ADDR 0x000001B5 -#define BF_SIGNAL_SIZE_E_INFO 0x000001B5, 0x00000300 -#define BF_SIGNAL_SHIFT_E_INFO 0x000001B5, 0x00000503 -#define BF_DARK_SIZE_E_INFO 0x000001B5, 0x00000308 -#define BF_DARK_SHIFT_E_INFO 0x000001B5, 0x0000050B - -#define REG_DATA2_E_ADDR 0x000001B6 -#define BF_LIT_SIZE_E_INFO 0x000001B6, 0x00000300 -#define BF_LIT_SHIFT_E_INFO 0x000001B6, 0x00000503 - -#define REG_DECIMATE_E_ADDR 0x000001B7 -#define BF_DECIMATE_FACTOR_E_INFO 0x000001B7, 0x00000704 - -#define REG_DIGINT_LIT_E_ADDR 0x000001B8 -#define BF_LIT_OFFSET_E_INFO 0x000001B8, 0x00000900 - -#define REG_DIGINT_DARK_E_ADDR 0x000001B9 -#define BF_DARK1_OFFSET_E_INFO 0x000001B9, 0x00000700 -#define BF_DARK2_OFFSET_E_INFO 0x000001B9, 0x00000907 - -#define REG_TS_CTRL_I_ADDR 0x00000220 -#define BF_TIMESLOT_OFFSET_I_INFO 0x00000220, 0x00000A00 -#define BF_INPUT_R_SELECT_I_INFO 0x00000220, 0x0000010A -#define BF_SAMPLE_TYPE_I_INFO 0x00000220, 0x0000030B -#define BF_CH2_EN_I_INFO 0x00000220, 0x0000010E -#define BF_SUBSAMPLE_I_INFO 0x00000220, 0x0000010F - -#define REG_TS_PATH_I_ADDR 0x00000221 -#define BF_AFE_PATH_CFG_I_INFO 0x00000221, 0x00000700 -#define BF_AFE_INT_C_BUF_I_INFO 0x00000221, 0x00000108 -#define BF_GOUT_I_INFO 0x00000221, 0x00000109 -#define BF_AMBIENT_CANCELLATION_I_INFO 0x00000221, 0x0000020A -#define BF_PRE_WIDTH_I_INFO 0x00000221, 0x0000040C - -#define REG_INPUTS_I_ADDR 0x00000222 -#define BF_INP12_I_INFO 0x00000222, 0x00000400 -#define BF_INP34_I_INFO 0x00000222, 0x00000404 - -#define REG_CATHODE_I_ADDR 0x00000223 -#define BF_VC1_SEL_I_INFO 0x00000223, 0x00000200 -#define BF_VC1_ALT_I_INFO 0x00000223, 0x00000202 -#define BF_VC1_PULSE_I_INFO 0x00000223, 0x00000204 -#define BF_VC2_SEL_I_INFO 0x00000223, 0x00000206 -#define BF_VC2_ALT_I_INFO 0x00000223, 0x00000208 -#define BF_VC2_PULSE_I_INFO 0x00000223, 0x0000020A -#define BF_PRECON_I_INFO 0x00000223, 0x0000030C - -#define REG_AFE_TRIM1_I_ADDR 0x00000224 -#define BF_TIA_GAIN_CH1_I_INFO 0x00000224, 0x00000300 -#define BF_TIA_GAIN_CH2_I_INFO 0x00000224, 0x00000303 -#define BF_VREF_PULSE_VAL_I_INFO 0x00000224, 0x00000206 -#define BF_AFE_TRIM_VREF_I_INFO 0x00000224, 0x00000208 -#define BF_VREF_PULSE_I_INFO 0x00000224, 0x0000010A -#define BF_AFE_TRIM_INT1_I_INFO 0x00000224, 0x0000020B -#define BF_AFE_TRIM_INT2_I_INFO 0x00000224, 0x0000020D -#define BF_AFE_TIA_SAT_DETECT_EN_I_INFO 0x00000224, 0x0000010F - -#define REG_AFE_TRIM2_I_ADDR 0x00000225 -#define BF_AFE_TRIM_INT1_CAP_I_INFO 0x00000225, 0x0000010C -#define BF_AFE_TRIM_INT2_CAP_I_INFO 0x00000225, 0x0000010D - -#define REG_AFE_DAC1_I_ADDR 0x00000226 -#define BF_DAC_LED_DC_CH1_I_INFO 0x00000226, 0x00000700 -#define BF_DAC_AMBIENT_CH1_I_INFO 0x00000226, 0x00000907 - -#define REG_AFE_DAC2_I_ADDR 0x00000227 -#define BF_DAC_LED_DC_CH2_I_INFO 0x00000227, 0x00000700 -#define BF_DAC_AMBIENT_CH2_I_INFO 0x00000227, 0x00000907 - -#define REG_LED_POW12_I_ADDR 0x00000228 -#define BF_LED_CURRENT1_I_INFO 0x00000228, 0x00000700 -#define BF_LED_DRIVESIDE1_I_INFO 0x00000228, 0x00000107 -#define BF_LED_CURRENT2_I_INFO 0x00000228, 0x00000708 -#define BF_LED_DRIVESIDE2_I_INFO 0x00000228, 0x0000010F - -#define REG_LED_MODE_I_ADDR 0x00000229 -#define BF_LED_MODE1_I_INFO 0x00000229, 0x00000100 -#define BF_LED_MODE2_I_INFO 0x00000229, 0x00000101 - -#define REG_MOD_PULSE_I_ADDR 0x00000230 -#define BF_MOD_OFFSET_I_INFO 0x00000230, 0x00000800 -#define BF_MOD_WIDTH_I_INFO 0x00000230, 0x00000808 - -#define REG_PATTERN1_I_ADDR 0x00000231 -#define BF_REVERSE_INTEG_I_INFO 0x00000231, 0x00000400 -#define BF_SUBTRACT_I_INFO 0x00000231, 0x00000404 -#define BF_MOD_DISABLE_I_INFO 0x00000231, 0x00000408 -#define BF_LED_DISABLE_I_INFO 0x00000231, 0x0000040C - - -#define REG_ADC_OFF1_I_ADDR 0x00000233 -#define BF_CH1_ADC_ADJUST_I_INFO 0x00000233, 0x00000E00 - -#define REG_ADC_OFF2_I_ADDR 0x00000234 -#define BF_CH2_ADC_ADJUST_I_INFO 0x00000234, 0x00000E00 - -#define REG_DATA1_I_ADDR 0x00000235 -#define BF_SIGNAL_SIZE_I_INFO 0x00000235, 0x00000300 -#define BF_SIGNAL_SHIFT_I_INFO 0x00000235, 0x00000503 -#define BF_DARK_SIZE_I_INFO 0x00000235, 0x00000308 -#define BF_DARK_SHIFT_I_INFO 0x00000235, 0x0000050B - -#define REG_DATA2_I_ADDR 0x00000236 -#define BF_LIT_SIZE_I_INFO 0x00000236, 0x00000300 -#define BF_LIT_SHIFT_I_INFO 0x00000236, 0x00000503 - -#define REG_DECIMATE_I_ADDR 0x00000237 -#define BF_DECIMATE_FACTOR_I_INFO 0x00000237, 0x00000704 - -#define REG_DIGINT_LIT_I_ADDR 0x00000238 -#define BF_LIT_OFFSET_I_INFO 0x00000238, 0x00000900 - -#define REG_DIGINT_DARK_I_ADDR 0x00000239 -#define BF_DARK1_OFFSET_I_INFO 0x00000239, 0x00000700 -#define BF_DARK2_OFFSET_I_INFO 0x00000239, 0x00000907 - -#define REG_TS_CTRL_J_ADDR 0x00000240 -#define BF_TIMESLOT_OFFSET_J_INFO 0x00000240, 0x00000A00 -#define BF_INPUT_R_SELECT_J_INFO 0x00000240, 0x0000010A -#define BF_SAMPLE_TYPE_J_INFO 0x00000240, 0x0000030B -#define BF_CH2_EN_J_INFO 0x00000240, 0x0000010E -#define BF_SUBSAMPLE_J_INFO 0x00000240, 0x0000010F - -#define REG_TS_PATH_J_ADDR 0x00000241 -#define BF_AFE_PATH_CFG_J_INFO 0x00000241, 0x00000700 -#define BF_AFE_INT_C_BUF_J_INFO 0x00000241, 0x00000108 -#define BF_GOUT_J_INFO 0x00000241, 0x00000109 -#define BF_AMBIENT_CANCELLATION_J_INFO 0x00000241, 0x0000020A -#define BF_PRE_WIDTH_J_INFO 0x00000241, 0x0000040C - -#define REG_INPUTS_J_ADDR 0x00000242 -#define BF_INP12_J_INFO 0x00000242, 0x00000400 -#define BF_INP34_J_INFO 0x00000242, 0x00000404 - -#define REG_CATHODE_J_ADDR 0x00000243 -#define BF_VC1_SEL_J_INFO 0x00000243, 0x00000200 -#define BF_VC1_ALT_J_INFO 0x00000243, 0x00000202 -#define BF_VC1_PULSE_J_INFO 0x00000243, 0x00000204 -#define BF_VC2_SEL_J_INFO 0x00000243, 0x00000206 -#define BF_VC2_ALT_J_INFO 0x00000243, 0x00000208 -#define BF_VC2_PULSE_J_INFO 0x00000243, 0x0000020A -#define BF_PRECON_J_INFO 0x00000243, 0x0000030C - -#define REG_AFE_TRIM1_J_ADDR 0x00000244 -#define BF_TIA_GAIN_CH1_J_INFO 0x00000244, 0x00000300 -#define BF_TIA_GAIN_CH2_J_INFO 0x00000244, 0x00000303 -#define BF_VREF_PULSE_VAL_J_INFO 0x00000244, 0x00000206 -#define BF_AFE_TRIM_VREF_J_INFO 0x00000244, 0x00000208 -#define BF_VREF_PULSE_J_INFO 0x00000244, 0x0000010A -#define BF_AFE_TRIM_INT1_J_INFO 0x00000244, 0x0000020B -#define BF_AFE_TRIM_INT2_J_INFO 0x00000244, 0x0000020D -#define BF_AFE_TIA_SAT_DETECT_EN_J_INFO 0x00000244, 0x0000010F - -#define REG_AFE_TRIM2_J_ADDR 0x00000245 -#define BF_AFE_TRIM_INT1_CAP_J_INFO 0x00000245, 0x0000010C -#define BF_AFE_TRIM_INT2_CAP_J_INFO 0x00000245, 0x0000010D - -#define REG_AFE_DAC1_J_ADDR 0x00000246 -#define BF_DAC_LED_DC_CH1_J_INFO 0x00000246, 0x00000700 -#define BF_DAC_AMBIENT_CH1_J_INFO 0x00000246, 0x00000907 - -#define REG_AFE_DAC2_J_ADDR 0x00000247 -#define BF_DAC_LED_DC_CH2_J_INFO 0x00000247, 0x00000700 -#define BF_DAC_AMBIENT_CH2_J_INFO 0x00000247, 0x00000907 - -#define REG_LED_POW12_J_ADDR 0x00000248 -#define BF_LED_CURRENT1_J_INFO 0x00000248, 0x00000700 -#define BF_LED_DRIVESIDE1_J_INFO 0x00000248, 0x00000107 -#define BF_LED_CURRENT2_J_INFO 0x00000248, 0x00000708 -#define BF_LED_DRIVESIDE2_J_INFO 0x00000248, 0x0000010F - -#define REG_LED_MODE_J_ADDR 0x00000249 -#define BF_LED_MODE1_J_INFO 0x00000249, 0x00000100 -#define BF_LED_MODE2_J_INFO 0x00000249, 0x00000101 - -#define REG_MOD_PULSE_J_ADDR 0x00000250 -#define BF_MOD_OFFSET_J_INFO 0x00000250, 0x00000800 -#define BF_MOD_WIDTH_J_INFO 0x00000250, 0x00000808 - -#define REG_PATTERN1_J_ADDR 0x00000251 -#define BF_REVERSE_INTEG_J_INFO 0x00000251, 0x00000400 -#define BF_SUBTRACT_J_INFO 0x00000251, 0x00000404 -#define BF_MOD_DISABLE_J_INFO 0x00000251, 0x00000408 -#define BF_LED_DISABLE_J_INFO 0x00000251, 0x0000040C - - -#define REG_ADC_OFF1_J_ADDR 0x00000253 -#define BF_CH1_ADC_ADJUST_J_INFO 0x00000253, 0x00000E00 - -#define REG_ADC_OFF2_J_ADDR 0x00000254 -#define BF_CH2_ADC_ADJUST_J_INFO 0x00000254, 0x00000E00 - -#define REG_DATA1_J_ADDR 0x00000255 -#define BF_SIGNAL_SIZE_J_INFO 0x00000255, 0x00000300 -#define BF_SIGNAL_SHIFT_J_INFO 0x00000255, 0x00000503 -#define BF_DARK_SIZE_J_INFO 0x00000255, 0x00000308 -#define BF_DARK_SHIFT_J_INFO 0x00000255, 0x0000050B - -#define REG_DATA2_J_ADDR 0x00000256 -#define BF_LIT_SIZE_J_INFO 0x00000256, 0x00000300 -#define BF_LIT_SHIFT_J_INFO 0x00000256, 0x00000503 - -#define REG_DECIMATE_J_ADDR 0x00000257 -#define BF_DECIMATE_FACTOR_J_INFO 0x00000257, 0x00000704 - -#define REG_DIGINT_LIT_J_ADDR 0x00000258 -#define BF_LIT_OFFSET_J_INFO 0x00000258, 0x00000900 - -#define REG_DIGINT_DARK_J_ADDR 0x00000259 -#define BF_DARK1_OFFSET_J_INFO 0x00000259, 0x00000700 -#define BF_DARK2_OFFSET_J_INFO 0x00000259, 0x00000907 - -#define REG_TS_CTRL_K_ADDR 0x00000260 -#define BF_TIMESLOT_OFFSET_K_INFO 0x00000260, 0x00000A00 -#define BF_INPUT_R_SELECT_K_INFO 0x00000260, 0x0000010A -#define BF_SAMPLE_TYPE_K_INFO 0x00000260, 0x0000030B -#define BF_CH2_EN_K_INFO 0x00000260, 0x0000010E -#define BF_SUBSAMPLE_K_INFO 0x00000260, 0x0000010F - -#define REG_TS_PATH_K_ADDR 0x00000261 -#define BF_AFE_PATH_CFG_K_INFO 0x00000261, 0x00000700 -#define BF_AFE_INT_C_BUF_K_INFO 0x00000261, 0x00000108 -#define BF_GOUT_K_INFO 0x00000261, 0x00000109 -#define BF_AMBIENT_CANCELLATION_K_INFO 0x00000261, 0x0000020A -#define BF_PRE_WIDTH_K_INFO 0x00000261, 0x0000040C - -#define REG_INPUTS_K_ADDR 0x00000262 -#define BF_INP12_K_INFO 0x00000262, 0x00000400 -#define BF_INP34_K_INFO 0x00000262, 0x00000404 - -#define REG_CATHODE_K_ADDR 0x00000263 -#define BF_VC1_SEL_K_INFO 0x00000263, 0x00000200 -#define BF_VC1_ALT_K_INFO 0x00000263, 0x00000202 -#define BF_VC1_PULSE_K_INFO 0x00000263, 0x00000204 -#define BF_VC2_SEL_K_INFO 0x00000263, 0x00000206 -#define BF_VC2_ALT_K_INFO 0x00000263, 0x00000208 -#define BF_VC2_PULSE_K_INFO 0x00000263, 0x0000020A -#define BF_PRECON_K_INFO 0x00000263, 0x0000030C - -#define REG_AFE_TRIM1_K_ADDR 0x00000264 -#define BF_TIA_GAIN_CH1_K_INFO 0x00000264, 0x00000300 -#define BF_TIA_GAIN_CH2_K_INFO 0x00000264, 0x00000303 -#define BF_VREF_PULSE_VAL_K_INFO 0x00000264, 0x00000206 -#define BF_AFE_TRIM_VREF_K_INFO 0x00000264, 0x00000208 -#define BF_VREF_PULSE_K_INFO 0x00000264, 0x0000010A -#define BF_AFE_TRIM_INT1_K_INFO 0x00000264, 0x0000020B -#define BF_AFE_TRIM_INT2_K_INFO 0x00000264, 0x0000020D -#define BF_AFE_TIA_SAT_DETECT_EN_K_INFO 0x00000264, 0x0000010F - -#define REG_AFE_TRIM2_K_ADDR 0x00000265 -#define BF_AFE_TRIM_INT1_CAP_K_INFO 0x00000265, 0x0000010C -#define BF_AFE_TRIM_INT2_CAP_K_INFO 0x00000265, 0x0000010D - -#define REG_AFE_DAC1_K_ADDR 0x00000266 -#define BF_DAC_LED_DC_CH1_K_INFO 0x00000266, 0x00000700 -#define BF_DAC_AMBIENT_CH1_K_INFO 0x00000266, 0x00000907 - -#define REG_AFE_DAC2_K_ADDR 0x00000267 -#define BF_DAC_LED_DC_CH2_K_INFO 0x00000267, 0x00000700 -#define BF_DAC_AMBIENT_CH2_K_INFO 0x00000267, 0x00000907 - -#define REG_LED_POW12_K_ADDR 0x00000268 -#define BF_LED_CURRENT1_K_INFO 0x00000268, 0x00000700 -#define BF_LED_DRIVESIDE1_K_INFO 0x00000268, 0x00000107 -#define BF_LED_CURRENT2_K_INFO 0x00000268, 0x00000708 -#define BF_LED_DRIVESIDE2_K_INFO 0x00000268, 0x0000010F - -#define REG_LED_MODE_K_ADDR 0x00000269 -#define BF_LED_MODE1_K_INFO 0x00000269, 0x00000100 -#define BF_LED_MODE2_K_INFO 0x00000269, 0x00000101 - -#define REG_MOD_PULSE_K_ADDR 0x00000270 -#define BF_MOD_OFFSET_K_INFO 0x00000270, 0x00000800 -#define BF_MOD_WIDTH_K_INFO 0x00000270, 0x00000808 - -#define REG_PATTERN1_K_ADDR 0x00000271 -#define BF_REVERSE_INTEG_K_INFO 0x00000271, 0x00000400 -#define BF_SUBTRACT_K_INFO 0x00000271, 0x00000404 -#define BF_MOD_DISABLE_K_INFO 0x00000271, 0x00000408 -#define BF_LED_DISABLE_K_INFO 0x00000271, 0x0000040C - - -#define REG_ADC_OFF1_K_ADDR 0x00000273 -#define BF_CH1_ADC_ADJUST_K_INFO 0x00000273, 0x00000E00 - -#define REG_ADC_OFF2_K_ADDR 0x00000274 -#define BF_CH2_ADC_ADJUST_K_INFO 0x00000274, 0x00000E00 - -#define REG_DATA1_K_ADDR 0x00000275 -#define BF_SIGNAL_SIZE_K_INFO 0x00000275, 0x00000300 -#define BF_SIGNAL_SHIFT_K_INFO 0x00000275, 0x00000503 -#define BF_DARK_SIZE_K_INFO 0x00000275, 0x00000308 -#define BF_DARK_SHIFT_K_INFO 0x00000275, 0x0000050B - -#define REG_DATA2_K_ADDR 0x00000276 -#define BF_LIT_SIZE_K_INFO 0x00000276, 0x00000300 -#define BF_LIT_SHIFT_K_INFO 0x00000276, 0x00000503 - -#define REG_DECIMATE_K_ADDR 0x00000277 -#define BF_DECIMATE_FACTOR_K_INFO 0x00000277, 0x00000704 - -#define REG_DIGINT_LIT_K_ADDR 0x00000278 -#define BF_LIT_OFFSET_K_INFO 0x00000278, 0x00000900 - -#define REG_DIGINT_DARK_K_ADDR 0x00000279 -#define BF_DARK1_OFFSET_K_INFO 0x00000279, 0x00000700 -#define BF_DARK2_OFFSET_K_INFO 0x00000279, 0x00000907 - -#define REG_TS_CTRL_L_ADDR 0x00000280 -#define BF_TIMESLOT_OFFSET_L_INFO 0x00000280, 0x00000A00 -#define BF_INPUT_R_SELECT_L_INFO 0x00000280, 0x0000010A -#define BF_SAMPLE_TYPE_L_INFO 0x00000280, 0x0000030B -#define BF_CH2_EN_L_INFO 0x00000280, 0x0000010E -#define BF_SUBSAMPLE_L_INFO 0x00000280, 0x0000010F - -#define REG_TS_PATH_L_ADDR 0x00000281 -#define BF_AFE_PATH_CFG_L_INFO 0x00000281, 0x00000700 -#define BF_AFE_INT_C_BUF_L_INFO 0x00000281, 0x00000108 -#define BF_GOUT_L_INFO 0x00000281, 0x00000109 -#define BF_AMBIENT_CANCELLATION_L_INFO 0x00000281, 0x0000020A -#define BF_PRE_WIDTH_L_INFO 0x00000281, 0x0000040C - -#define REG_INPUTS_L_ADDR 0x00000282 -#define BF_INP12_L_INFO 0x00000282, 0x00000400 -#define BF_INP34_L_INFO 0x00000282, 0x00000404 - -#define REG_CATHODE_L_ADDR 0x00000283 -#define BF_VC1_SEL_L_INFO 0x00000283, 0x00000200 -#define BF_VC1_ALT_L_INFO 0x00000283, 0x00000202 -#define BF_VC1_PULSE_L_INFO 0x00000283, 0x00000204 -#define BF_VC2_SEL_L_INFO 0x00000283, 0x00000206 -#define BF_VC2_ALT_L_INFO 0x00000283, 0x00000208 -#define BF_VC2_PULSE_L_INFO 0x00000283, 0x0000020A -#define BF_PRECON_L_INFO 0x00000283, 0x0000030C - -#define REG_AFE_TRIM1_L_ADDR 0x00000284 -#define BF_TIA_GAIN_CH1_L_INFO 0x00000284, 0x00000300 -#define BF_TIA_GAIN_CH2_L_INFO 0x00000284, 0x00000303 -#define BF_VREF_PULSE_VAL_L_INFO 0x00000284, 0x00000206 -#define BF_AFE_TRIM_VREF_L_INFO 0x00000284, 0x00000208 -#define BF_VREF_PULSE_L_INFO 0x00000284, 0x0000010A -#define BF_AFE_TRIM_INT1_L_INFO 0x00000284, 0x0000020B -#define BF_AFE_TRIM_INT2_L_INFO 0x00000284, 0x0000020D -#define BF_AFE_TIA_SAT_DETECT_EN_L_INFO 0x00000284, 0x0000010F - -#define REG_AFE_TRIM2_L_ADDR 0x00000285 -#define BF_AFE_TRIM_INT1_CAP_L_INFO 0x00000285, 0x0000010C -#define BF_AFE_TRIM_INT2_CAP_L_INFO 0x00000285, 0x0000010D - -#define REG_AFE_DAC1_L_ADDR 0x00000286 -#define BF_DAC_LED_DC_CH1_L_INFO 0x00000286, 0x00000700 -#define BF_DAC_AMBIENT_CH1_L_INFO 0x00000286, 0x00000907 - -#define REG_AFE_DAC2_L_ADDR 0x00000287 -#define BF_DAC_LED_DC_CH2_L_INFO 0x00000287, 0x00000700 -#define BF_DAC_AMBIENT_CH2_L_INFO 0x00000287, 0x00000907 - -#define REG_LED_POW12_L_ADDR 0x00000288 -#define BF_LED_CURRENT1_L_INFO 0x00000288, 0x00000700 -#define BF_LED_DRIVESIDE1_L_INFO 0x00000288, 0x00000107 -#define BF_LED_CURRENT2_L_INFO 0x00000288, 0x00000708 -#define BF_LED_DRIVESIDE2_L_INFO 0x00000288, 0x0000010F - -#define REG_LED_MODE_L_ADDR 0x00000289 -#define BF_LED_MODE1_L_INFO 0x00000289, 0x00000100 -#define BF_LED_MODE2_L_INFO 0x00000289, 0x00000101 - -#define REG_MOD_PULSE_L_ADDR 0x00000290 -#define BF_MOD_OFFSET_L_INFO 0x00000290, 0x00000800 -#define BF_MOD_WIDTH_L_INFO 0x00000290, 0x00000808 - -#define REG_PATTERN1_L_ADDR 0x00000291 -#define BF_REVERSE_INTEG_L_INFO 0x00000291, 0x00000400 -#define BF_SUBTRACT_L_INFO 0x00000291, 0x00000404 -#define BF_MOD_DISABLE_L_INFO 0x00000291, 0x00000408 -#define BF_LED_DISABLE_L_INFO 0x00000291, 0x0000040C - - -#define REG_ADC_OFF1_L_ADDR 0x00000293 -#define BF_CH1_ADC_ADJUST_L_INFO 0x00000293, 0x00000E00 - -#define REG_ADC_OFF2_L_ADDR 0x00000294 -#define BF_CH2_ADC_ADJUST_L_INFO 0x00000294, 0x00000E00 - -#define REG_DATA1_L_ADDR 0x00000295 -#define BF_SIGNAL_SIZE_L_INFO 0x00000295, 0x00000300 -#define BF_SIGNAL_SHIFT_L_INFO 0x00000295, 0x00000503 -#define BF_DARK_SIZE_L_INFO 0x00000295, 0x00000308 -#define BF_DARK_SHIFT_L_INFO 0x00000295, 0x0000050B - -#define REG_DATA2_L_ADDR 0x00000296 -#define BF_LIT_SIZE_L_INFO 0x00000296, 0x00000300 -#define BF_LIT_SHIFT_L_INFO 0x00000296, 0x00000503 - -#define REG_DECIMATE_L_ADDR 0x00000297 -#define BF_DECIMATE_FACTOR_L_INFO 0x00000297, 0x00000704 - -#define REG_DIGINT_LIT_L_ADDR 0x00000298 -#define BF_LIT_OFFSET_L_INFO 0x00000298, 0x00000900 - -#define REG_DIGINT_DARK_L_ADDR 0x00000299 -#define BF_DARK1_OFFSET_L_INFO 0x00000299, 0x00000700 -#define BF_DARK2_OFFSET_L_INFO 0x00000299, 0x00000907 - -#define REG_BIOZ_AFECON_A_ADDR 0x000002A0 -#define BF_BIOZ_DACREFEN_A_INFO 0x000002A0, 0x00000100 -#define BF_BIOZ_TIAEN_A_INFO 0x000002A0, 0x00000101 -#define BF_BIOZ_TIMESLOT_OFFSET_A_INFO 0x000002A0, 0x00000A02 - -#define REG_BIOZ_WGFCW_LOW_A_ADDR 0x000002A1 -#define BF_BIOZ_SINEFCW_L_A_INFO 0x000002A1, 0x00001000 - -#define REG_BIOZ_WGFCW_HI_A_ADDR 0x000002A2 -#define BF_BIOZ_SINEFCW_H_A_INFO 0x000002A2, 0x00000400 - -#define REG_BIOZ_WGPHASE_LOW_A_ADDR 0x000002A3 -#define BF_BIOZ_SINE_PHASE_OFFSET_L_A_INFO 0x000002A3, 0x00001000 - -#define REG_BIOZ_WGPHASE_HI_A_ADDR 0x000002A4 -#define BF_BIOZ_SINE_PHASE_OFFSET_H_A_INFO 0x000002A4, 0x00000400 - -#define REG_BIOZ_WGOFFSET_A_ADDR 0x000002A5 -#define BF_BIOZ_SINE_OFFSET_A_INFO 0x000002A5, 0x00000C00 - -#define REG_BIOZ_WGAMPLITUDE_A_ADDR 0x000002A6 -#define BF_BIOZ_SINEAMPLITUDE_A_INFO 0x000002A6, 0x00000B00 - -#define REG_BIOZ_DACCON_A_ADDR 0x000002A7 -#define BF_BIOZ_RATE_DIV_A_INFO 0x000002A7, 0x00000800 -#define BF_BIOZ_BW50KEN_A_INFO 0x000002A7, 0x00000108 -#define BF_BIOZ_BW200KEN_A_INFO 0x000002A7, 0x00000109 -#define BF_BIOZ_DACBUFBW_A_INFO 0x000002A7, 0x0000030A -#define BF_BIOZ_EXBUFEN_A_INFO 0x000002A7, 0x0000010D - -#define REG_BIOZ_DACGAIN_A_ADDR 0x000002A8 -#define BF_BIOZ_DAC_GAIN_A_INFO 0x000002A8, 0x00000C00 -#define BF_BIOZ_DACGAIN_EN_A_INFO 0x000002A8, 0x0000010C - -#define REG_BIOZ_DACOFFSET_A_ADDR 0x000002A9 -#define BF_BIOZ_DAC_OFFSET_A_INFO 0x000002A9, 0x00000C00 -#define BF_BIOZ_DACOFFSET_EN_A_INFO 0x000002A9, 0x0000010C - -#define REG_BIOZ_DFTPHASE_LOW_A_ADDR 0x000002B0 -#define BF_BIOZ_DFT_PHASE_OFFSET_L_A_INFO 0x000002B0, 0x00001000 - -#define REG_BIOZ_DFTPHASE_HI_A_ADDR 0x000002B1 -#define BF_BIOZ_DFT_PHASE_OFFSET_H_A_INFO 0x000002B1, 0x00000400 - -#define REG_BIOZ_AFECON_E_ADDR 0x00000320 -#define BF_BIOZ_DACREFEN_E_INFO 0x00000320, 0x00000100 -#define BF_BIOZ_TIAEN_E_INFO 0x00000320, 0x00000101 -#define BF_BIOZ_TIMESLOT_OFFSET_E_INFO 0x00000320, 0x00000A02 - -#define REG_BIOZ_WGFCW_LOW_E_ADDR 0x00000321 -#define BF_BIOZ_SINEFCW_L_E_INFO 0x00000321, 0x00001000 - -#define REG_BIOZ_WGFCW_HI_E_ADDR 0x00000322 -#define BF_BIOZ_SINEFCW_H_E_INFO 0x00000322, 0x00000400 - -#define REG_BIOZ_WGPHASE_LOW_E_ADDR 0x00000323 -#define BF_BIOZ_SINE_PHASE_OFFSET_L_E_INFO 0x00000323, 0x00001000 - -#define REG_BIOZ_WGPHASE_HI_E_ADDR 0x00000324 -#define BF_BIOZ_SINE_PHASE_OFFSET_H_E_INFO 0x00000324, 0x00000400 - -#define REG_BIOZ_WGOFFSET_E_ADDR 0x00000325 -#define BF_BIOZ_SINE_OFFSET_E_INFO 0x00000325, 0x00000C00 - -#define REG_BIOZ_WGAMPLITUDE_E_ADDR 0x00000326 -#define BF_BIOZ_SINEAMPLITUDE_E_INFO 0x00000326, 0x00000B00 - -#define REG_BIOZ_DACCON_E_ADDR 0x00000327 -#define BF_BIOZ_RATE_DIV_E_INFO 0x00000327, 0x00000800 -#define BF_BIOZ_BW50KEN_E_INFO 0x00000327, 0x00000108 -#define BF_BIOZ_BW200KEN_E_INFO 0x00000327, 0x00000109 -#define BF_BIOZ_DACBUFBW_E_INFO 0x00000327, 0x0000030A -#define BF_BIOZ_EXBUFEN_E_INFO 0x00000327, 0x0000010D - -#define REG_BIOZ_DACGAIN_E_ADDR 0x00000328 -#define BF_BIOZ_DAC_GAIN_E_INFO 0x00000328, 0x00000C00 -#define BF_BIOZ_DACGAIN_EN_E_INFO 0x00000328, 0x0000010C - -#define REG_BIOZ_DACOFFSET_E_ADDR 0x00000329 -#define BF_BIOZ_DAC_OFFSET_E_INFO 0x00000329, 0x00000C00 -#define BF_BIOZ_DACOFFSET_EN_E_INFO 0x00000329, 0x0000010C - -#define REG_BIOZ_DFTPHASE_LOW_E_ADDR 0x00000330 -#define BF_BIOZ_DFT_PHASE_OFFSET_L_E_INFO 0x00000330, 0x00001000 - -#define REG_BIOZ_DFTPHASE_HI_E_ADDR 0x00000331 -#define BF_BIOZ_DFT_PHASE_OFFSET_H_E_INFO 0x00000331, 0x00000400 - -#define REG_BIOZ_AFECON_F_ADDR 0x00000340 -#define BF_BIOZ_DACREFEN_F_INFO 0x00000340, 0x00000100 -#define BF_BIOZ_TIAEN_F_INFO 0x00000340, 0x00000101 -#define BF_BIOZ_TIMESLOT_OFFSET_F_INFO 0x00000340, 0x00000A02 - -#define REG_BIOZ_WGFCW_LOW_F_ADDR 0x00000341 -#define BF_BIOZ_SINEFCW_L_F_INFO 0x00000341, 0x00001000 - -#define REG_BIOZ_WGFCW_HI_F_ADDR 0x00000342 -#define BF_BIOZ_SINEFCW_H_F_INFO 0x00000342, 0x00000400 - -#define REG_BIOZ_WGPHASE_LOW_F_ADDR 0x00000343 -#define BF_BIOZ_SINE_PHASE_OFFSET_L_F_INFO 0x00000343, 0x00001000 - -#define REG_BIOZ_WGPHASE_HI_F_ADDR 0x00000344 -#define BF_BIOZ_SINE_PHASE_OFFSET_H_F_INFO 0x00000344, 0x00000400 - -#define REG_BIOZ_WGOFFSET_F_ADDR 0x00000345 -#define BF_BIOZ_SINE_OFFSET_F_INFO 0x00000345, 0x00000C00 - -#define REG_BIOZ_WGAMPLITUDE_F_ADDR 0x00000346 -#define BF_BIOZ_SINEAMPLITUDE_F_INFO 0x00000346, 0x00000B00 - -#define REG_BIOZ_DACCON_F_ADDR 0x00000347 -#define BF_BIOZ_RATE_DIV_F_INFO 0x00000347, 0x00000800 -#define BF_BIOZ_BW50KEN_F_INFO 0x00000347, 0x00000108 -#define BF_BIOZ_BW200KEN_F_INFO 0x00000347, 0x00000109 -#define BF_BIOZ_DACBUFBW_F_INFO 0x00000347, 0x0000030A -#define BF_BIOZ_EXBUFEN_F_INFO 0x00000347, 0x0000010D - -#define REG_BIOZ_DACGAIN_F_ADDR 0x00000348 -#define BF_BIOZ_DAC_GAIN_F_INFO 0x00000348, 0x00000C00 -#define BF_BIOZ_DACGAIN_EN_F_INFO 0x00000348, 0x0000010C - -#define REG_BIOZ_DACOFFSET_F_ADDR 0x00000349 -#define BF_BIOZ_DAC_OFFSET_F_INFO 0x00000349, 0x00000C00 -#define BF_BIOZ_DACOFFSET_EN_F_INFO 0x00000349, 0x0000010C - -#define REG_BIOZ_DFTPHASE_LOW_F_ADDR 0x00000350 -#define BF_BIOZ_DFT_PHASE_OFFSET_L_F_INFO 0x00000350, 0x00001000 - -#define REG_BIOZ_DFTPHASE_HI_F_ADDR 0x00000351 -#define BF_BIOZ_DFT_PHASE_OFFSET_H_F_INFO 0x00000351, 0x00000400 - -#endif /* __ADI_ADPD6000_BF_REG_H__ */ -/*! @} */ diff --git a/src/adpd/lib/ADPD6000/adi_adpd6000_bioz.c_bak b/src/adpd/lib/ADPD6000/adi_adpd6000_bioz.c_bak deleted file mode 100644 index 190714c..0000000 --- a/src/adpd/lib/ADPD6000/adi_adpd6000_bioz.c_bak +++ /dev/null @@ -1,591 +0,0 @@ -/*! - * @brief BIOZ APIs Implementation - * @copyright Copyright (c) 2021 - Analog Devices Inc. All Rights Reserved. - */ - -/*! - * @addtogroup adi_adpd6000_sdk - * @{ - */ - -/*============= I N C L U D E S ============*/ -#include "adi_adpd6000.h" -#include -#include - -/*============= D E F I N E S ==============*/ -#define PI 3.1415926 - -/*============= D A T A ====================*/ -float bioz_r_Cal = 2000.0; - -/*============= C O D E ====================*/ -int32_t adi_adpd6000_bioz_set_slot_mode(adi_adpd6000_device_t *device, adi_adpd6000_bioz_slot_mode_e mode) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(mode > API_ADPD6000_BIOZ_SLOT_ABCDEF); - - err = adi_adpd6000_hal_bf_write(device, BF_BIOZ_TIMESLOT_EN_INFO, mode); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_get_slot_num(adi_adpd6000_device_t *device, uint8_t *slot_num) -{ - int32_t err; - uint16_t data; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_read(device, BF_BIOZ_TIMESLOT_EN_INFO, &data); - ADPD6000_ERROR_RETURN(err); - *slot_num = data; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_set_timeslot_offset(adi_adpd6000_device_t *device, uint8_t slot, uint16_t offset) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_TIMESLOT_OFFSET_A_INFO, offset); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_wave_set_freq(adi_adpd6000_device_t *device, uint8_t slot, uint32_t freq) -{ - int32_t err; - uint64_t fcw; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - /* fcw = 2^26* freq/32e6 */ - fcw = (uint64_t)67108864 * (uint64_t)freq; - fcw = fcw / 32000000; - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_SINEFCW_L_A_INFO, fcw & 0xffff); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_SINEFCW_H_A_INFO, (fcw >> 16) & 0xf); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_wave_set_phase(adi_adpd6000_device_t *device, uint8_t slot, uint16_t phase) -{ - int32_t err; - uint64_t phase_offset; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(phase > 360); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - /* phase_offset = phase/360*2^20 */ - phase_offset = (uint64_t)1048576 * (uint64_t)phase; - phase_offset = phase_offset / 360; - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_SINE_PHASE_OFFSET_L_A_INFO, phase_offset & 0xffff); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_SINE_PHASE_OFFSET_H_A_INFO, (phase_offset >> 16) & 0xf); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_wave_set_offset(adi_adpd6000_device_t *device, uint8_t slot, uint16_t offset) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_SINE_OFFSET_A_INFO, offset); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_wave_set_amp(adi_adpd6000_device_t *device, uint8_t slot, uint16_t amp) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_SINEAMPLITUDE_A_INFO, amp); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_cfg_wave(adi_adpd6000_device_t *device, uint8_t slot, uint16_t amp, uint32_t freq, uint16_t phase, uint16_t offset) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_bioz_wave_set_amp(device, slot, amp); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_bioz_wave_set_freq(device, slot, freq); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_bioz_wave_set_phase(device, slot, phase); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_bioz_wave_set_offset(device, slot, offset); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_tia_set_gain(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_tia_gain_res_e res, uint8_t cap) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - ADPD6000_INVALID_PARAM_RETURN(res > API_ADPD6000_BIOZ_TIA_GAIN_10K); - ADPD6000_INVALID_PARAM_RETURN(cap > 15); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_TIA_RGAIN_A_INFO, res); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_TIA_CGAIN_A_INFO, cap); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_tia_enable_lowpower(adi_adpd6000_device_t *device, uint8_t slot, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_TIA_LPMODE_A_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_dac_set_gain(adi_adpd6000_device_t *device, uint8_t slot, uint16_t gain) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_DAC_GAIN_A_INFO, gain); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_bypass_sinc3_filter(adi_adpd6000_device_t *device, uint8_t slot, bool en_bypass) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_SINC3BYP_A_INFO, en_bypass ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_sinc3_set_sample_rate(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_sinc3_os_rate_e rate) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - ADPD6000_INVALID_PARAM_RETURN(rate > API_ADPD6000_BIOZ_SINC3_OVERSAMPLE_RATE_5); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_SINC3BYP_A_INFO, rate); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_enable_average(adi_adpd6000_device_t *device, uint8_t slot, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_AVRGEN_A_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_set_average_sample(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_sinc3_avg_e sample) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - ADPD6000_INVALID_PARAM_RETURN(sample > API_ADPD6000_BIOZ_SINC3_AVERAGE_16); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_AVRGNUM_A_INFO, sample); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_dft_set_phase(adi_adpd6000_device_t *device, uint8_t slot, uint16_t phase) -{ - int32_t err; - uint64_t phase_offset; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(phase > 360); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - /* phase_offset = phase/360*2^20 */ - phase_offset = (uint64_t)1048576 * (uint64_t)phase; - phase_offset = phase_offset / 360; - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_DFT_PHASE_OFFSET_L_A_INFO, phase_offset & 0xffff); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_DFT_PHASE_OFFSET_H_A_INFO, (phase_offset >> 16) & 0xf); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_dft_set_point_number(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_dft_point_e num) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(num > API_ADPD6000_BIOZ_DFT_POINT_8192); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_DFTNUM_A_INFO, num); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_dft_enable_hanning(adi_adpd6000_device_t *device, uint8_t slot, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_HANNINGEN_A_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_enable_dac_ref(adi_adpd6000_device_t *device, uint8_t slot, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_DACREFEN_A_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_enable_tia(adi_adpd6000_device_t *device, uint8_t slot, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_TIAEN_A_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_enable_exbuf(adi_adpd6000_device_t *device, uint8_t slot, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_EXBUFEN_A_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_enable_pga(adi_adpd6000_device_t *device, uint8_t slot, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_PGA_PD_A_INFO, enable ? 0 : 1); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_set_amp_mode(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_amp_mode_e mode) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_AMP_OFF_A_INFO, mode); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_set_slot_connection(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_slot_connect_e mode) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(mode > API_ADPD6000_BIOZ_CONN_BIO_IMP_CUR); - - switch (mode) - { - case API_ADPD6000_BIOZ_CONN_EXT_RCAL_VOL: - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_BIOZ_ANA_CTRL2_A_ADDR, 0x2803); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_BIOZ_ANA_CTRL3_A_ADDR, 0); - ADPD6000_ERROR_RETURN(err); - break; - case API_ADPD6000_BIOZ_CONN_EXT_RCAL_CUR: - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_BIOZ_ANA_CTRL2_A_ADDR, 0x0003); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_BIOZ_ANA_CTRL3_A_ADDR, 0); - ADPD6000_ERROR_RETURN(err); - break; - case API_ADPD6000_BIOZ_CONN_INT_RCAL_VOL: - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_BIOZ_ANA_CTRL2_A_ADDR, 0x3c00); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_BIOZ_ANA_CTRL3_A_ADDR, 0x03); - ADPD6000_ERROR_RETURN(err); - break; - case API_ADPD6000_BIOZ_CONN_INT_RCAL_CUR: - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_BIOZ_ANA_CTRL2_A_ADDR, 0x0000); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_BIOZ_ANA_CTRL3_A_ADDR, 0x03); - ADPD6000_ERROR_RETURN(err); - break; - case API_ADPD6000_BIOZ_CONN_BIO_IMP_VOL: - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_BIOZ_ANA_CTRL2_A_ADDR, 0x5400); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_BIOZ_ANA_CTRL3_A_ADDR, 0); - ADPD6000_ERROR_RETURN(err); - break; - case API_ADPD6000_BIOZ_CONN_BIO_IMP_CUR: - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_BIOZ_ANA_CTRL2_A_ADDR, 0x4000); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_BIOZ_ANA_CTRL3_A_ADDR, 0); - ADPD6000_ERROR_RETURN(err); - break; - default: - break; - } - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_sel_exbuf_input(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_imp_pin_e input) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_DSW_A_INFO, input); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_sel_tia_input(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_bioz_imp_pin_e input) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_BIOZ_TSW_A_INFO, input); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_read_fifo(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, uint32_t *bioz_real, uint32_t *bioz_imag, uint8_t *bioz_num) -{ - int32_t err; - uint8_t i, bioz_count = 0;; - uint8_t fifo_data[3]; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - for (i = 0; i < fifo->bioz_slot; i++) - { - err = adi_adpd6000_device_fifo_read_bytes(device, fifo_data, 3); - ADPD6000_ERROR_RETURN(err); - *bioz_real++ = (fifo_data[0] << 16) | (fifo_data[1] << 8) | (fifo_data[2]); - - err = adi_adpd6000_device_fifo_read_bytes(device, fifo_data, 3); - ADPD6000_ERROR_RETURN(err); - *bioz_imag++ = (fifo_data[0] << 16) | (fifo_data[1] << 8) | (fifo_data[2]); - bioz_count++; - } - *bioz_num = bioz_count; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_read_fifo_struct(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, adi_adpd6000_bioz_slot_data_t *bioz, uint8_t *bioz_num) -{ - int32_t err; - uint8_t i, bioz_count = 0;; - uint8_t fifo_data[3]; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - for (i = 0; i < fifo->bioz_slot; i++) - { - err = adi_adpd6000_device_fifo_read_bytes(device, fifo_data, 3); - ADPD6000_ERROR_RETURN(err); - bioz[i].real = (fifo_data[0] << 16) | (fifo_data[1] << 8) | (fifo_data[2]); - - err = adi_adpd6000_device_fifo_read_bytes(device, fifo_data, 3); - ADPD6000_ERROR_RETURN(err); - bioz[i].imag = (fifo_data[0] << 16) | (fifo_data[1] << 8) | (fifo_data[2]); - bioz_count++; - } - *bioz_num = bioz_count; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_cal_ms6(uint32_t O0OOO00O00[(0X2B68+034*177-16062)],uint32_t O00OO0OO0O[(0X1E98+021*145-10291)],float*amp,float*phase) -{ - int32_t OO00OOOOOO[(0X3463+050*156-19645)]; - int32_t O0OO00OO00[(0X1E47+072*130-15285)]; - for(int OO0O00O0O0=(0X34E9+015*157-15586);OO0O00O0O0<(0X3DF7+064*183-25373);OO0O00O0O0++){OO00OOOOOO[OO0O00O0O0]=(((O0OOO00O00[OO0O00O0O0]&(0x800000)))>(0X1FF7+03*148-8627))?(O0OOO00O00[OO0O00O0O0]-0xffffff-(0X60DC+012*183-26625)):O0OOO00O00[OO0O00O0O0];O0OO00OO00[OO0O00O0O0]=(((O00OO0OO0O[OO0O00O0O0]&(0x800000)))>(0X406D+05*122-17103))?(O00OO0OO0O[OO0O00O0O0]-0xffffff-(0XF17+014*108-5158)):O00OO0OO0O[OO0O00O0O0];} - _Complex double OOO0OOO0O0=OO00OOOOOO[(0X4EE1+076*145-29183)]-O0OO00OO00[(0X582F+056*148-29383)]*I; - _Complex double OOO000OO0O=OO00OOOOOO[(0X7932+015*134-32767)]-O0OO00OO00[(0X36E3+017*166-16540)]*I; - _Complex double OO00OO0000=OO00OOOOOO[(0X7225+070*176-39075)]-O0OO00OO00[(0X4BE8+014*198-21806)]*I; - _Complex double OO00O0O000=OO00OOOOOO[(0X67C5+066*142-34230)]-O0OO00OO00[(0X701B+01*134-28830)]*I; - _Complex double O0O00OOOOO=OO00OOOOOO[(0X7AF8+076*134-39784)]-O0OO00OO00[(0X21DF+072*104-14699)]*I; - _Complex double O0O000OOOO=OO00OOOOOO[(0X7D05+020*170-34720)]-O0OO00OO00[(0X6E3A+031*185-32838)]*I; - _Complex double O000O0OOOO=bioz_r_Cal*OOO0OOO0O0;_Complex double O0000OOOOO=O000O0OOOO/OOO000OO0O; - _Complex double O0O000O0O0=O000O0OOOO/OO00OO0000;_Complex double O000OO0O00=O000O0OOOO/OO00O0O000; - _Complex double OOO00O000O=O000O0OOOO/O0O00OOOOO;_Complex double OOOO000OOO=O000O0OOOO/O0O000OOOO; - _Complex double O000OOO00O=-(0X686C+021*130-28940)*O0000OOOOO*O0O000O0O0*OOOO000OOO*(O0000OOOOO*O0O000O0O0+O0000OOOOO*OOOO000OOO-O0O000O0O0*OOOO000OOO)/(O0000OOOOO*O0000OOOOO*O0O000O0O0*O0O000O0O0-(0X32C8+011*192-14726)*O0000OOOOO*O0000OOOOO*O0O000O0O0*OOOO000OOO+O0000OOOOO*O0000OOOOO*OOOO000OOO*OOOO000OOO-(0X4C32+020*189-22528)*O0000OOOOO*O0O000O0O0*O0O000O0O0*OOOO000OOO-(0X2FB8+070*106-18150)*O0000OOOOO*O0O000O0O0*OOOO000OOO*OOOO000OOO+O0O000O0O0*O0O000O0O0*OOOO000OOO*OOOO000OOO); - _Complex double O0OO0OO0O0=-(0XB5B+04*143-3477)*O0000OOOOO*O0O000O0O0*OOOO000OOO*(O0000OOOOO*O0O000O0O0-O0000OOOOO*OOOO000OOO+O0O000O0O0*OOOO000OOO)/(O0000OOOOO*O0000OOOOO*O0O000O0O0*O0O000O0O0-(0X822+056*118-7508)*O0000OOOOO*O0000OOOOO*O0O000O0O0*OOOO000OOO+O0000OOOOO*O0000OOOOO*OOOO000OOO*OOOO000OOO-(0X71F2+015*140-30988)*O0000OOOOO*O0O000O0O0*O0O000O0O0*OOOO000OOO-(0X1AF8+0*102-6902)*O0000OOOOO*O0O000O0O0*OOOO000OOO*OOOO000OOO+O0O000O0O0*O0O000O0O0*OOOO000OOO*OOOO000OOO); - _Complex double O0O0O0O0OO=-(0X6A51+074*142-35735)*O000OO0O00*OOO00O000O*OOOO000OOO*(O000OO0O00*OOO00O000O+O000OO0O00*OOOO000OOO-OOO00O000O*OOOO000OOO)/(O000OO0O00*O000OO0O00*OOO00O000O*OOO00O000O-(0X30DB+071*146-20827)*O000OO0O00*O000OO0O00*OOO00O000O*OOOO000OOO+O000OO0O00*O000OO0O00*OOOO000OOO*OOOO000OOO-(0X5432+07*158-22658)*O000OO0O00*OOO00O000O*OOO00O000O*OOOO000OOO-(0XAE3+032*125-6035)*O000OO0O00*OOO00O000O*OOOO000OOO*OOOO000OOO+OOO00O000O*OOO00O000O*OOOO000OOO*OOOO000OOO); - _Complex double O00O0OOOO0=-(0X7F83+027*169-36528)*O000OO0O00*OOO00O000O*OOOO000OOO*(O000OO0O00*OOO00O000O-O000OO0O00*OOOO000OOO+OOO00O000O*OOOO000OOO)/(O000OO0O00*O000OO0O00*OOO00O000O*OOO00O000O-(0X7771+027*186-34853)*O000OO0O00*O000OO0O00*OOO00O000O*OOOO000OOO+O000OO0O00*O000OO0O00*OOOO000OOO*OOOO000OOO-(0XE18+025*195-7701)*O000OO0O00*OOO00O000O*OOO00O000O*OOOO000OOO-(0X521F+01*139-21160)*O000OO0O00*OOO00O000O*OOOO000OOO*OOOO000OOO+OOO00O000O*OOO00O000O*OOOO000OOO*OOOO000OOO); - _Complex double O0OOO0O00O=(-O000OOO00O*O0OO0OO0O0*O0O0O0O0OO-O000OOO00O*O0OO0OO0O0*O00O0OOOO0-O000OOO00O*O0O0O0O0OO*O00O0OOOO0+O000OOO00O*O0O0O0O0OO*OOOO000OOO+O000OOO00O*O00O0OOOO0*OOOO000OOO-O0OO0OO0O0*O0O0O0O0OO*O00O0OOOO0+O0OO0OO0O0*O0O0O0O0OO*OOOO000OOO+O0OO0OO0O0*O00O0OOOO0*OOOO000OOO)/(O000OOO00O*O0O0O0O0OO+O000OOO00O*O00O0OOOO0+O0OO0OO0O0*O0O0O0O0OO+O0OO0OO0O0*O00O0OOOO0); - *amp=cabs(O0OOO0O00O); - *phase=catan(cimag(O0OOO0O00O)/creal(O0OOO0O00O))*(0X1E94+013*124-9012)/PI; - return API_ADPD6000_ERROR_OK;} - -int32_t adi_adpd6000_bioz_cal_ms4(uint32_t Real[4], uint32_t Image[4], float *amp, float *phase) -{ - float real, imag; - float mod[4],ph[4]; - float zbody, ph_cal; - uint8_t i; - - for (i = 0; i < 4; i++) - { - real = (((Real[i] & 0x800000)) > 0) ? ((float)Real[i] - (float)0xffffff - 1) : (float)Real[i]; - imag = (((Image[i] & 0x800000)) > 0) ? ((float)Image[i] - (float)0xffffff - 1) : (float)Image[i]; - mod[i] = sqrt(real * real + imag * imag); - ph[i] = atan(-imag/real); - } - zbody = bioz_r_Cal * mod[1] * mod[2] / (mod[0] * mod[3]); - ph_cal = (ph[1] + ph[2] - ph[3] - ph[0]) * 180.0 / PI; - *amp = zbody; - *phase = ph_cal; - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_set_rcal(float rcal) -{ - bioz_r_Cal = rcal; - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_get_rcal(float *rcal) -{ - if (rcal == NULL) - { - return -1; - } - *rcal = bioz_r_Cal; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_bioz_get_internal_rcal(adi_adpd6000_device_t *device, float *rcal) -{ - int32_t err = API_ADPD6000_ERROR_OK; - uint16_t fuse = 0; - uint16_t timeout = 0x1000; - uint16_t data, fuse_code; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_NULL_POINTER_RETURN(rcal); - - err = adi_adpd6000_device_enable_slot_operation_mode_go(device, false); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, 0x00000053, 0x00000104, 1); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_reg_write(device, 0x00000044, 7); - ADPD6000_ERROR_RETURN(err); - - while (timeout--) - { - err = adi_adpd6000_hal_bf_read(device, 0x000000d6, 0x00000201, &fuse); - ADPD6000_ERROR_RETURN(err); - if (fuse == 2) - { - break; - } - } - - if (fuse == 2) - { - err = adi_adpd6000_hal_reg_read(device, 0x000000c1, &data); - ADPD6000_ERROR_RETURN(err); - fuse_code = data << 8; - err = adi_adpd6000_hal_reg_read(device, 0x000000c2, &data); - ADPD6000_ERROR_RETURN(err); - fuse_code |= data; - *rcal = (((float)fuse_code - 1024) / 1000.0 + 1) * 2000.0; - } - else - { - err = API_ADPD6000_ERROR_FUSE_NOT_DONE; - } - - adi_adpd6000_hal_reg_write(device, 0x00000044, 0); - adi_adpd6000_hal_bf_write(device, 0x00000053, 0x00000104, 0); - - return err; -} -/*! @} */ diff --git a/src/adpd/lib/ADPD6000/adi_adpd6000_config.h b/src/adpd/lib/ADPD6000/adi_adpd6000_config.h deleted file mode 100644 index 5fefbcc..0000000 --- a/src/adpd/lib/ADPD6000/adi_adpd6000_config.h +++ /dev/null @@ -1,36 +0,0 @@ -/*! - * @brief ADPD6000/ADPD6000-LITE Configuration Header - * @copyright Copyright (c) 2021 - Analog Devices Inc. All Rights Reserved. - */ - -/*! - * @addtogroup adi_adpd6000_sdk - * @{ - */ - -#ifndef __ADI_ADPD6000_CONFIG_H__ -#define __ADI_ADPD6000_CONFIG_H__ - -/*============= D E F I N E S ==============*/ -/*!< verbose log report control, bit0~15: level, bit16~31: content */ -#define ADPD6000_LOG_ERR_MSG 0x00000001 /*!< error message */ -#define ADPD6000_LOG_WARN_MSG 0x00000002 /*!< warning message */ -#define ADPD6000_LOG_INFO_MSG 0x00000004 /*!< info message */ -#define ADPD6000_LOG_MISC_MSG 0x00010000 /*!< miscellaneous message */ -#define ADPD6000_LOG_FUNC_MSG 0x00020000 /*!< function call message */ -#define ADPD6000_LOG_VAR_MSG 0x00040000 /*!< variable related message */ -#define ADPD6000_LOG_REG_MSG 0x00080000 /*!< register r/w message */ - -#define ADPD6000_LOG_NONE_MSG 0x00000000 /*!< all not selected */ -#define ADPD6000_LOG_ALL_MSG 0xffffffff /*!< all selected */ - -#ifndef ADPD6000_REPORT_VERBOSE -#define ADPD6000_REPORT_VERBOSE 0 /*!< actual log control */ -#endif - -/*!< max buffer size that sdk is using internally for control port access */ -#define ADPD6000_SDK_MAX_BUFSIZE 16 /*!< buffer size sdk allocates for control port access */ - -#endif /* __ADI_ADPD6000_CONFIG_H__ */ - -/*! @} */ diff --git a/src/adpd/lib/ADPD6000/adi_adpd6000_device.c b/src/adpd/lib/ADPD6000/adi_adpd6000_device.c deleted file mode 100644 index b2d8e21..0000000 --- a/src/adpd/lib/ADPD6000/adi_adpd6000_device.c +++ /dev/null @@ -1,360 +0,0 @@ -/*! - * @brief ADPD6000 Device Level APIs Implementation - * @copyright Copyright (c) 2021 - Analog Devices Inc. All Rights Reserved. - */ - -/*! - * @addtogroup adi_adpd6000_sdk - * @{ - */ - -/*============= I N C L U D E S ============*/ -#include "adi_adpd6000.h" -/*============= D E F I N E S ==============*/ - -/*============= D A T A ====================*/ - -/*============= C O D E ====================*/ -int32_t adi_adpd6000_device_get_id(adi_adpd6000_device_t *device, uint8_t *chip_id, uint8_t *chip_rev) -{ - int32_t err; - uint16_t id, rev; - - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_NULL_POINTER_RETURN(chip_id); - ADPD6000_NULL_POINTER_RETURN(chip_rev); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_read(device, BF_CHIP_ID_INFO, &id); - ADPD6000_ERROR_RETURN(err); - *chip_id = (uint8_t)id; - - err = adi_adpd6000_hal_bf_read(device, BF_VERSION_INFO, &rev); - ADPD6000_ERROR_RETURN(err); - *chip_rev = (uint8_t)rev; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_get_sdk_rev(adi_adpd6000_device_t *device, uint32_t *rev) -{ - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_NULL_POINTER_RETURN(rev); - ADPD6000_LOG_FUNC(); - - *rev = ADPD6000_SDK_VER; - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_sw_reset(adi_adpd6000_device_t *device) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_write(device, BF_SW_RESET_INFO, 1); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_SW_RESET_INFO, 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_init(adi_adpd6000_device_t *device) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_device_sw_reset(device); - ADPD6000_ERROR_RETURN(err); - - /* Change default trim */ - err = adi_adpd6000_hal_reg_write(device, 0x0046, 0x2004); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_reg_write(device, 0x004c, 0x400b); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_reg_write(device, 0x0074, 0x0028); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_reg_write(device, 0x0077, 0x0100); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_enable_slot_operation_mode_go(adi_adpd6000_device_t *device, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_write(device, BF_OP_MODE_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_set_fifo_threshold(adi_adpd6000_device_t *device, uint16_t threshold) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(threshold > 0x1ff); - - err = adi_adpd6000_hal_bf_write(device, BF_FIFO_TH_INFO, threshold); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_clr_fifo(adi_adpd6000_device_t *device) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_write(device, BF_CLEAR_FIFO_INFO, 1); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_CLEAR_FIFO_INFO, 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_get_fifo_count(adi_adpd6000_device_t *device, uint16_t *count) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_NULL_POINTER_RETURN(count); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_read(device, BF_FIFO_BYTE_COUNT_INFO, count); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_fifo_read_bytes(adi_adpd6000_device_t *device, uint8_t *data, uint32_t len) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_NULL_POINTER_RETURN(data); - - err = adi_adpd6000_hal_fifo_read_bytes(device, REG_FIFO_DATA_ADDR, data, len); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_fifo_get_fifo_int_status(adi_adpd6000_device_t *device, uint8_t *status) -{ - int32_t err; - uint16_t data; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_NULL_POINTER_RETURN(status); - - err = adi_adpd6000_hal_reg_read(device, REG_FIFO_STATUS_ADDR, &data); - ADPD6000_ERROR_RETURN(err); - *status = (data >> 12) & 0x07; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_enable_fifo_thres_interrupt(adi_adpd6000_device_t *device, adi_adpd6000_interrupt_type_e type, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - - err = adi_adpd6000_hal_bf_write(device, type + BF_INTX_EN_FIFO_TH_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_enable_auto_clear_int(adi_adpd6000_device_t *device, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - - err = adi_adpd6000_hal_bf_write(device, BF_INT_ACLEAR_FIFO_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_get_sequence_fifo_config(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo) -{ - int32_t err; - uint16_t data, i; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_NULL_POINTER_RETURN(fifo); - - err = adi_adpd6000_hal_bf_read(device, BF_ECG_TIMESLOT_EN_INFO, &data); - ADPD6000_ERROR_RETURN(err); - fifo->ecg_slot = data; - err = adi_adpd6000_hal_bf_read(device, BF_PPG_TIMESLOT_EN_INFO, &data); - ADPD6000_ERROR_RETURN(err); - fifo->ppg_slot = data; - err = adi_adpd6000_hal_bf_read(device, BF_BIOZ_TIMESLOT_EN_INFO, &data); - ADPD6000_ERROR_RETURN(err); - fifo->bioz_slot = data; - - err = adi_adpd6000_hal_bf_read(device, BF_ENA_STAT_ECG_INFO, &data); - ADPD6000_ERROR_RETURN(err); - fifo->ecg_size = (data == 1) ? 4 : 3; - if ((fifo->ppg_slot == 0) && (fifo->bioz_slot == 0)) - { - fifo->ecg_over_sample = 1; - } - else - { - err = adi_adpd6000_hal_bf_read(device, BF_ECG_OVERSAMPLING_RATIO_INFO, &data); - ADPD6000_ERROR_RETURN(err); - fifo->ecg_over_sample = data; - } - fifo->sequence_size = fifo->ecg_slot * fifo->ecg_over_sample * fifo->ecg_size; - - fifo->ppg_chnl_num = 0; - for (i = 0; i < fifo->ppg_slot; i++) - { - fifo->ppg_chnl_num += 1; - err = adi_adpd6000_hal_bf_read(device, ADPD6000_TIME_SLOT_SPAN * i + BF_CH2_EN_A_INFO, &data); - ADPD6000_ERROR_RETURN(err); - fifo->ppg_fifo[i].ppg_chl2_en = data; - fifo->ppg_chnl_num += fifo->ppg_fifo[i].ppg_chl2_en; - - err = adi_adpd6000_hal_bf_read(device, ADPD6000_TIME_SLOT_SPAN * i + BF_SIGNAL_SIZE_A_INFO, &data); - ADPD6000_ERROR_RETURN(err); - fifo->ppg_fifo[i].signal_size = data; - - err = adi_adpd6000_hal_bf_read(device, ADPD6000_TIME_SLOT_SPAN * i + BF_DARK_SIZE_A_INFO, &data); - ADPD6000_ERROR_RETURN(err); - fifo->ppg_fifo[i].dark_size = data; - - err = adi_adpd6000_hal_bf_read(device, ADPD6000_TIME_SLOT_SPAN * i + BF_LIT_SIZE_A_INFO, &data); - ADPD6000_ERROR_RETURN(err); - fifo->ppg_fifo[i].lit_size = data; - - fifo->sequence_size += (fifo->ppg_fifo[i].signal_size + fifo->ppg_fifo[i].dark_size +fifo->ppg_fifo[i].lit_size) * (1 + fifo->ppg_fifo[i].ppg_chl2_en); - } - - fifo->sequence_size += 6 * fifo->bioz_slot; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_enable_internal_osc_960k(adi_adpd6000_device_t *device) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - - err = adi_adpd6000_hal_bf_write(device, BF_OSC_960K_EN_INFO, 1); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_ALT_CLOCKS_INFO, API_ADPD6000_SYS_CLOCK_INT_CLK); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_set_osc_trim(adi_adpd6000_device_t *device, uint16_t high_osc, uint16_t low_osc) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - - err = adi_adpd6000_hal_bf_write(device, BF_OSC_32M_FREQ_ADJ_INFO, high_osc); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_OSC_960K_FREQ_ADJ_INFO, low_osc); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_config_ext_clock(adi_adpd6000_device_t *device, adi_adpd6000_sys_clock_src_e src, uint8_t clk_pin) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_INVALID_PARAM_RETURN(src > API_ADPD6000_SYS_CLOCK_EXT_TM); - ADPD6000_INVALID_PARAM_RETURN(clk_pin > 2); - - err = adi_adpd6000_hal_bf_write(device, BF_ALT_CLOCKS_INFO, src); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_ALT_CLK_GPIO_INFO, clk_pin); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_set_slot_freq(adi_adpd6000_device_t *device, uint32_t sys_clk, uint32_t freq) -{ - int32_t err; - uint32_t data; - ADPD6000_NULL_POINTER_RETURN(device); - - data = sys_clk / freq; - err = adi_adpd6000_hal_bf_write(device, BF_TIMESLOT_PERIOD_L_INFO, data & 0xffff); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_TIMESLOT_PERIOD_H_INFO, (data >> 16 ) & 0x7f); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_enbale_sleep_mode(adi_adpd6000_device_t *device, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - - err = adi_adpd6000_hal_bf_write(device, BF_GO_SLEEP_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_device_cal_960k_osc(adi_adpd6000_device_t *device) -{ - int32_t err = API_ADPD6000_ERROR_OK; - uint16_t fuse = 0; - uint16_t timeout = 0x1000; - uint16_t data, fuse_code; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_device_enable_slot_operation_mode_go(device, false); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, 0x00000053, 0x00000104, 1); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_reg_write(device, 0x00000044, 7); - ADPD6000_ERROR_RETURN(err); - - while (timeout--) - { - err = adi_adpd6000_hal_bf_read(device, 0x000000d6, 0x00000201, &fuse); - ADPD6000_ERROR_RETURN(err); - if (fuse == 2) - { - break; - } - } - - if (fuse == 2) - { - err = adi_adpd6000_hal_reg_read(device, 0x000000c8, &data); - ADPD6000_ERROR_RETURN(err); - fuse_code = data << 8; - err = adi_adpd6000_hal_reg_read(device, 0x000000c9, &data); - ADPD6000_ERROR_RETURN(err); - fuse_code |= data; - err = adi_adpd6000_hal_bf_write(device, BF_OSC_960K_FREQ_ADJ_INFO, fuse_code); - ADPD6000_ERROR_RETURN(err); - } - else - { - err = API_ADPD6000_ERROR_FUSE_NOT_DONE; - } - - adi_adpd6000_hal_reg_write(device, 0x00000044, 0); - adi_adpd6000_hal_bf_write(device, 0x00000053, 0x00000104, 0); - - return err; -} -/*! @} */ diff --git a/src/adpd/lib/ADPD6000/adi_adpd6000_ecg.c b/src/adpd/lib/ADPD6000/adi_adpd6000_ecg.c deleted file mode 100644 index 0d62ab2..0000000 --- a/src/adpd/lib/ADPD6000/adi_adpd6000_ecg.c +++ /dev/null @@ -1,261 +0,0 @@ -/*! - * @brief ECG APIs Implementation - * @copyright Copyright (c) 2021 - Analog Devices Inc. All Rights Reserved. - */ - -/*! - * @addtogroup adi_adpd6000_sdk - * @{ - */ - -/*============= I N C L U D E S ============*/ -#include "adi_adpd6000.h" - -/*============= D E F I N E S ==============*/ - -/*============= D A T A ====================*/ - -/*============= C O D E ====================*/ -int32_t adi_adpd6000_ecg_enable_slot(adi_adpd6000_device_t *device, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_write(device, BF_ECG_TIMESLOT_EN_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ecg_set_input_mux(adi_adpd6000_device_t *device, bool input_en, bool short_en) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_write(device, BF_ECG_INP_CONNECT_INFO , input_en ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_ECG_SHORT_INFO , short_en ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ecg_set_odr(adi_adpd6000_device_t *device, adi_adpd6000_ecg_odr_e odr) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(odr > API_ADPD6000_ECG_ODR_4000); - - err = adi_adpd6000_hal_bf_write(device, BF_ECG_ODR_SEL_INFO , odr); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ecg_set_oversample(adi_adpd6000_device_t *device, uint8_t oversample) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_write(device, BF_ECG_OVERSAMPLING_RATIO_INFO , oversample); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ecg_enable_statusbyte(adi_adpd6000_device_t *device, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_write(device, BF_ENA_STAT_ECG_INFO , enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ecg_get_ecg_status(adi_adpd6000_device_t *device, uint8_t *status) -{ - int32_t err; - uint16_t data; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_NULL_POINTER_RETURN(status); - - err = adi_adpd6000_hal_reg_read(device, REG_ECG_STATUS_ADDR, &data); - ADPD6000_ERROR_RETURN(err); - *status = (uint8_t)data; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ecg_clr_ecg_status(adi_adpd6000_device_t *device, uint8_t status) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_reg_write(device, REG_ECG_STATUS_ADDR, status); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ecg_set_rld(adi_adpd6000_device_t *device, bool enable, adi_adpd6000_ecg_rld_output_e rld) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(rld > API_ADPD6000_ECG_RLD_OUTPUT_CM_INPUT); - - err = adi_adpd6000_hal_bf_write(device, BF_ECG_RLD_EN_INFO , enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_ECG_RLD_OUT_SEL_INFO , rld); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ecg_leadoff_set_rld(adi_adpd6000_device_t *device, bool enable, adi_adpd6000_ecg_dc_leadoff_th_e th) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(th > API_ADPD6000_ECG_DC_LEADOFF_400MV); - - err = adi_adpd6000_hal_bf_write(device, BF_ECG_RLD_SAT_EN_INFO , enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_ECG_RLD_SAT_THRESHOLD_INFO , th); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ecg_leadoff_set_ac(adi_adpd6000_device_t *device, bool enable, adi_adpd6000_ecg_ac_leadoff_mag_e mag, uint8_t th) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(mag > API_ADPD6000_ECG_AC_LEADOFF_70NA); - - err = adi_adpd6000_hal_bf_write(device, BF_ECG_ACLO_EN_INFO , enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_ECG_ACLO_MAG_INFO , mag); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_ECG_ACLO_THRESHOLD_INFO , th); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ecg_leadoff_set_dc(adi_adpd6000_device_t *device, bool enable, adi_adpd6000_ecg_dc_leadoff_mag_e mag, - adi_adpd6000_ecg_dc_leadoff_th_e th, adi_adpd6000_ecg_dc_leadoff_pol_e in_pol, adi_adpd6000_ecg_dc_leadoff_pol_e ip_pol) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(mag > API_ADPD6000_ECG_DC_LEADOFF_16NA); - - err = adi_adpd6000_hal_bf_write(device, BF_ECG_DCLO_EN_INFO , enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_ECG_DCLO_MAG_INFO , mag); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_ECG_DCLO_THRESHOLD_INFO , th); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_ECG_DCLO_POLARITY_IN_INFO , in_pol); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_ECG_DCLO_POLARITY_IP_INFO , ip_pol); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ecg_read_fifo(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, uint32_t *ecg_data, uint8_t *ecg_num) -{ - int32_t err; - uint8_t i, ecg_count = 0;; - uint8_t fifo_data[4]; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_NULL_POINTER_RETURN(fifo); - ADPD6000_NULL_POINTER_RETURN(ecg_data); - ADPD6000_NULL_POINTER_RETURN(ecg_num); - - if (fifo->ecg_slot) - { - for (i = 0; i < fifo->ecg_over_sample; i++) - { - err = adi_adpd6000_device_fifo_read_bytes(device, fifo_data, fifo->ecg_size); - ADPD6000_ERROR_RETURN(err); - if ((fifo_data[0] == 0xff) && (fifo->ecg_size == 4) && ((fifo->ppg_slot != 0) || (fifo->bioz_slot != 0))) - { - continue; - } - else - { - if (fifo->ecg_size == 4) - { - *ecg_data++ = (fifo_data[0] << 24) || (fifo_data[1] << 16) | (fifo_data[2] << 8) | (fifo_data[3]); - } - else - { - *ecg_data++ = ((fifo_data[0] << 16) | (fifo_data[1] << 8) | (fifo_data[2])); - } - ecg_count++; - } - } - } - *ecg_num = ecg_count; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ecg_read_data_status(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, uint32_t *ecg_data, uint8_t *status, uint8_t *ecg_num) -{ - int32_t err; - uint8_t i, ecg_count = 0;; - uint8_t fifo_data[4]; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_NULL_POINTER_RETURN(fifo); - ADPD6000_NULL_POINTER_RETURN(ecg_data); - ADPD6000_NULL_POINTER_RETURN(ecg_num); - - if (fifo->ecg_slot) - { - for (i = 0; i < fifo->ecg_over_sample; i++) - { - err = adi_adpd6000_device_fifo_read_bytes(device, fifo_data, fifo->ecg_size); - ADPD6000_ERROR_RETURN(err); - if ((fifo_data[0] == 0xff) && (fifo->ecg_size == 4) && ((fifo->ppg_slot != 0) || (fifo->bioz_slot != 0))) - { - continue; - } - else - { - if (fifo->ecg_size == 4) - { - *ecg_data++ = (fifo_data[1] << 16) | (fifo_data[2] << 8) | (fifo_data[3]); - if (status != NULL) - { - *status++ = fifo_data[0]; - } - } - else - { - *ecg_data++ = ((fifo_data[0] << 16) | (fifo_data[1] << 8) | (fifo_data[2])); - } - ecg_count++; - } - } - } - *ecg_num = ecg_count; - - return API_ADPD6000_ERROR_OK; -} -/*! @} */ diff --git a/src/adpd/lib/ADPD6000/adi_adpd6000_gpio.c b/src/adpd/lib/ADPD6000/adi_adpd6000_gpio.c deleted file mode 100644 index 7868abe..0000000 --- a/src/adpd/lib/ADPD6000/adi_adpd6000_gpio.c +++ /dev/null @@ -1,68 +0,0 @@ -/*! - * @brief BIOZ APIs Implementation - * @copyright Copyright (c) 2021 - Analog Devices Inc. All Rights Reserved. - */ - -/*! - * @addtogroup adi_adpd6000_sdk - * @{ - */ - -/*============= I N C L U D E S ============*/ -#include "adi_adpd6000.h" - -/*============= D E F I N E S ==============*/ - -/*============= C O D E ====================*/ -int32_t adi_adpd6000_gpio_set_mode(adi_adpd6000_device_t *device, uint8_t index, adi_adpd6000_gpio_mode_e mode) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_INVALID_PARAM_RETURN(index > 2); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_write(device, BF_GPIO_PIN_CFG0_INFO + 3 * index, mode); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_gpio_set_output(adi_adpd6000_device_t *device, uint8_t index, uint8_t level) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_INVALID_PARAM_RETURN(index > 2); - ADPD6000_LOG_FUNC(); - - if (index < 2) - { - err = adi_adpd6000_hal_bf_write(device, BF_GPIOOUT0_INFO + 8 * index, level); - ADPD6000_ERROR_RETURN(err); - } - else - { - err = adi_adpd6000_hal_bf_write(device, BF_GPIOOUT2_INFO, level); - ADPD6000_ERROR_RETURN(err); - } - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_gpio_get_input(adi_adpd6000_device_t *device, uint8_t index, uint8_t *level) -{ - int32_t err; - uint16_t data; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_NULL_POINTER_RETURN(level); - ADPD6000_INVALID_PARAM_RETURN(index > 2); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_read(device, BF_GPIO_INPUT_INFO, &data); - ADPD6000_ERROR_RETURN(err); - - *level = (data >> index) & 0x01; - - return API_ADPD6000_ERROR_OK; -} - -/*! @} */ diff --git a/src/adpd/lib/ADPD6000/adi_adpd6000_hal.c b/src/adpd/lib/ADPD6000/adi_adpd6000_hal.c deleted file mode 100644 index acc86d0..0000000 --- a/src/adpd/lib/ADPD6000/adi_adpd6000_hal.c +++ /dev/null @@ -1,203 +0,0 @@ -/*! - * @brief Hardare Abstract Layer Implementation - * @copyright Copyright (c) 2021 - Analog Devices Inc. All Rights Reserved. - */ - -/*! - * @addtogroup adi_adpd6000_sdk - * @{ - */ - -/*============= I N C L U D E S ============*/ -#include "adi_adpd6000.h" - -/*============= C O D E ====================*/ -int32_t adi_adpd6000_hal_reg_read(adi_adpd6000_device_t *device, uint32_t reg_addr, uint16_t *reg_data) -{ - int32_t err; - uint32_t address; - uint8_t wr_buf[ADPD6000_SDK_MAX_BUFSIZE] = {0}; - uint8_t rd_buf[ADPD6000_SDK_MAX_BUFSIZE] = {0}; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_NULL_POINTER_RETURN(reg_data); - - address = (reg_addr << 1); - wr_buf[0] = ((address >> 8) & 0xFF); /* address [15:08] */ - wr_buf[1] = ((address ) & 0xFF); /* address [07:00] */ - - err = device->read(device->user_data, rd_buf, 2, wr_buf, 2); - ADPD6000_ERROR_RETURN(err); - - *reg_data = rd_buf[1] + (rd_buf[0] << 8); - - ADPD6000_LOG_REG("r@%.8x = %.8x", reg_addr, *reg_data); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_hal_reg_write(adi_adpd6000_device_t *device, uint32_t reg_addr, uint16_t reg_data) -{ - int32_t err; - uint32_t address; - uint8_t wr_buf[ADPD6000_SDK_MAX_BUFSIZE] = {0}; - ADPD6000_NULL_POINTER_RETURN(device); - - address = (reg_addr << 1) + 1; - wr_buf[0] = ((address >> 8) & 0xFF); /* address [15:08] */ - wr_buf[1] = ((address ) & 0xFF); /* address [07:00] */ - wr_buf[2] = ((reg_data >> 8) & 0xFF); /* data [15:08] */ - wr_buf[3] = ((reg_data ) & 0xFF); /* data [07:00] */ - - err = device->write(device->user_data, wr_buf, 4); - ADPD6000_ERROR_RETURN(err); - ADPD6000_LOG_REG("w@%.8x = %.8x", reg_addr, reg_data); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_hal_bf_read(adi_adpd6000_device_t *device, uint32_t reg_addr, uint32_t bf_info, uint16_t *bf_val) -{ - uint16_t reg_value, reg_mask; - int32_t err; - uint8_t bit_start = bf_info; - uint8_t bit_count = bf_info >> 8; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_NULL_POINTER_RETURN(bf_val); - ADPD6000_INVALID_PARAM_RETURN((bit_count > 16) || (bit_count == 0)); - - err = adi_adpd6000_hal_reg_read(device, reg_addr, ®_value); - ADPD6000_ERROR_RETURN(err); - if((bit_count + bit_start) <= 16) - { - reg_mask = (bit_count == 16) ? 0xffff : ((1 << bit_count) - 1); - *bf_val = (reg_value >> bit_start) & reg_mask; - } - else - { - *bf_val = reg_value >> bit_start; - err = adi_adpd6000_hal_reg_read(device, reg_addr + 4, ®_value); - ADPD6000_ERROR_RETURN(err); - reg_mask = (1 << (bit_count - (16 - bit_start))) - 1; - *bf_val += (reg_value & reg_mask) << (16 - bit_start); - } - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_hal_bf_write(adi_adpd6000_device_t *device, uint32_t reg_addr, uint32_t bf_info, uint16_t bf_val) -{ - uint16_t reg_value, reg_mask; - int32_t err; - uint8_t bit_start = bf_info; - uint8_t bit_count = bf_info >> 8; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_INVALID_PARAM_RETURN((bit_count > 16) || (bit_count == 0)); - - if ((bit_count == 16) && (bit_start == 0)) - { - err = adi_adpd6000_hal_reg_write(device, reg_addr, bf_val); - ADPD6000_ERROR_RETURN(err); - } - else - { - err = adi_adpd6000_hal_reg_read(device, reg_addr, ®_value); - ADPD6000_ERROR_RETURN(err); - if((bit_count + bit_start) <= 16) - { - reg_mask = (bit_count == 16) ? 0xffff : ((1 << bit_count) - 1); - reg_value &= ~(reg_mask << bit_start); - reg_value |= (bf_val << bit_start); - err = adi_adpd6000_hal_reg_write(device, reg_addr, reg_value); - ADPD6000_ERROR_RETURN(err); - } - else - { - reg_mask = 0xffff; - reg_value &= ~(reg_mask << bit_start); - reg_value |= (bf_val << bit_start); - err = adi_adpd6000_hal_reg_write(device, reg_addr, reg_value); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_reg_read(device, reg_addr + 4, ®_value); - ADPD6000_ERROR_RETURN(err); - reg_mask = (1 << (bit_count - (16 - bit_start))) - 1; - reg_value &= ~reg_mask; - reg_value |= (bf_val >> (16 - bit_start)); - err = adi_adpd6000_hal_reg_write(device, reg_addr, reg_value); - ADPD6000_ERROR_RETURN(err); - } - } - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_hal_log_write(adi_adpd6000_device_t *device, uint32_t log_type, const char* comment, ...) -{ - #if ((ADPD6000_REPORT_VERBOSE & 0x0000ffff) > 0) && ((ADPD6000_REPORT_VERBOSE & 0xffff0000) > 0) - va_list argp; - char *msg_header = NULL; - static char logmsgbuf[100]; - if (device == NULL) - return API_ADPD6000_ERROR_NULL_PARAM; - if (comment == NULL) - return API_ADPD6000_ERROR_NULL_PARAM; - if ((device->log_write != NULL) && ((ADPD6000_REPORT_VERBOSE & log_type & 0x0000ffff) > 0) && ((ADPD6000_REPORT_VERBOSE & log_type & 0xffff0000) > 0)) - { - va_start(argp, comment); - if (((ADPD6000_REPORT_VERBOSE & ADPD6000_LOG_ERR_MSG) > 0) && ((log_type & ADPD6000_LOG_ERR_MSG) > 0)) - msg_header = "ERR "; - if (((ADPD6000_REPORT_VERBOSE & ADPD6000_LOG_WARN_MSG) > 0) && ((log_type & ADPD6000_LOG_WARN_MSG) > 0)) - msg_header = "WARN"; - if (((ADPD6000_REPORT_VERBOSE & ADPD6000_LOG_INFO_MSG) > 0) && ((log_type & ADPD6000_LOG_INFO_MSG) > 0)) - msg_header = "INFO"; - if (sprintf(logmsgbuf, "%s: ", msg_header) < 0) - return API_ADPD6000_ERROR_LOG_WRITE; - if (vsprintf(logmsgbuf + 6, comment, argp) < 0) - return API_ADPD6000_ERROR_LOG_WRITE; - if (API_ADPD6000_ERROR_OK != device->log_write(device->user_data, logmsgbuf)) - return API_ADPD6000_ERROR_LOG_WRITE; - va_end(argp); - } - #endif - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_hal_error_report(adi_adpd6000_device_t* device, uint32_t log_type, const char* file_name, const char* func_name, uint32_t line_num, const char* var_name, const char* comment) -{ - if (device == NULL) - return API_ADPD6000_ERROR_NULL_PARAM; - if (file_name == NULL) - return API_ADPD6000_ERROR_NULL_PARAM; - if (func_name == NULL) - return API_ADPD6000_ERROR_NULL_PARAM; - if (var_name == NULL) - return API_ADPD6000_ERROR_NULL_PARAM; - if (comment == NULL) - return API_ADPD6000_ERROR_NULL_PARAM; - - if (API_ADPD6000_ERROR_OK != adi_adpd6000_hal_log_write(device, log_type, "%s, \"%s\" in %s(...), line%d in %s", - comment, var_name, func_name, line_num, file_name)) - return API_ADPD6000_ERROR_LOG_WRITE; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_hal_fifo_read_bytes(adi_adpd6000_device_t *device, uint32_t reg_addr, uint8_t *reg_data, uint32_t len) -{ - int32_t err; - uint32_t address; - uint8_t wr_buf[ADPD6000_SDK_MAX_BUFSIZE] = {0}; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_NULL_POINTER_RETURN(reg_data); - - address = (reg_addr << 1); - wr_buf[0] = ((address >> 8) & 0xFF); /* address [15:08] */ - wr_buf[1] = ((address ) & 0xFF); /* address [07:00] */ - - err = device->read(device->user_data, reg_data, len, wr_buf, 2); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -/*! @} */ diff --git a/src/adpd/lib/ADPD6000/adi_adpd6000_ppg.c b/src/adpd/lib/ADPD6000/adi_adpd6000_ppg.c deleted file mode 100644 index fd0ecb0..0000000 --- a/src/adpd/lib/ADPD6000/adi_adpd6000_ppg.c +++ /dev/null @@ -1,892 +0,0 @@ -/*! - * @brief PPG APIs Implementation - * @copyright Copyright (c) 2021 - Analog Devices Inc. All Rights Reserved. - */ - -/*! - * @addtogroup adi_adpd6000_sdk - * @{ - */ - -/*============= I N C L U D E S ============*/ -#include "adi_adpd6000.h" -#include "math.h" - -/*============= D E F I N E S ==============*/ - -/*============= D A T A ====================*/ -uint16_t ppg_sample_count; -adi_adpd6000_ppg_agc_run_t ppg_agc_run[12]; - -/*============= C O D E ====================*/ -int32_t adi_adpd6000_ppg_set_slot_mode(adi_adpd6000_device_t *device, adi_adpd6000_ppg_slot_mode_e mode) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(mode > API_ADPD6000_PPG_SLOT_ABCDEFGHIJKL); - - err = adi_adpd6000_hal_bf_write(device, BF_PPG_TIMESLOT_EN_INFO, mode); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_get_slot_num(adi_adpd6000_device_t *device, uint8_t *slot_num) -{ - int32_t err; - uint16_t data; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_hal_bf_read(device, BF_PPG_TIMESLOT_EN_INFO, &data); - ADPD6000_ERROR_RETURN(err); - *slot_num = data; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_get_channel_num(adi_adpd6000_device_t *device, uint8_t *channel_num) -{ - int32_t err; - uint8_t i, slot_num, channel; - uint16_t chl2_en; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - err = adi_adpd6000_ppg_get_slot_num(device, &slot_num); - ADPD6000_ERROR_RETURN(err); - - channel = 0; - for (i = 0; i < slot_num; i++) - { - err = adi_adpd6000_hal_bf_read(device, ADPD6000_TIME_SLOT_SPAN * i + BF_CH2_EN_A_INFO, &chl2_en); - ADPD6000_ERROR_RETURN(err); - channel = (chl2_en == 0) ? channel + 1 : channel + 2; - } - *channel_num = channel; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_tia_set_input_res(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_tia_input_res_e res) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - ADPD6000_INVALID_PARAM_RETURN(res > API_ADPD6000_PPG_TIA_INPUT_RES_6K5); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_INPUT_R_SELECT_A_INFO, res); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_tia_set_gain_res(adi_adpd6000_device_t *device, uint8_t slot, uint8_t channel, adi_adpd6000_ppg_tia_gain_res_e gain) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - ADPD6000_INVALID_PARAM_RETURN(channel > 1); - ADPD6000_INVALID_PARAM_RETURN(gain > API_ADPD6000_PPG_TIA_GAIN_RES_12K5); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_TIA_GAIN_CH1_A_INFO + channel * 3, gain); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_tia_set_vref_value(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_tia_vref_value_e vref_value) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - ADPD6000_INVALID_PARAM_RETURN(vref_value > API_ADPD6000_PPG_TIA_VREF_1P265); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_AFE_TRIM_VREF_A_INFO, vref_value); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_tia_set_vref_pulse_alt_value(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_tia_vref_value_e alt_value) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - ADPD6000_INVALID_PARAM_RETURN(alt_value > API_ADPD6000_PPG_TIA_VREF_1P265); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_VREF_PULSE_VAL_A_INFO, alt_value); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_tia_enable_vref_pulse(adi_adpd6000_device_t *device, uint8_t slot, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_VREF_PULSE_A_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_enable_input_diff_mode(adi_adpd6000_device_t *device, uint8_t pair_index, bool diff_en) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(pair_index > 1); - - err = adi_adpd6000_hal_bf_write(device, BF_PAIR12_INFO + pair_index, diff_en ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_input_mux(adi_adpd6000_device_t *device, uint8_t slot, uint8_t pair_index, adi_adpd6000_ppg_input_mux_e mux) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(pair_index > 1); - ADPD6000_INVALID_PARAM_RETURN(mux > API_ADPD6000_PPG_INPUT_A2_B2); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_INP12_A_INFO + pair_index * 4, mux); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_sleep_input_mux(adi_adpd6000_device_t *device, uint8_t pair_index, adi_adpd6000_ppg_sleep_input_mux_e mux) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(pair_index > 1); - ADPD6000_INVALID_PARAM_RETURN(mux > API_ADPD6000_PPG_INPUT_SLEEP_ODD_FLOAT_EVEN_CATH2); - - err = adi_adpd6000_hal_bf_write(device, BF_INP_SLEEP_12_INFO + pair_index * 4, mux); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_cathode(adi_adpd6000_device_t *device, adi_adpd6000_ppg_cath_type_e cath1, adi_adpd6000_ppg_cath_type_e cath2) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(cath1 > API_ADPD6000_PPG_CATH_FLOAT); - ADPD6000_INVALID_PARAM_RETURN(cath2 > API_ADPD6000_PPG_CATH_FLOAT); - - err = adi_adpd6000_hal_bf_write(device, BF_VC1_SLEEP_INFO, cath1); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, BF_VC2_SLEEP_INFO, cath2); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_cfg_modulator(adi_adpd6000_device_t *device, uint8_t slot, - adi_adpd6000_ppg_mod_type_e type, uint8_t width, uint8_t offset) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - ADPD6000_INVALID_PARAM_RETURN(type > API_ADPD6000_PPG_MOD_TYPE_NONFLOAT); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_MOD_TYPE_A_INFO, type); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_MOD_WIDTH_A_INFO, width); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_MOD_OFFSET_A_INFO, offset); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_enable_amp(adi_adpd6000_device_t *device, uint8_t slot, uint8_t channel, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - ADPD6000_INVALID_PARAM_RETURN(channel > 1); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_CH1_AMP_DISABLE_A_INFO + channel * 4, enable ? 0 : 1); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_integ_set_gain(adi_adpd6000_device_t *device, uint8_t slot, uint8_t channel, adi_adpd6000_ppg_integ_gain_res_e gain) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - ADPD6000_INVALID_PARAM_RETURN(channel > 1); - ADPD6000_INVALID_PARAM_RETURN(gain > API_ADPD6000_PPG_INTEG_50K_GAIN_2); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_AFE_TRIM_INT1_A_INFO + channel * 2, gain); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_integ_select_cap(adi_adpd6000_device_t *device, uint8_t slot, uint8_t channel, adi_adpd6000_ppg_integ_cap_e cap) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - ADPD6000_INVALID_PARAM_RETURN(channel > 1); - ADPD6000_INVALID_PARAM_RETURN(cap > API_ADPD6000_PPG_INTEG_CAP_12P6); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_AFE_TRIM_INT1_CAP_A_INFO + channel, cap); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_integ_set_width(adi_adpd6000_device_t *device, uint8_t slot, uint8_t width) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_INTEG_WIDTH_A_INFO, width); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_integ_set_offset(adi_adpd6000_device_t *device, uint8_t slot, uint16_t offset) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_reg_write(device, ADPD6000_TIME_SLOT_SPAN * slot + REG_INTEG_OFFSET_A_ADDR, offset); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_integ_enable_single_clk(adi_adpd6000_device_t *device, uint8_t slot, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_SINGLE_INTEG_A_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_led_set_current(adi_adpd6000_device_t *device, uint8_t slot, uint8_t led_idx, uint8_t current) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_LED_CURRENT1_A_INFO + 8 * led_idx, current); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_led_set_channel(adi_adpd6000_device_t *device, uint8_t slot, uint8_t led_idx, adi_adpd6000_ppg_led_channel_e chl) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_LED_DRIVESIDE1_A_INFO + 8 * led_idx, chl); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_led_set_mode(adi_adpd6000_device_t *device, uint8_t slot, - uint8_t led_idx, adi_adpd6000_ppg_led_mode_e mode) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_LED_MODE1_A_INFO + led_idx, mode); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_led_set_width(adi_adpd6000_device_t *device, uint8_t slot, uint8_t width) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_LED_WIDTH_A_INFO, width); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_led_set_offset(adi_adpd6000_device_t *device, uint8_t slot, uint8_t offset, uint8_t sec_offset) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_LED_OFFSET_A_INFO, offset); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_LED_SECOND_OFFSET_A_INFO, sec_offset); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_led_set_count(adi_adpd6000_device_t *device, uint8_t slot, uint16_t num_int, uint16_t num_repeat) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_NUM_INT_A_INFO, num_int); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_NUM_REPEAT_A_INFO, num_repeat); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_adc_adjust(adi_adpd6000_device_t *device, uint8_t slot, uint16_t adjust1, uint16_t adjust2) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_CH1_ADC_ADJUST_A_INFO, adjust1); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_CH2_ADC_ADJUST_A_INFO, adjust2); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_minperiod(adi_adpd6000_device_t *device, uint8_t slot, uint16_t min_period) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_MIN_PERIOD_A_INFO, min_period); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_sel_precon(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_precon_e sel) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - ADPD6000_INVALID_PARAM_RETURN(sel > API_ADPD6000_PPG_PRECON_SHORT_DIFF_PAIR); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_PRECON_A_INFO, sel); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_sel_afe_path(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_afe_path_e sel) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - ADPD6000_INVALID_PARAM_RETURN(sel > API_ADPD6000_PPG_AFE_PATH_ADC); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_AFE_PATH_CFG_A_INFO, sel); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_config_vc(adi_adpd6000_device_t *device, uint8_t slot, uint8_t vc_index, - adi_adpd6000_ppg_vc_select_e vc_sel, adi_adpd6000_ppg_vc_select_e vc_alt, adi_adpd6000_ppg_vc_pulse_e vc_pulse) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - ADPD6000_INVALID_PARAM_RETURN(vc_index > 1); - ADPD6000_INVALID_PARAM_RETURN(vc_sel > API_ADPD6000_PPG_VC_GND); - ADPD6000_INVALID_PARAM_RETURN(vc_alt > API_ADPD6000_PPG_VC_GND); - ADPD6000_INVALID_PARAM_RETURN(vc_pulse > API_ADPD6000_PPG_VC_PULSE_FLOATING); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_VC1_SEL_A_INFO + 6 * vc_index, vc_sel); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_VC1_ALT_A_INFO + 6 * vc_index, vc_alt); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_VC1_PULSE_A_INFO + 6 * vc_index, vc_pulse); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_enable_channe2(adi_adpd6000_device_t *device, uint8_t slot, bool enable) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_CH2_EN_A_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_decimate(adi_adpd6000_device_t *device, uint8_t slot, bool enable, uint8_t decimate) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_SUBSAMPLE_A_INFO, enable ? 1 : 0); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_DECIMATE_FACTOR_A_INFO, decimate); - ADPD6000_ERROR_RETURN(err); - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_ambientdac(adi_adpd6000_device_t *device, uint8_t slot, uint8_t channel, uint16_t current) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + channel + BF_DAC_AMBIENT_CH1_A_INFO, current); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_dcdac(adi_adpd6000_device_t *device, uint8_t slot, uint8_t channel, uint8_t current) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + channel + BF_DAC_LED_DC_CH1_A_INFO, current); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_data_size(adi_adpd6000_device_t *device, uint8_t slot, uint8_t signal_size, uint8_t lit_size, uint8_t dark_size) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_SIGNAL_SIZE_A_INFO, signal_size); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_LIT_SIZE_A_INFO, lit_size); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_DARK_SIZE_A_INFO, dark_size); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_window_offset(adi_adpd6000_device_t *device, uint8_t slot, uint8_t signal_offset, uint8_t lit_offset, uint8_t dark_offset) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_SIGNAL_SHIFT_A_INFO, signal_offset); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_LIT_SHIFT_A_INFO, lit_offset); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_DARK_SHIFT_A_INFO, dark_offset); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_alctype(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_alc_type_e alc_type) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_AMBIENT_CANCELLATION_A_INFO, alc_type); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_alc_loop_width(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_alc_loop_width_e alc_loop_width) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(slot >= 12); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_COARSE_LOOP_WIDTH_A_INFO, alc_loop_width); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_set_sample_type(adi_adpd6000_device_t *device, uint8_t slot, adi_adpd6000_ppg_sample_type_e type) -{ - int32_t err; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_INVALID_PARAM_RETURN(type > API_ADPD6000_PPG_SAMPLE_TYPE_IMPULSE); - - err = adi_adpd6000_hal_bf_write(device, ADPD6000_TIME_SLOT_SPAN * slot + BF_SAMPLE_TYPE_A_INFO, type); - ADPD6000_ERROR_RETURN(err); - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_read_fifo(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, uint32_t *signal_data, uint32_t *dark_data, uint32_t *lit_data, uint8_t *ppg_num) -{ - int32_t err; - uint8_t i, j, k, ppg_count = 0; - uint8_t fifo_data[4]; - uint32_t data; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - for (i = 0; i < fifo->ppg_slot; i++) - { - for (j = 0; j <= fifo->ppg_fifo[i].ppg_chl2_en; j++) - { - ppg_count++; - if (fifo->ppg_fifo[i].signal_size > 0) - { - err = adi_adpd6000_device_fifo_read_bytes(device, fifo_data, fifo->ppg_fifo[i].signal_size); - ADPD6000_ERROR_RETURN(err); - if (signal_data != NULL) - { - data = 0; - for (k = 0; k < fifo->ppg_fifo[i].signal_size; k++) - { - data += (fifo_data[k] << ((fifo->ppg_fifo[i].signal_size - 1 - k) * 8)); - } - *signal_data++ = data; - } - } - if (fifo->ppg_fifo[i].dark_size > 0) - { - err = adi_adpd6000_device_fifo_read_bytes(device, fifo_data, fifo->ppg_fifo[i].dark_size); - ADPD6000_ERROR_RETURN(err); - if (dark_data != NULL) - { - data = 0; - for (k = 0; k < fifo->ppg_fifo[i].dark_size; k++) - { - data += (fifo_data[k] << ((fifo->ppg_fifo[i].dark_size - 1 - k) * 8)); - } - *dark_data++ = data; - } - } - if (fifo->ppg_fifo[i].lit_size > 0) - { - err = adi_adpd6000_device_fifo_read_bytes(device, fifo_data, fifo->ppg_fifo[i].lit_size); - ADPD6000_ERROR_RETURN(err); - if (lit_data != NULL) - { - data = 0; - for (k = 0; k < fifo->ppg_fifo[i].lit_size; k++) - { - data += (fifo_data[k] << ((fifo->ppg_fifo[i].lit_size - 1 - k) * 8)); - } - *lit_data++ = data; - } - } - } - } - *ppg_num = ppg_count; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_read_struct_fifo(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, adi_adpd6000_ppg_slot_data_t *signal_data, adi_adpd6000_ppg_slot_data_t *dark_data, adi_adpd6000_ppg_slot_data_t *lit_data, uint8_t *slot_num) -{ - int32_t err; - uint8_t i, j, k; - uint8_t fifo_data[4]; - uint32_t data; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - - for (i = 0; i < fifo->ppg_slot; i++) - { - for (j = 0; j <= fifo->ppg_fifo[i].ppg_chl2_en; j++) - { - if (fifo->ppg_fifo[i].signal_size > 0) - { - err = adi_adpd6000_device_fifo_read_bytes(device, fifo_data, fifo->ppg_fifo[i].signal_size); - ADPD6000_ERROR_RETURN(err); - if (signal_data != NULL) - { - data = 0; - for (k = 0; k < fifo->ppg_fifo[i].signal_size; k++) - { - data += (fifo_data[k] << ((fifo->ppg_fifo[i].signal_size - 1 - k) * 8)); - } - signal_data[i].chnl[j] = data; - } - } - if (fifo->ppg_fifo[i].dark_size > 0) - { - err = adi_adpd6000_device_fifo_read_bytes(device, fifo_data, fifo->ppg_fifo[i].dark_size); - ADPD6000_ERROR_RETURN(err); - if (dark_data != NULL) - { - data = 0; - for (k = 0; k < fifo->ppg_fifo[i].dark_size; k++) - { - data += (fifo_data[k] << ((fifo->ppg_fifo[i].dark_size - 1 - k) * 8)); - } - dark_data[i].chnl[j] = data; - } - } - if (fifo->ppg_fifo[i].lit_size > 0) - { - err = adi_adpd6000_device_fifo_read_bytes(device, fifo_data, fifo->ppg_fifo[i].lit_size); - ADPD6000_ERROR_RETURN(err); - if (lit_data != NULL) - { - data = 0; - for (k = 0; k < fifo->ppg_fifo[i].lit_size; k++) - { - data += (fifo_data[k] << ((fifo->ppg_fifo[i].lit_size - 1 - k) * 8)); - } - lit_data[i].chnl[j] = data; - } - } - } - } - *slot_num = fifo->ppg_slot; - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_agc_init(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, adi_adpd6000_ppg_agc_cfg_t *ppg_agc_cfg) -{ - int32_t err; - uint8_t i; - uint16_t data; - ppg_sample_count = 0; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_NULL_POINTER_RETURN(fifo); - ADPD6000_NULL_POINTER_RETURN(ppg_agc_cfg); - ADPD6000_INVALID_PARAM_RETURN(ppg_agc_cfg->ppg_average_sample_number == 0); - - for (i = 0; i < fifo->ppg_slot; i++) - { - ppg_agc_run[i].ppg_data_sum = 0; - ppg_agc_run[i].agc_done = 0; - - err = adi_adpd6000_hal_bf_read(device, ADPD6000_TIME_SLOT_SPAN * i + BF_NUM_INT_A_INFO, &data); - ADPD6000_ERROR_RETURN(err); - ppg_agc_run[i].ppg_full_scale = 16383 * data; - err = adi_adpd6000_hal_bf_read(device, ADPD6000_TIME_SLOT_SPAN * i + BF_NUM_REPEAT_A_INFO, &data); - ADPD6000_ERROR_RETURN(err); - ppg_agc_run[i].ppg_full_scale = ppg_agc_run[i].ppg_full_scale * data; - - err = adi_adpd6000_hal_bf_read(device, ADPD6000_TIME_SLOT_SPAN * i + BF_TIA_GAIN_CH1_A_INFO, &data); - ADPD6000_ERROR_RETURN(err); - ppg_agc_run[i].tia_gain = data; - err = adi_adpd6000_hal_bf_read(device, ADPD6000_TIME_SLOT_SPAN * i + BF_LED_CURRENT1_A_INFO + 8 * ppg_agc_cfg->slot[i].led_chnl, &data); - ADPD6000_ERROR_RETURN(err); - ppg_agc_run[i].led_current = data; - } - - return API_ADPD6000_ERROR_OK; -} - -int32_t adi_adpd6000_ppg_agc_process(adi_adpd6000_device_t *device, adi_adpd6000_fifo_config_t *fifo, adi_adpd6000_ppg_agc_cfg_t *ppg_agc_cfg, uint32_t *signal_data) -{ - int32_t err; - uint8_t i; - uint32_t data; - float step; - float ppg_data_avg, ppg_chnl_current; - ADPD6000_NULL_POINTER_RETURN(device); - ADPD6000_LOG_FUNC(); - ADPD6000_NULL_POINTER_RETURN(fifo); - ADPD6000_NULL_POINTER_RETURN(ppg_agc_cfg); - ADPD6000_NULL_POINTER_RETURN(signal_data); - - ppg_sample_count++; - if (ppg_sample_count <= ppg_agc_cfg->ppg_skip_sample_number) - { - return API_ADPD6000_ERROR_OK; - } - if (ppg_sample_count <= (ppg_agc_cfg->ppg_skip_sample_number + ppg_agc_cfg->ppg_average_sample_number)) - { - for (i = 0; i < fifo->ppg_slot; i++) - { - if (fifo->ppg_fifo[i].ppg_chl2_en == 0) - { - ppg_agc_run[i].ppg_data_sum += *signal_data++; - } - else - { - if (ppg_agc_cfg->slot[i].tia_chnl == 0) - { - ppg_agc_run[i].ppg_data_sum += *signal_data++; - data = *signal_data++; - } - else if (ppg_agc_cfg->slot[i].tia_chnl == 1) - { - data = *signal_data++; - ppg_agc_run[i].ppg_data_sum += *signal_data++; - } - } - } - data = data; - return API_ADPD6000_ERROR_OK; - } - - for (i = 0; i < fifo->ppg_slot; i++) - { - if ((ppg_agc_cfg->slot[i].agc_en == 0) || (ppg_agc_run[i].agc_done == 1)) - { - continue; - } - ppg_data_avg = ppg_agc_run[i].ppg_data_sum / ppg_agc_cfg->ppg_average_sample_number; - ppg_data_avg ++; - ppg_chnl_current = (float)(ppg_agc_run[i].led_current * ppg_agc_run[i].ppg_full_scale * 0.5 / ppg_data_avg); - if (ppg_chnl_current > 127) - { - if(ppg_agc_run[i].tia_gain > 0) - { - ppg_agc_run[i].tia_gain = ppg_agc_run[i].tia_gain - 1; - ppg_chnl_current = ppg_agc_run[i].led_current; - } - else - { - ppg_chnl_current = ppg_agc_run[i].led_current + 0x10; - ppg_chnl_current = (ppg_chnl_current > 127) ? 127 : ppg_chnl_current; - } - } - else if (ppg_chnl_current < 1) - { - if(ppg_agc_run[i].tia_gain < 5) - { - ppg_agc_run[i].tia_gain = ppg_agc_run[i].tia_gain + 1; - ppg_chnl_current = ppg_agc_run[i].led_current; - } - else - { - ppg_chnl_current = (ppg_agc_run[i].led_current > 0x11) ? (ppg_agc_run[i].led_current - 0x10) : 1; - } - } - - if (ppg_agc_cfg->power_first_en == 0) /* low tia gain */ - { - if (fabs(ppg_agc_run[i].led_current - (uint8_t)ppg_chnl_current) > 1) - { - ppg_agc_run[i].led_current = (uint8_t)ppg_chnl_current; - } - else - { - if (ppg_agc_cfg->slot[i].agc_type == 1) - { - ppg_agc_run[i].agc_done = 1; - } - } - - if ((ppg_agc_run[i].led_current < 0x20) && (ppg_agc_run[i].tia_gain < 5)) - { - ppg_agc_run[i].led_current = ppg_agc_run[i].led_current * 2; - ppg_agc_run[i].tia_gain = ppg_agc_run[i].tia_gain + 1; - } - } - else /* low LED current */ - { - step = fabs(ppg_chnl_current - ppg_agc_run[i].led_current) / 4; - if (step < 1) - { - step = 1; - } - if (ppg_agc_run[i].led_current > ((uint8_t)ppg_chnl_current + 1)) - { - ppg_agc_run[i].led_current -= (uint8_t)step; - } - else if (ppg_agc_run[i].led_current < ((uint8_t)ppg_chnl_current - 1)) - { - ppg_agc_run[i].led_current += (uint8_t)step; - } - if ((ppg_agc_run[i].led_current > 0x10) && (ppg_agc_run[i].tia_gain > 2)) - { - ppg_agc_run[i].led_current = ppg_agc_run[i].led_current / 2; - ppg_agc_run[i].tia_gain = ppg_agc_run[i].tia_gain - 1; - } - } - } - - adi_adpd6000_device_enable_slot_operation_mode_go(device, false); - for (i = 0; i < fifo->ppg_slot; i++) - { - if (ppg_agc_cfg->slot[i].agc_en == 1) - { - err = adi_adpd6000_ppg_tia_set_gain_res(device, i, ppg_agc_cfg->slot[i].tia_chnl, (adi_adpd6000_ppg_tia_gain_res_e)ppg_agc_run[i].tia_gain); - ADPD6000_ERROR_RETURN(err); - err = adi_adpd6000_ppg_led_set_current(device, i, ppg_agc_cfg->slot[i].led_chnl, ppg_agc_run[i].led_current); - ADPD6000_ERROR_RETURN(err); - } - } - adi_adpd6000_device_clr_fifo(device); - adi_adpd6000_device_enable_slot_operation_mode_go(device, true); - - ppg_sample_count = 0; - for (i = 0; i < fifo->ppg_slot; i++) - { - ppg_agc_run[i].ppg_data_sum = 0;; - } - - return err; -} -/*! @} */ diff --git a/src/adpd/u_adpd6100.cpp b/src/adpd/u_adpd6100.cpp deleted file mode 100644 index fc71c0e..0000000 --- a/src/adpd/u_adpd6100.cpp +++ /dev/null @@ -1,1281 +0,0 @@ -#include "u_adpd6100.h" -int32_t spi_read(void* user_data, uint8_t *rd_buf, uint32_t rd_len, uint8_t *wr_buf, uint32_t wr_len); -int32_t spi_write(void* user_data, uint8_t *wr_buf, uint32_t len); -int32_t log_print(void* user_data, char *string); - -static const char* TAG = "ADPD"; - - -ADPD6::ADPD6(void){ - ADPD6::adpd1_spi_handle= NULL; // ESP32 Spi device handle - - ADPD6::adpd_spi_dev = { // spi configuration - .command_bits = 0, - .address_bits = 16, - .dummy_bits = 0, - .mode = 3, - .clock_speed_hz = SPI_MASTER_FREQ_10M, - .spics_io_num = ADPD1_CS, - .queue_size=1 - }; - // handle for spi IO - ADPD6::handle = { - .user_data = &adpd1_spi_handle, - .read = *spi_read, - .write = *spi_write, - .log_write = *log_print - }; - - ADPD6::load_default(&(ADPD6::sys_config)); - ADPD6::load_default(&(ADPD6::led_config)); - ADPD6::load_default(&(ADPD6::SNR_config)); - ADPD6::load_default(&(ADPD6::DI_config)); - ADPD6::load_default(&(ADPD6::AI_config)); - ADPD6::load_default(&(ADPD6::signal_config)); -}; - -ADPD6::~ADPD6(void){ - -}; - - -bool ADPD6::begin(void){ - esp_err_t err = 1; - if (_spi_device_attached){ - spi_bus_remove_device(ADPD6::adpd1_spi_handle); - ADPD6::_spi_device_attached = false; - ADPD6::chip_check = false; - ESP_LOGI(TAG, "ADPD reset"); - } - - ESP_LOGV(TAG, "ADPD Initate:"); - - err = spi_bus_add_device(SPI2_HOST, &(ADPD6::adpd_spi_dev), &(ADPD6::adpd1_spi_handle)); - if (err != ESP_OK){ - ESP_LOGE(TAG, "SPI Bus error: %s", esp_err_to_name(err)); - return false; - } - ESP_LOGI(TAG, "ADPD attached as SPI device"); - - ADPD6::_spi_device_attached = true; - uint8_t ret1 = 0; - uint8_t ret2 = 0; - adi_adpd6000_device_get_id(&(ADPD6::handle), &ret1, &ret2); - - if (ret1 == 196) { - ADPD6::chip_check = true; - Serial.printf("ADPD Found, chip version: %d\n", ret2); - err = adi_adpd6000_device_sw_reset(&(ADPD6::handle)); - ADPD6000_ERROR_RETURN(err); - ADPD6::load_default(&(ADPD6::sys_config)); - ADPD6::global_setup(&(ADPD6::sys_config)); - return true; - }else{ - ADPD6::chip_check = false; - ESP_LOGE(TAG, "ADPD not found"); - return false; - } - return false; - -} - -int32_t ADPD6::clear_fifo(void){ - return adi_adpd6000_device_clr_fifo(&(ADPD6::handle)); -} - -int32_t ADPD6::run_freq(uint32_t freq){ - return adi_adpd6000_device_set_slot_freq(&(ADPD6::handle), 960000, freq); -} - -int32_t ADPD6::RUN(){ - return adi_adpd6000_device_enable_slot_operation_mode_go(&(ADPD6::handle), true); -} - -int32_t ADPD6::STOP(){ - return adi_adpd6000_device_enable_slot_operation_mode_go(&(ADPD6::handle), false); -} - -uint16_t ADPD6::fifo_count(){ - uint16_t ret = 0; - adi_adpd6000_device_get_fifo_count(&(ADPD6::handle), &ret); - if (ret < 641) return ret; - return 0; -} - -int32_t ADPD6::readfifo(uint16_t num_samples, uint8_t width, uint32_t* data){ - uint16_t i, j; - uint8_t readout[width]; - for (i = 0; i < num_samples; i++){ - data[i] = 0; - adi_adpd6000_device_fifo_read_bytes(&(ADPD6::handle), readout, width); - for (j = 0; j < width; j++){ - data[i] += readout[j] << ((width - j - 1) * 8); - } - } - return 0; -} - -int32_t ADPD6::write_reg(uint32_t reg_addr, uint16_t *reg_data){ - return adi_adpd6000_hal_reg_write(&(ADPD6::handle), reg_addr, *reg_data); -} - -int32_t ADPD6::read_reg(uint32_t reg_addr){ - uint16_t reg_data = 0; - adi_adpd6000_hal_reg_read(&(ADPD6::handle), reg_addr, ®_data); - return reg_data; -} - - -int32_t ADPD6::num_ts(uint8_t mode){ - return adi_adpd6000_ppg_set_slot_mode(&(ADPD6::handle), (adi_adpd6000_ppg_slot_mode_e)mode); -} - - -void ADPD6::load_default(struct ts_led *ts){ - ts->driver1_current = 0; - ts->driver2_current = 0; - ts->led1_channel = LED_A; - ts->led2_channel = LED_A; - ts->led1_mode = 0; - ts->led2_mode = 0; -} - -int32_t ADPD6::ts_setup(adi_adpd6000_slot_e timeslot_no, struct ts_led *init) -{ - int32_t ret; - uint16_t data = 0; - - /* Set LED power and A/B channels*/ - data = init->led2_channel << 15| - init->driver2_current << 8| - init->led1_channel << 7| - init->driver1_current << 0; - - ret = ADPD6::write_reg(REG_LED_POW12_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - /* Set LED power and A/B channels*/ - data = init->led2_mode << 1| - init->led1_mode << 0; - - ret = ADPD6::write_reg(REG_LED_MODE_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - return API_ADPD6000_ERROR_OK; -} - - -void ADPD6::load_default(struct ts_SNR *ts){ - ts->ch1_dac = 0; - ts->ch1_led_offset = 0; - ts->ch2_dac = 0; - ts->ch2_led_offset = 0; - ts->TIA_ceiling_det = 0; - ts->Ch2_R_int = 0; - ts->Ch1_R_int = 0; - ts->Vref_pulse = 0; - ts->Vref_VPD = 2; - ts->Vref_VPD_pulse = 3; - ts->TIA_gain_CH2 = 1; - ts->TIA_gain_CH1 = 1; - ts->C_int_CH2 = 0; - ts->C_int_CH1 = 0; -} - - -int32_t ADPD6::ts_setup(adi_adpd6000_slot_e timeslot_no, struct ts_SNR *init) -{ - int32_t ret; - uint16_t data = 0; - - /* Set TIA AC Dac for channel 1*/ - data = init->ch1_dac << 7| - init->ch1_led_offset << 0; - - ret = ADPD6::write_reg(REG_AFE_DAC1_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - /* Set TIA AC Dac for channel 1*/ - data = init->ch2_dac << 7| - init->ch2_led_offset << 0; - - ret = ADPD6::write_reg(REG_AFE_DAC2_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - - data = init->TIA_ceiling_det << 15| - init->Ch2_R_int << 13| - init->Ch1_R_int << 11| - init->Vref_pulse << 10| - init->Vref_VPD << 8| - init->Vref_pulse << 6| - init->TIA_gain_CH2 << 3| - init->TIA_gain_CH1 << 0; - - ret = ADPD6::write_reg(REG_AFE_TRIM1_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->C_int_CH2 << 13| - init->C_int_CH1 << 12; - - ret = ADPD6::write_reg(REG_AFE_TRIM2_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - return API_ADPD6000_ERROR_OK; -} - -void ADPD6::load_default(struct ts_signal *ts){ - ts->pre_condition_width = 4; - ts->ac_type = 0; - ts->gpio_out = 0; - ts->INT2BUT = 0; - ts->AFE_PATh = 0x28; // 0x28 - ts->IN34 = 0; - ts->IN12 = 1; - ts->pre_condition_type = 0; - ts->VC2_pulse_type = 0; - ts->VC2_pulse_to = 0; - ts->VC2_active = 2; - ts->VC1_pulse_type = 0; - ts->VC1_pulse_to = 0; - ts->VC1_active = 2; // set vc1 to vref + 215mV -} - - -int32_t ADPD6::ts_setup(adi_adpd6000_slot_e timeslot_no, struct ts_signal *init) -{ - int32_t ret; - uint16_t data = 0; - - /* Set TIA AC Dac for channel 1*/ - data = init->pre_condition_width << 12| - init->ac_type << 10| - init->gpio_out << 9| - init->INT2BUT << 8| - init->AFE_PATh << 0; - - ret = ADPD6::write_reg(REG_TS_PATH_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - /* Set TIA AC Dac for channel 1*/ - data = init->IN34 << 4| - init->IN12 << 0; - - ret = ADPD6::write_reg(REG_INPUTS_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - - data = init->pre_condition_type << 12| - init->VC2_pulse_type << 10| - init->VC2_pulse_to << 8| - init->VC2_active << 6| - init->VC1_pulse_type << 4| - init->VC1_pulse_to << 2| - init->VC1_active << 0; - - ret = ADPD6::write_reg(REG_CATHODE_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - return API_ADPD6000_ERROR_OK; -} - -void ADPD6::load_default(struct ts_DI_timing *ts){ - ts->num_integration = 1; - ts->num_repeats = 1; - ts->Modulation_type = 0; - ts->period_min = 0; - ts->LED_pulse_width = 2; - ts->LED_pulse_offset = 0x10; - ts->LED_pulse2_offset = 0x13; - ts->LIT_OFFSET = 0x26; - ts->DARK_OFFSET1 = 0x06; - ts->DARK_OFFSET2 = 0x40; - ts->subsample_en = 0; - ts->ch2_en = 0; - ts->sample_type = 3; // two-region digital integration - ts->ts_offset = 0; - ts->dark_shift = 0; - ts->dark_size = 0; - ts->signal_shift = 0; - ts->signal_size = 3; - ts->lit_shift = 0; - ts->lit_size = 0; -} - - -int32_t ADPD6::ts_setup(adi_adpd6000_slot_e timeslot_no, struct ts_DI_timing *init){ - int32_t ret; - uint16_t data = 0; - - - data = init->num_integration << 8| - init->num_repeats << 0; - ret = ADPD6::write_reg(REG_COUNTS_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = init->Modulation_type << 12| - init->period_min << 0; - ret = ADPD6::write_reg(REG_PERIOD_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->LED_pulse_width << 8| - init->LED_pulse_offset << 0; - ret = ADPD6::write_reg(REG_LED_PULSE1_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - data = 0; - data = init->LED_pulse2_offset << 0; - ret = ADPD6::write_reg(REG_LED_PULSE2_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->LIT_OFFSET << 0; - ret = ADPD6::write_reg(REG_DIGINT_LIT_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->DARK_OFFSET2 << 7| - init->DARK_OFFSET1 << 0; - ret = ADPD6::write_reg(REG_DIGINT_DARK_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->subsample_en << 15| - init->ch2_en << 14| - init->sample_type << 11| - init->ts_offset << 0; - ret = ADPD6::write_reg(REG_TS_CTRL_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->dark_shift << 11| - init->dark_size << 8| - init->signal_shift << 3| - init->signal_size << 0; - ret = ADPD6::write_reg(REG_DATA1_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->lit_shift << 3| - init->lit_size << 0; - ret = ADPD6::write_reg(REG_DATA2_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; -} - -void ADPD6::load_default(struct ts_AI_timing *ts){ - ts->num_integration = 1; - ts->num_repeats = 1; - ts->Modulation_type = 0; - ts->period_min = 0; - ts->LED_pulse_width = 2; - ts->LED_pulse_offset = 0x10; - ts->LED_pulse2_offset = 0x13; - ts->SINGLE_INTEG = 0; - ts->CH2_DIS_AMP = 0; - ts->CH1_DIS_AMP = 0; - ts->ADC_count = 0; - ts->integration_width = 3; - ts->offset_us = 0xD; - ts->offset_ns = 0; - ts->pattern_LED_disable = 0; - ts->pattern_MOD_disable = 0; - ts->pattern_subtract = 0; - ts->pattern_reverse_int = 0; - ts->subsample_en = 0; - ts->ch2_en = 0; - ts->sample_type = 0; - ts->ts_offset = 0; - -} - -int32_t ADPD6::ts_setup(adi_adpd6000_slot_e timeslot_no, struct ts_AI_timing *init){ - int32_t ret; - uint16_t data = 0; - - data = init->Modulation_type << 12| - init->period_min << 0; - ret = ADPD6::write_reg(REG_PERIOD_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->LED_pulse_width << 8| - init->LED_pulse_offset << 0; - ret = ADPD6::write_reg(REG_LED_PULSE1_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - data = 0; - data = init->LED_pulse2_offset << 0; - ret = ADPD6::write_reg(REG_LED_PULSE2_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - //0x012E INTEG_WIDTH_A - data = 0; - data = init->SINGLE_INTEG << 15| - init->CH2_DIS_AMP << 11| - init->CH1_DIS_AMP << 7| - init->ADC_count << 5| - init->integration_width << 0; - ret = ADPD6::write_reg(REG_INTEG_WIDTH_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - //0x012F INTEG_OFFSET_A - data = 0; - data = init->offset_us << 5| - init->offset_ns << 0; - ret = ADPD6::write_reg(REG_INTEG_OFFSET_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - //0x130 MOD_PULSE_A - data = 0; - data = init->modulation_width << 8| - init->modulation_offset << 0; - ret = ADPD6::write_reg(REG_MOD_PULSE_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - //0x0131 PATTERN1_A - data = 0; - data = init->pattern_LED_disable << 12| - init->pattern_MOD_disable << 8| - init->pattern_subtract << 4| - init->pattern_reverse_int << 0; - ret = ADPD6::write_reg(REG_PATTERN1_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - - //0x0120 TS_CTRL_A - data = 0; - data = init->subsample_en << 15| - init->ch2_en << 14| - init->sample_type << 11| - init->ts_offset << 0; - ret = ADPD6::write_reg(REG_TS_CTRL_A_ADDR + (timeslot_no) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; -} - -void ADPD6::load_default(struct system_config *ts){ - ts->IN34_sleep = 0; - ts->IN12_sleep = 0; - ts->VC2_sleep = 0; - ts->VC1_sleep = 0; - ts->PAIR34 = 0; - ts->PAIR12 = 0; - ts->GPIO_slew = 0; - ts->GPIO_drv = 0; - ts->GPIO2_cfg = 0; - ts->GPIO1_cfg = 0; - ts->GPIO0_cfg = 0; - ts->GPIO1_output = 0; - ts->GPIO0_output = 0; - ts->GPIO2_output = 0; - ts->IOVDD = 0; - ts->SPI_drv = 0; - ts->SPI_slew = 0; - ts->alt_clock = 0; - ts->alt_clock_gpio = 0; - ts->ext_clock_freq = 0; - ts->internal_clock_en = 1; -} - - -int32_t ADPD6::global_setup(struct system_config *init){ - int32_t ret; - uint16_t data = 0; - - data = 0x2004; - ret = ADPD6::write_reg(0x0046, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0x400b; - ret = ADPD6::write_reg(0x004c, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->IN34_sleep << 4| - init->IN12_sleep << 0; - ret = ADPD6::write_reg(REG_INPUT_SLEEP_ADDR, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - - data = 0; - data = init->VC2_sleep << 6| - init->VC1_sleep << 4| - init->PAIR34 << 1| - init->PAIR12 << 0; - ret = ADPD6::write_reg(REG_INPUT_CFG_ADDR, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->GPIO_slew << 14| - init->GPIO_drv << 12| - init->GPIO2_cfg << 6| - init->GPIO1_cfg << 3| - init->GPIO0_cfg << 0; - ret = ADPD6::write_reg(REG_GPIO_CFG_ADDR, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - - data = 0; - data = init->GPIO1_output << 8| - init->GPIO0_output << 0; - ret = ADPD6::write_reg(REG_GPIO01_ADDR, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->GPIO2_output << 0; - ret = ADPD6::write_reg(REG_GPIO23_ADDR, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->IOVDD << 6| - 1 << 4| - init->SPI_slew << 2| - init->SPI_drv << 0; - ret = ADPD6::write_reg(REG_IO_ADJUST_ADDR, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; - - data = 0; - data = init->alt_clock << 8| - init->alt_clock_gpio << 6| - init->ext_clock_freq << 2| - init->internal_clock_en << 1; - ret = ADPD6::write_reg(REG_SYS_CTL_ADDR, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; -} - -void ADPD6::load_default(struct GPIO0_config *ts){ - ts->GPIO0_cfg = 0; - ts->GPIO0_output = 0; - ts->EXT_SYNC_EN = 0; - ts->SYNC_GPIO = 0; -} - -int32_t ADPD6::gpio_setup(struct GPIO0_config *init){ - int32_t ret; - uint16_t data = 0; - data = init->EXT_SYNC_EN << 2| - init->SYNC_GPIO << 0; - ret = ADPD6::write_reg(REG_GPIO_EXT_ADDR, &data); - if(ret != API_ADPD6000_ERROR_OK) return ret; - - data = 0; - data = init->GPIO0_output << 0; - ret = ADPD6::write_reg(REG_GPIO01_ADDR, &data); - if(ret != API_ADPD6000_ERROR_OK) return ret; - - data = 0; - data = init->GPIO0_cfg; - ret = ADPD6::write_reg(REG_GPIO_CFG_ADDR, &data); - if(ret != API_ADPD6000_ERROR_OK) return ret; - - return 0; -} - - - - - - -int32_t ADPD6::preset_config_1(uint8_t ts, uint8_t num_integ){ - if (!(ADPD6::chip_check)){ - ESP_LOGE(TAG, "ADPD Not init in preset1"); - return -2; - } - - ESP_LOGV(TAG, "Preset_config 1 set for timeslot:%d. Two ambient channels. Total 2 x 3 bytes", ts); - // Channel 1: PD1 (sun-facing), channel 2: PD2 (leaf-facing IR) - - ADPD6::STOP(); - ADPD6::DI_config.period_min = 58; - ADPD6::DI_config.LIT_OFFSET = 64; - ADPD6::DI_config.DARK_OFFSET1 = 48; - ADPD6::DI_config.DARK_OFFSET2 = 90; - ADPD6::DI_config.LED_pulse_offset = 60; - ADPD6::DI_config.LED_pulse_width = 19; - ADPD6::DI_config.sample_type = 3; - ADPD6::DI_config.signal_size = 0; - ADPD6::DI_config.num_integration = num_integ; - ADPD6::DI_config.num_repeats = 1; - ADPD6::DI_config.dark_size = 3; - ADPD6::DI_config.lit_size = 0; - - ADPD6::signal_config.pre_condition_type = 5; - ADPD6::SNR_config.Ch1_R_int = 1; - ADPD6::SNR_config.C_int_CH1 = 1; - ADPD6::SNR_config.Ch2_R_int = 1; - ADPD6::SNR_config.C_int_CH2 = 1; - ADPD6::signal_config.INT2BUT = 1; - ADPD6::signal_config.ac_type = 0; - - ADPD6::DI_config.ch2_en = true; - ADPD6::signal_config.IN12 = 0B0101; - ADPD6::signal_config.IN34 = 0B0000; - - ADPD6::num_ts(ts + 1); - - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::DI_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::signal_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::SNR_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::led_config)); - - return 0; - -} - - -// fluo and ref channel setup -int32_t ADPD6::preset_config_2(uint8_t ts, uint8_t num_integ){ - if (!(ADPD6::chip_check)){ - ESP_LOGE(TAG, "ADPD Not init in preset 2"); - return -2; - } - - ESP_LOGV(TAG, "Preset_config 2 set for timeslot:%d. Total 2 x 2 x 3 bytes", ts); - // Channel 1: PD2 (leaf-facing IR), channel 2: PD4 (leaf-facing Vis) - - ADPD6::STOP(); - ADPD6::DI_config.period_min = 58; - ADPD6::DI_config.LIT_OFFSET = 72;//64; - ADPD6::DI_config.DARK_OFFSET1 = 48; - ADPD6::DI_config.DARK_OFFSET2 = 90; - ADPD6::DI_config.LED_pulse_offset = 60; - ADPD6::DI_config.LED_pulse_width = 15;//19; - ADPD6::DI_config.sample_type = 3; - ADPD6::DI_config.signal_size = 0; - ADPD6::DI_config.num_integration = num_integ; - ADPD6::DI_config.num_repeats = 1; - ADPD6::DI_config.dark_size = 3; - ADPD6::DI_config.lit_size = 3; - - ADPD6::signal_config.pre_condition_type = 5; - ADPD6::SNR_config.Ch1_R_int = 1; - ADPD6::SNR_config.C_int_CH1 = 1; - ADPD6::SNR_config.Ch2_R_int = 2; // 1 - ADPD6::SNR_config.C_int_CH2 = 1; - ADPD6::signal_config.INT2BUT = 1; - ADPD6::signal_config.ac_type = 2; - - ADPD6::DI_config.ch2_en = true; - ADPD6::signal_config.IN12 = 0B0011; - ADPD6::signal_config.IN34 = 0B0100; - - - - ADPD6::num_ts(ts + 1); - - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::DI_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::signal_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::SNR_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::led_config)); - - return 0; -} - -int32_t ADPD6::preset_config_2x(uint8_t ts, uint8_t num_integ, uint8_t lit_offset, uint8_t dark1_offset, uint8_t dark2_offset, uint8_t pulse_offset, uint8_t pulse_duration){ - if (!(ADPD6::chip_check)){ - ESP_LOGE(TAG, "ADPD Not init in preset 2"); - return -2; - } - - ESP_LOGV(TAG, "Preset_config 2 set for timeslot:%d. Total 2 x 2 x 3 bytes", ts); - // Channel 1: PD2 (leaf-facing IR), channel 2: PD4 (leaf-facing Vis) - - ADPD6::STOP(); - ADPD6::DI_config.period_min = 58; - ADPD6::DI_config.LIT_OFFSET = lit_offset; - ADPD6::DI_config.DARK_OFFSET1 = dark1_offset; - ADPD6::DI_config.DARK_OFFSET2 = dark2_offset; - ADPD6::DI_config.LED_pulse_offset = pulse_offset; - ADPD6::DI_config.LED_pulse_width = pulse_duration; - ADPD6::DI_config.sample_type = 3; - ADPD6::DI_config.signal_size = 0; - ADPD6::DI_config.num_integration = num_integ; - ADPD6::DI_config.num_repeats = 1; - ADPD6::DI_config.dark_size = 3; - ADPD6::DI_config.lit_size = 3; - - ADPD6::signal_config.pre_condition_type = 5; - ADPD6::SNR_config.Ch1_R_int = 1; - ADPD6::SNR_config.C_int_CH1 = 1; - ADPD6::SNR_config.Ch2_R_int = 1; - ADPD6::SNR_config.C_int_CH2 = 1; - ADPD6::signal_config.INT2BUT = 1; - ADPD6::signal_config.ac_type = 2; - - ADPD6::DI_config.ch2_en = true; - ADPD6::signal_config.IN12 = 0B0011; - ADPD6::signal_config.IN34 = 0B0100; - - - - ADPD6::num_ts(ts + 1); - - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::DI_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::signal_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::SNR_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::led_config)); - - return 0; -} - -// reflection channel setup -int32_t ADPD6::preset_config_3(uint8_t ts, uint8_t num_integ){ - if (!(ADPD6::chip_check)){ - ESP_LOGE(TAG, "ADPD Not init in preset 2"); - return -2; - } - - ESP_LOGV(TAG, "Preset_config 3 set for timeslot:%d. Total 2 x 3 bytes", ts); - // Channel 1: PD2 (leaf-facing IR), channel 2: PD4 (leaf-facing Vis) - - ADPD6::STOP(); - ADPD6::DI_config.period_min = 58; - ADPD6::DI_config.LIT_OFFSET = 72;//64; - ADPD6::DI_config.DARK_OFFSET1 = 48; - ADPD6::DI_config.DARK_OFFSET2 = 90; - ADPD6::DI_config.LED_pulse_offset = 60; - ADPD6::DI_config.LED_pulse_width = 19; - ADPD6::DI_config.sample_type = 3; - ADPD6::DI_config.signal_size = 3; - ADPD6::DI_config.num_integration = num_integ; - ADPD6::DI_config.num_repeats = 1; - ADPD6::DI_config.dark_size = 0; - ADPD6::DI_config.lit_size = 0; - - ADPD6::signal_config.pre_condition_type = 5; - ADPD6::SNR_config.Ch1_R_int = 1; - ADPD6::SNR_config.C_int_CH1 = 1; - ADPD6::SNR_config.Ch2_R_int = 1; - ADPD6::SNR_config.C_int_CH2 = 1; - ADPD6::signal_config.INT2BUT = 1; - ADPD6::signal_config.ac_type = 2; - - ADPD6::DI_config.ch2_en = true; - ADPD6::signal_config.IN12 = 0B0011; - ADPD6::signal_config.IN34 = 0B0100; - - - ADPD6::num_ts(ts + 1); - - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::DI_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::signal_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::SNR_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::led_config)); - - return 0; -} - - - -// far-red -int32_t ADPD6::preset_config_4(uint8_t ts){ - if (!(ADPD6::chip_check)){ - ESP_LOGE(TAG, "ADPD Not init in preset 3"); - return -2; - } - - ESP_LOGV(TAG, "Preset_config 4 set for timeslot:%d. Total 0 bytes", ts); - // Channel 1: PD2 (leaf-facing IR), channel 2: PD4 (leaf-facing Vis) - - ADPD6::STOP(); - ADPD6::DI_config.period_min = 320; - ADPD6::DI_config.LIT_OFFSET = 100; - ADPD6::DI_config.DARK_OFFSET1 = 48; - ADPD6::DI_config.DARK_OFFSET2 = 300; - ADPD6::DI_config.LED_pulse_offset = 60; - ADPD6::DI_config.LED_pulse_width = 200; - ADPD6::DI_config.sample_type = 3; - ADPD6::DI_config.signal_size = 0; - ADPD6::DI_config.num_integration = 1; - ADPD6::DI_config.num_repeats = 1; - ADPD6::DI_config.dark_size = 0; - ADPD6::DI_config.lit_size = 0; - - ADPD6::signal_config.pre_condition_type = 5; - ADPD6::SNR_config.Ch1_R_int = 1; - ADPD6::SNR_config.C_int_CH1 = 1; - ADPD6::SNR_config.Ch2_R_int = 1; - ADPD6::SNR_config.C_int_CH2 = 1; - ADPD6::signal_config.INT2BUT = 1; - ADPD6::signal_config.ac_type = 0; - - ADPD6::DI_config.ch2_en = false; - ADPD6::signal_config.IN12 = 0B0011; - ADPD6::signal_config.IN34 = 0B0100; - - - - ADPD6::num_ts(ts + 1); - - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::DI_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::signal_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::SNR_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::led_config)); - - return 0; -} - -int32_t ADPD6::repeats_only(uint8_t ts, uint16_t num_integration, uint8_t num_repeats){ - uint16_t data = 0; - int32_t ret; - data = num_integration << 8| - num_repeats << 0; - ret = ADPD6::write_reg(REG_COUNTS_A_ADDR + (ts) * 0x20, &data); - if(ret != API_ADPD6000_ERROR_OK) - return ret; -} - -// external trigger -int32_t ADPD6::preset_config_ext_fast(uint8_t ts){ - return ADPD6::preset_config_ext_fast(ts, 1); - -} - -int32_t ADPD6::preset_config_ext_fast(uint8_t ts, uint8_t integ){ - if (!(ADPD6::chip_check)){ - ESP_LOGE(TAG, "ADPD Not init in preset 2"); - return -2; - } - - ESP_LOGV(TAG, "External trigger test: %d", ts); - // Channel 1: PD2 (leaf-facing IR), channel 2: PD4 (leaf-facing Vis) - - ADPD6::STOP(); - ADPD6::DI_config.period_min = 50; - ADPD6::DI_config.LIT_OFFSET = 70; - ADPD6::DI_config.DARK_OFFSET1 = 48; - ADPD6::DI_config.DARK_OFFSET2 = 85; - ADPD6::DI_config.LED_pulse_offset = 60; - ADPD6::DI_config.LED_pulse_width = 15; - ADPD6::DI_config.sample_type = 3; - ADPD6::DI_config.signal_size = 0; - ADPD6::DI_config.num_integration = integ; - ADPD6::DI_config.num_repeats = 1; - ADPD6::DI_config.dark_size = 3; - ADPD6::DI_config.lit_size = 3; - - ADPD6::signal_config.pre_condition_type = 5; - ADPD6::SNR_config.Ch1_R_int = 1; - ADPD6::SNR_config.C_int_CH1 = 1; - ADPD6::SNR_config.Ch2_R_int = 2; - ADPD6::SNR_config.C_int_CH2 = 1; - ADPD6::signal_config.INT2BUT = 1; - ADPD6::signal_config.ac_type = 2; - - ADPD6::DI_config.ch2_en = true; - ADPD6::signal_config.IN12 = 0B0011; - ADPD6::signal_config.IN34 = 0B0100; - - - - ADPD6::num_ts(ts + 1); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::DI_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::signal_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::SNR_config)); - ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::led_config)); - - return 0; -} - - - - - -// int32_t ADPD6::current_sweep(uint8_t led, uint8_t PD_IN, uint8_t gain, uint32_t *data){ -// if (!(ADPD6::chip_check)){ -// Serial.println("adpd not init1"); -// return -2; -// } - -// if (data == NULL) return -1; -// for (uint8_t i = 0; i < 60; i++) data[i] = 0; - -// ADPD6::STOP(); -// ADPD6::DI_config.period_min = 58; -// ADPD6::DI_config.LIT_OFFSET = 64; -// ADPD6::DI_config.DARK_OFFSET1 = 48; -// ADPD6::DI_config.DARK_OFFSET2 = 90; -// ADPD6::DI_config.LED_pulse_offset = 60; -// ADPD6::DI_config.LED_pulse_width = 19; -// ADPD6::DI_config.sample_type = 3; -// ADPD6::DI_config.num_integration = 1; - -// if ((led == 1)|(led == 3)){ -// ADPD6::led_config.led1_channel = LED_A; -// ADPD6::led_config.led2_channel = LED_A; -// } -// if ((led == 2)|(led == 4)){ -// ADPD6::led_config.led1_channel = LED_B; -// ADPD6::led_config.led2_channel = LED_B; -// } - -// ADPD6::signal_config.IN12 = 0; -// ADPD6::signal_config.IN34 = 0; -// switch (PD_IN) -// { -// case 1: -// ADPD6::signal_config.IN12 = 1; -// break; -// case 2: -// ADPD6::signal_config.IN12 = 3; -// break; -// case 3: -// ADPD6::signal_config.IN34 = 1; -// break; -// case 4: -// ADPD6::signal_config.IN34 = 3; -// break; -// default: -// break; -// } - -// ADPD6::signal_config.pre_condition_type = 5; -// ADPD6::SNR_config.Ch1_R_int = 3; -// ADPD6::SNR_config.C_int_CH1 = 1; -// ADPD6::signal_config.INT2BUT = 1; -// ADPD6::signal_config.ac_type = 2; - -// ADPD6::SNR_config.TIA_gain_CH1 = gain; -// ADPD6::led_config.driver1_current = 0; -// ADPD6::led_config.driver2_current = 0; - - -// uint8_t* p_I_led_driver = &(ADPD6::led_config.driver1_current); -// *p_I_led_driver = 0; -// if (led > 2) p_I_led_driver = &(ADPD6::led_config.driver2_current); -// ADPD6::num_ts(12); - -// for (uint8_t i = 0; i < 12; i++){ -// ADPD6::ts_setup((adi_adpd6000_slot_e)i, &(ADPD6::DI_config)); -// ADPD6::ts_setup((adi_adpd6000_slot_e)i, &(ADPD6::signal_config)); -// ADPD6::ts_setup((adi_adpd6000_slot_e)i, &(ADPD6::SNR_config)); -// } - -// ADPD6::run_freq(500); -// uint32_t ret[12]; -// uint8_t cycle_counter = 0; - -// for (uint8_t c = 0; c < 5; c++){ - -// for (uint8_t i = 0; i < 12; i++){ -// *p_I_led_driver = i * 10 + c * 2; -// ADPD6::ts_setup((adi_adpd6000_slot_e)i, &(ADPD6::led_config)); -// } - -// ADPD6::clear_fifo(); -// ADPD6::RUN(); -// while (ADPD6::fifo_count() < 12 * 3 * 4){ -// delay(10); -// } -// ADPD6::STOP(); - - -// for (uint8_t n = 0; n < 4; n++){ -// ADPD6::readfifo(12, 3, ret); -// for (uint8_t i = 0; i < 12; i++){ -// data[i * 5 + c] += ret[i]; -// } -// } -// } - -// for (uint8_t i = 0; i < 60; i++){ -// data[i] = (data[i] >> 2); -// } -// return 0; -// } - - -// int32_t ADPD6::augogain(uint8_t led, uint8_t PD_IN, uint8_t mode, struct light_gain_setting * output){ -// uint32_t trace[60]; -// uint8_t idx = 0; - -// for (int8_t g = 5; g > -1; g--){ -// ADPD6::current_sweep(led, PD_IN, g, trace); -// idx = 59; -// for (uint8_t i = 0; i < 60; i++){ -// //Serial.println(trace[i]); -// if (trace[i] > 8000){ -// idx = i; -// break; -// } -// } -// if (idx < 10) break; - -// if ( (idx < 30) || ((idx < 59) && (mode == 0)) ){ // Led saturated with half the current or low gain high current mode -// // Serial.print(g); -// // Serial.print(idx); - -// output->current = idx * 2; -// output->gain = g; -// return 0 ; -// } -// output->gain = g; -// } -// output->current = idx * 2; -// return 0; -// } - -// int32_t ADPD6::augogain(uint8_t led, uint8_t PD_IN, uint8_t mode){ -// struct light_gain_setting output; -// ADPD6::augogain(led, PD_IN, mode, &output); -// Serial.println(output.current); -// Serial.println(output.gain); -// return 0; -// } - -// int32_t ADPD6::led_input_preset(struct light_gain_setting* output){ -// if (output == NULL) return -1; -// if (!(ADPD6::chip_check)){ -// Serial.println("adpd not init2"); -// return -2; -// } -// ADPD6::STOP(); - -// ADPD6::led_config.led1_channel = (driverside) output->led_side; -// ADPD6::led_config.led2_channel = (driverside)output->led_side; - -// ADPD6::led_config.driver1_current = 0; -// ADPD6::led_config.driver2_current = 0; -// if (output->led_driver == 1) ADPD6::led_config.driver1_current = output->current; -// if (output->led_driver == 2) ADPD6::led_config.driver2_current = output->current; - -// ADPD6::signal_config.IN12 = 0; -// ADPD6::signal_config.IN34 = 0; - -// if (output->PD == 1) ADPD6::signal_config.IN12 = 1; -// if (output->PD == 2) ADPD6::signal_config.IN12 = 3; -// if (output->PD == 3) ADPD6::signal_config.IN34 = 1; -// if (output->PD == 4) ADPD6::signal_config.IN34 = 3; - -// ADPD6::SNR_config.TIA_gain_CH1 = output->gain; -// return 0; -// } - -// int32_t ADPD6::DI_FluoRef(uint8_t ts, uint8_t num_integ){ -// if (!(ADPD6::chip_check)){ -// Serial.println("adpd not init3"); -// return -2; -// } - -// ADPD6::STOP(); -// ADPD6::DI_config.period_min = 58; -// ADPD6::DI_config.LIT_OFFSET = 64; -// ADPD6::DI_config.DARK_OFFSET1 = 48; -// ADPD6::DI_config.DARK_OFFSET2 = 90; -// ADPD6::DI_config.LED_pulse_offset = 60; -// ADPD6::DI_config.LED_pulse_width = 19; -// ADPD6::DI_config.sample_type = 3; -// ADPD6::DI_config.signal_size = 3; -// ADPD6::DI_config.num_integration = num_integ; -// ADPD6::DI_config.lit_size = 0; -// ADPD6::DI_config.dark_size = 0; - -// ADPD6::signal_config.pre_condition_type = 5; -// ADPD6::SNR_config.Ch1_R_int = 3; -// ADPD6::SNR_config.C_int_CH1 = 1; -// ADPD6::SNR_config.Ch2_R_int = 3; -// ADPD6::SNR_config.C_int_CH2 = 1; -// ADPD6::signal_config.INT2BUT = 1; -// ADPD6::signal_config.ac_type = 2; - -// ADPD6::DI_config.ch2_en = true; -// ADPD6::signal_config.IN12 = 0B0011; -// ADPD6::signal_config.IN34 = 0B0100; - -// ADPD6::num_ts(ts + 1); - -// ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::DI_config)); -// ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::signal_config)); -// ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::SNR_config)); -// ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::led_config)); - -// return 0; - -// } - -// int32_t ADPD6::DI_ambient(uint8_t ts, uint8_t num_integ){ -// if (!(ADPD6::chip_check)){ -// Serial.println("adpd not init3"); -// return -2; -// } - -// ADPD6::STOP(); -// ADPD6::DI_config.period_min = 58; -// ADPD6::DI_config.LIT_OFFSET = 64; -// ADPD6::DI_config.DARK_OFFSET1 = 48; -// ADPD6::DI_config.DARK_OFFSET2 = 90; -// ADPD6::DI_config.LED_pulse_offset = 60; -// ADPD6::DI_config.LED_pulse_width = 19; -// ADPD6::DI_config.sample_type = 3; -// ADPD6::DI_config.num_integration = num_integ; -// ADPD6::DI_config.dark_size = 3; -// ADPD6::DI_config.lit_size = 0; -// ADPD6::DI_config.signal_size = 0; - -// ADPD6::signal_config.pre_condition_type = 5; -// ADPD6::SNR_config.Ch1_R_int = 3; -// ADPD6::SNR_config.C_int_CH1 = 1; -// ADPD6::SNR_config.Ch2_R_int = 3; -// ADPD6::SNR_config.C_int_CH2 = 1; -// ADPD6::signal_config.INT2BUT = 1; -// ADPD6::signal_config.ac_type = 0; - -// ADPD6::DI_config.ch2_en = false; -// ADPD6::signal_config.IN12 = 0B0001; -// ADPD6::signal_config.IN34 = 0B0000; - -// ADPD6::num_ts(ts + 1); - -// ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::DI_config)); -// ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::signal_config)); -// ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::SNR_config)); -// ADPD6::ts_setup((adi_adpd6000_slot_e)ts, &(ADPD6::led_config)); - -// return 0; - -// } - - - - - -// int32_t ADPD6::current_sweep2(uint8_t led, uint8_t PD_IN, uint8_t gain, uint8_t ac, uint32_t *data){ -// if (!(ADPD6::chip_check)){ -// Serial.println("adpd not init1"); -// return -2; -// } - -// if (data == NULL) return -1; -// for (uint8_t i = 0; i < 60; i++) data[i] = 0; - -// ADPD6::STOP(); -// ADPD6::DI_config.period_min = 58; -// ADPD6::DI_config.LIT_OFFSET = 64; -// ADPD6::DI_config.DARK_OFFSET1 = 48; -// ADPD6::DI_config.DARK_OFFSET2 = 90; -// ADPD6::DI_config.LED_pulse_offset = 60; -// ADPD6::DI_config.LED_pulse_width = 19; -// ADPD6::DI_config.sample_type = 3; -// ADPD6::DI_config.num_integration = 1; - -// if ((led == 1)|(led == 3)){ -// ADPD6::led_config.led1_channel = LED_A; -// ADPD6::led_config.led2_channel = LED_A; -// } -// if ((led == 2)|(led == 4)){ -// ADPD6::led_config.led1_channel = LED_B; -// ADPD6::led_config.led2_channel = LED_B; -// } - -// ADPD6::signal_config.IN12 = 0; -// ADPD6::signal_config.IN34 = 0; -// switch (PD_IN) -// { -// case 1: -// ADPD6::signal_config.IN12 = 1; -// break; -// case 2: -// ADPD6::signal_config.IN12 = 3; -// break; -// case 3: -// ADPD6::signal_config.IN34 = 1; -// break; -// case 4: -// ADPD6::signal_config.IN34 = 3; -// break; -// default: -// break; -// } - -// ADPD6::signal_config.pre_condition_type = 5; -// ADPD6::SNR_config.Ch1_R_int = 3; -// ADPD6::SNR_config.C_int_CH1 = 1; -// ADPD6::signal_config.INT2BUT = 1; -// if (ac < 3) ADPD6::signal_config.ac_type = ac; -// if (ac > 3){ -// ADPD6::signal_config.ac_type = 3; -// ADPD6::SNR_config.ch1_dac = ac * 10; -// } - - -// ADPD6::SNR_config.TIA_gain_CH1 = gain; -// ADPD6::led_config.driver1_current = 0; -// ADPD6::led_config.driver2_current = 0; - - -// uint8_t* p_I_led_driver = &(ADPD6::led_config.driver1_current); -// *p_I_led_driver = 0; -// if (led > 2) p_I_led_driver = &(ADPD6::led_config.driver2_current); -// ADPD6::num_ts(12); - -// for (uint8_t i = 0; i < 12; i++){ -// ADPD6::ts_setup((adi_adpd6000_slot_e)i, &(ADPD6::DI_config)); -// ADPD6::ts_setup((adi_adpd6000_slot_e)i, &(ADPD6::signal_config)); -// ADPD6::ts_setup((adi_adpd6000_slot_e)i, &(ADPD6::SNR_config)); -// } - -// ADPD6::run_freq(500); -// uint32_t ret[12]; -// uint8_t cycle_counter = 0; - -// for (uint8_t c = 0; c < 5; c++){ - -// for (uint8_t i = 0; i < 12; i++){ -// *p_I_led_driver = i * 10 + c * 2; -// ADPD6::ts_setup((adi_adpd6000_slot_e)i, &(ADPD6::led_config)); -// } - -// ADPD6::clear_fifo(); -// ADPD6::RUN(); -// while (ADPD6::fifo_count() < 12 * 3 * 4){ -// delay(10); -// } -// ADPD6::STOP(); -// //Serial.println(ADPD6::read_reg(REG_AFE_DAC1_A_ADDR)); - - - -// for (uint8_t n = 0; n < 4; n++){ -// ADPD6::readfifo(12, 3, ret); -// for (uint8_t i = 0; i < 12; i++){ -// data[i * 5 + c] += ret[i]; -// } -// } -// } - -// for (uint8_t i = 0; i < 60; i++){ -// data[i] = (data[i] >> 2); -// } -// return 0; -// } \ No newline at end of file diff --git a/ambit-1.ino b/src/ambit-1.ino similarity index 54% rename from ambit-1.ino rename to src/ambit-1.ino index d42d3b3..287ba39 100644 --- a/ambit-1.ino +++ b/src/ambit-1.ino @@ -5,13 +5,15 @@ #include "src/mlx90632/u_mlx.h" #include "serial.h" #include "do_command.h" -#include "src/wrench.h" #include "config.h" #include "driver/uart.h" #include "driver/gpio.h" #include #include "Esp.h" #include "nvs1.h" +#include "openjii_proto.h" + +void frontend_json_register(); // src/frontend_json.cpp static const char* TAG = "INO"; ADPD6 adpd; @@ -75,6 +77,11 @@ void setup(){ Serial.println(esp_timer_get_time()); Serial.setTimeout(50); + /* The binary FSM protocol shares UART0 with the ESP-IDF log console + * (Serial -> UART0). Silence all logging so it can't interleave ASCII into + * the binary stream sent to the ambyte over the FFC. */ + esp_log_level_set("*", ESP_LOG_NONE); + digitalWrite(STF_FLASH_PIN, HIGH); delayMicroseconds(1); @@ -99,8 +106,7 @@ void setup(){ load_info_from_nvs(true); - - Serial.write(AMBIT_BOOT_IDLE); + frontend_json_register(); esp_sleep_enable_timer_wakeup(10000000); @@ -114,33 +120,60 @@ int do_esp_cmd(); int c = -1; char choose[50]; +/* Sticky host latch: UNKNOWN is the app-discovery default, so it stays awake + * and quiet until traffic identifies a host. Only the ambyte's 0xAA wake + * preamble positively latches BINARY and opts into the frozen light-sleep / + * 0x85 idle-heartbeat contract; an arbitrary high byte must not do that. + * TEXT also stays awake because the openJII app / Calibratron / a JSON host do + * not retry and UART sleep-wake (threshold 3 edges) eats in-flight bytes. Its + * persistent last-activity timestamp releases the latch after a long idle so + * a device can later be installed on an ambyte without a reboot. */ +enum HostLatch : uint8_t { HOST_UNKNOWN = 0, HOST_BINARY, HOST_TEXT }; +static HostLatch host_latch = HOST_UNKNOWN; +static uint32_t host_last_activity_ms = 0; +static const uint32_t TEXT_LATCH_RELEASE_MS = 120000; + void loop(){ + // An in-flight JSON envelope owns the stream until it completes or times + // out; poll() is what fires its 1 s idle timeout, so keep calling it. + if (ojii::busy()){ + if (Serial.available() > 0) host_last_activity_ms = millis(); + ojii::poll(Serial); + if (ojii::busy()){ delay(1); return; } + host_last_activity_ms = millis(); + } + c = -1; int b; - unsigned int sleep_timer = millis(); - + for (;;) { c = Serial.available(); if (c > 0){ - sleep_timer = millis(); + host_last_activity_ms = millis(); c = Serial.peek(); if (c == 255) Serial.read(); if (c < 255) break; }else{ - if (millis() - sleep_timer > sleep_threshod_ms){ + if (host_latch == HOST_TEXT){ + if (millis() - host_last_activity_ms > TEXT_LATCH_RELEASE_MS){ + host_latch = HOST_UNKNOWN; + } + delay(10); + }else if (host_latch == HOST_BINARY && + millis() - host_last_activity_ms > sleep_threshod_ms){ Serial.flush(); flush_serial(20); ambit_light_sleep(); c = esp_sleep_get_wakeup_cause(); - sleep_timer = millis(); + host_last_activity_ms = millis(); if (c == 8){ sleep_threshod_ms = 1000; }else{ sleep_threshod_ms = 200; - } - + } + Serial.write(AMBIT_BOOT_IDLE); Serial.flush(); @@ -155,13 +188,20 @@ void loop(){ } } - + + /* First-byte router. Every frozen binary framing byte (0x80 0x85 0xA0 0xA1 + * 0xAA 0xB4 0xD2-0xD4 0xDE 0xF0) is >127; both text dialects are printable + * ASCII, and only the JSON envelope starts with '{' or '['. ojii::poll() + * must never see binary traffic (it consumes bytes and silently drops >127 + * junk while unlocked), hence peek-then-route. */ c = Serial.peek(); - if (c > 127) { // not from computer + if (c > 127) { // binary FSM to the ambyte (frozen wire) + ojii::reset(); // a stale partial line must not prepend to later text while (Serial.available() > 0){ b = serial_read_until(170, 160, 222, 50, false); if (b == 1){ // wake up signal + host_latch = HOST_BINARY; flush_serial(5); Serial.write(128); sleep_threshod_ms = 500; @@ -179,13 +219,29 @@ void loop(){ break; } } - + host_last_activity_ms = millis(); + + }else if (c == '{' || c == '['){ // openJII JSON envelope + host_latch = HOST_TEXT; + ojii::poll(Serial); + host_last_activity_ms = millis(); }else{ - sleep_threshod_ms = 30000; + // Text console: what the openJII app driver and the Calibratron speak. + // The old `sleep_threshod_ms = 30000` stay-awake heuristic is + // superseded by the TEXT latch. + host_latch = HOST_TEXT; + /* Claim the sink for this host before dispatching. do_esp_cmd() latches + * CONNECTION_TYPE=AMBYTE on every binary command and nothing used to + * clear it, so a text command that does not set its own sink (plain + * `arrun`; arrun1/arrun2/q/r/w all do) would run with the ambyte sink + * still selected and emit FSM wake traffic at a text host. Verbs that + * set their own sink still override this. */ + CONNECTION_TYPE = CONNECTION_TYPES::COMPUTER; Serial_Input_Chars(choose, ":,", 200, sizeof(choose) - 1); do_command(choose); - + host_last_activity_ms = millis(); + } -} \ No newline at end of file +} diff --git a/src/as7341/spec_meas.h b/src/as7341/spec_meas.h deleted file mode 100644 index 92341a2..0000000 --- a/src/as7341/spec_meas.h +++ /dev/null @@ -1,43 +0,0 @@ -#include "Adafruit_AS7341.h" -#include "../pin_config.h" - -#define PAR_COE1 -0.515 -#define PAR_COE2 0.801 -#define PAR_COE3 -0.385 -#define PAR_COE4 -0.202 -#define PAR_COE5 0.301 -#define PAR_COE6 0.183 -#define PAR_COE7 0.055 -#define PAR_COE8 0.081 -#define PAR_COE9 -0.033 - -#define Spec_COE1 12 -#define Spec_COE2 10 -#define Spec_COE3 11 -#define Spec_COE4 10 -#define Spec_COE5 10 -#define Spec_COE6 9 -#define Spec_COE7 7 -#define Spec_COE8 4 -#define Spec_COE9 1 - -#define PAR_OFFSET 4 - -extern Adafruit_AS7341 as7341; - -extern bool as7341_setup; -bool check_AS7341(); -void AS_LED_Current(uint16_t current); -void AS_LED_ON(); -void AS_LED_OFF(); - -void AS_all_channel(uint16_t T1, uint16_t T2, uint16_t *spec); -void AS_all_channel(uint16_t T1, uint16_t T2); -double get_PAR(); -double get_PAR(uint16_t *spec); - - - -uint8_t as7431_reg_write(uint8_t reg, uint8_t data); -uint8_t as7431_reg_read(uint8_t reg, uint8_t* data, uint16_t len); -void as7431_blink(uint8_t n, uint8_t intensity); diff --git a/src/calibration_math.h b/src/calibration_math.h new file mode 100644 index 0000000..cae948c --- /dev/null +++ b/src/calibration_math.h @@ -0,0 +1,188 @@ +#ifndef _CALIBRATION_MATH_H_ +#define _CALIBRATION_MATH_H_ + +#include +#include +#include +#include +#include + +namespace ambit_calibration { + +enum AdpdChannel : uint8_t { + S630 = 0, + R630 = 1, + SUN = 2, + LEAF = 3, + S730 = 4, + R730 = 5, + CHANNEL_COUNT = 6, +}; + +// These limits are part of the calibration contract, not transport-specific +// policy. Keep every text, binary, and NVS-load path on the same predicates so +// a value cannot be accepted over one frontend and rejected after reboot. +constexpr float ACTINIC_COEFFICIENT_MIN = 0.01f; // exclusive +constexpr float ACTINIC_COEFFICIENT_MAX = 1.0f; // inclusive +constexpr float SPEC_COEFFICIENT_MIN = 0.05f; // inclusive +constexpr float SPEC_COEFFICIENT_MAX = 100.0f; // inclusive +constexpr uint32_t MAX_ADPD_BASELINE = 0xFFFFFFU; +constexpr uint32_t MAX_S630_BASELINE = 400U; + +inline bool valid_actinic_coefficient(float value) { + return isfinite(value) && value > ACTINIC_COEFFICIENT_MIN && + value <= ACTINIC_COEFFICIENT_MAX; +} + +inline bool valid_spec_coefficient(float value) { + return isfinite(value) && value >= SPEC_COEFFICIENT_MIN && + value <= SPEC_COEFFICIENT_MAX; +} + +inline bool valid_adpd_baseline(const uint32_t values[CHANNEL_COUNT]) { + if (values == nullptr || values[S630] > MAX_S630_BASELINE) return false; + for (uint8_t i = 0; i < CHANNEL_COUNT; ++i) { + if (values[i] > MAX_ADPD_BASELINE) return false; + } + return true; +} + +// ── Spectral / PAR calibration (the cmd 35 three-tier chain) ──────────────── +// Ten AS7341 channels in the reported wavelength order: F1..F8, NIR, Clear. +constexpr uint8_t SPEC_CHANNEL_COUNT = 10; + +// These bounds are provisional. They exist to keep obvious garbage out of NVS +// and off the wire; the real ranges are only knowable once the LR1-B / +// Li-250A campaign produces fitted vectors (plans/AMBIT_COMMAND35_SPECPAR.md +// §11). The element-wise isfinite() check is the load-bearing part today: it is +// the only thing standing between a bad write and a NaN propagating through the +// tier-1 divide into every downstream field of the cmd 35 payload. +constexpr float SPEC_OFFSET_MAX = 1.0f; // exclusive; basic-count units +constexpr float SPEC_SENS_MAX = 1000.0f; // inclusive +constexpr float PAR_WEIGHT_ABS_MAX = 1.0e4f; +constexpr float PAR_SLOPE_MAX = 100.0f; // inclusive +constexpr float PAR_INTERCEPT_ABS_MAX = 500.0f; + +inline bool valid_spec_offset(const float values[SPEC_CHANNEL_COUNT]) { + if (values == nullptr) return false; + for (uint8_t i = 0; i < SPEC_CHANNEL_COUNT; ++i) { + if (!isfinite(values[i]) || values[i] < 0.0f || + values[i] >= SPEC_OFFSET_MAX) { + return false; + } + } + return true; +} + +inline bool valid_spec_sens(const float values[SPEC_CHANNEL_COUNT]) { + if (values == nullptr) return false; + for (uint8_t i = 0; i < SPEC_CHANNEL_COUNT; ++i) { + if (!isfinite(values[i]) || values[i] <= 0.0f || + values[i] > SPEC_SENS_MAX) { + return false; + } + } + return true; +} + +// Signs are deliberately unconstrained: the tier-2 fit legitimately produces a +// negative NIR/Clear term (stray-light subtraction), and constraining the +// visible channels is a fitting decision that belongs in the host tooling, not +// in a firmware acceptance test. +inline bool valid_par_weight(const float values[SPEC_CHANNEL_COUNT]) { + if (values == nullptr) return false; + for (uint8_t i = 0; i < SPEC_CHANNEL_COUNT; ++i) { + if (!isfinite(values[i]) || fabsf(values[i]) > PAR_WEIGHT_ABS_MAX) { + return false; + } + } + return true; +} + +inline bool valid_par_slope(float value) { + return isfinite(value) && value > 0.0f && value <= PAR_SLOPE_MAX; +} + +inline bool valid_par_intercept(float value) { + return isfinite(value) && fabsf(value) <= PAR_INTERCEPT_ABS_MAX; +} + +// par_weight ships all-zero until tier 2 is fitted, which would make the +// computed PAR collapse to par_intercept. The cmd 35 handler reports that +// through flags bit2 rather than passing a meaningless number off as a reading. +inline bool par_weight_is_unset(const float values[SPEC_CHANNEL_COUNT]) { + if (values == nullptr) return true; + for (uint8_t i = 0; i < SPEC_CHANNEL_COUNT; ++i) { + if (values[i] != 0.0f) return false; + } + return true; +} + +// Text calibration input must distinguish a real zero from atoi-style parse +// failures. strtod lets us reject missing, non-numeric, non-finite, fractional, +// and out-of-range values while accepting harmless surrounding whitespace. +inline bool parse_adpd_baseline_value(const char *text, uint32_t *value) { + if (text == nullptr || value == nullptr) return false; + + errno = 0; + char *end = nullptr; + const double parsed = strtod(text, &end); + if (end == text || errno == ERANGE || !isfinite(parsed) || parsed < 0.0 || + parsed > static_cast(MAX_ADPD_BASELINE) || + floor(parsed) != parsed) { + return false; + } + while (*end != '\0' && isspace(static_cast(*end))) ++end; + if (*end != '\0') return false; + + *value = static_cast(parsed); + return true; +} + +// Strict float tokeniser for the text calibration vectors. The established +// loose idiom (Serial_Input_Double -> range predicate) is fine where zero is +// already out of range, but par_weight accepts 0.0 as both its default and a +// legitimate fitted value, so a strtod garbage-to-0.0 would be indistinguishable +// from a deliberate write. Same shape as parse_adpd_baseline_value() minus the +// integer clause. +inline bool parse_calibration_float(const char *text, float *value) { + if (text == nullptr || value == nullptr) return false; + + errno = 0; + char *end = nullptr; + const double parsed = strtod(text, &end); + if (end == text || errno == ERANGE || !isfinite(parsed)) return false; + while (*end != '\0' && isspace(static_cast(*end))) ++end; + if (*end != '\0') return false; + + *value = static_cast(parsed); + return true; +} + +// Unsigned subtraction is defined modulo 2^32, so this remains correct when +// millis() wraps. Timeout must stay below half the uint32_t range, as ours does. +inline bool wrap_safe_timeout_elapsed(uint32_t start, uint32_t now, + uint32_t timeout_ms) { + return static_cast(now - start) >= timeout_ms; +} + +// Older AMBITs did not store sun/leaf baselines. Keep their established 65000 +// correction until a full six-channel calibration has been saved. +inline uint32_t effective_adpd_offset(uint8_t channel, + const uint32_t offsets[CHANNEL_COUNT], + bool has_explicit_offset = false) { + if (channel >= CHANNEL_COUNT) return 0; + if (has_explicit_offset || offsets[channel] != 0) return offsets[channel]; + return (channel == SUN || channel == LEAF) ? 65000U : 0U; +} + +inline uint32_t apply_adpd_offset(uint8_t channel, uint32_t sample, + const uint32_t offsets[CHANNEL_COUNT], + bool has_explicit_offset = false) { + const uint32_t offset = effective_adpd_offset(channel, offsets, has_explicit_offset); + return sample > offset ? sample - offset : 0U; +} + +} // namespace ambit_calibration + +#endif // _CALIBRATION_MATH_H_ diff --git a/config.h b/src/config.h similarity index 100% rename from config.h rename to src/config.h diff --git a/src/core.cpp b/src/core.cpp new file mode 100644 index 0000000..959fa84 --- /dev/null +++ b/src/core.cpp @@ -0,0 +1,43 @@ +#include "core.h" +#include "PAM.h" + +void core_set_currents(uint8_t i620, uint8_t i720, uint8_t ir){ + adpd_current_config.I620 = i620; + adpd_current_config.I720 = i720; + adpd_current_config.IR = ir; + adpd_current_config.init = true; + adpd_mode = ADPD_CONFIG_MODE::MPF_MODE; // dirty: next array run re-applies +} + +void core_set_gains(uint8_t fluo, uint8_t fluo_ref, uint8_t ir, uint8_t ir_ref, + uint8_t sun, uint8_t leaf){ + adpd_gains_config.Fluo = fluo; + adpd_gains_config.FluoRef = fluo_ref; + adpd_gains_config.IR = ir; + adpd_gains_config.IRRef = ir_ref; + adpd_gains_config.Sun = sun; + adpd_gains_config.Leaf = leaf; + adpd_gains_config.init = true; + adpd_mode = ADPD_CONFIG_MODE::MPF_MODE; // dirty +} + +void core_config_detector(void){ + conf_slow_FR_1(); // reads the global config + adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; +} + +void core_ensure_array_config(void){ + if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1){ + conf_slow_FR_1(); + adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; + } +} + +int core_run_array(uint8_t len, uint8_t* arr, uint8_t persist, bool allow_interrupt){ + core_ensure_array_config(); + return run_arr_type1(len, arr, persist, allow_interrupt); +} + +int core_run_mpf(uint16_t length, uint8_t interval, bool change_act, uint8_t act){ + return run_trigger_spacer(length, interval, change_act, act, true); +} diff --git a/src/core.h b/src/core.h new file mode 100644 index 0000000..badf782 --- /dev/null +++ b/src/core.h @@ -0,0 +1,42 @@ +#ifndef _CORE_H_ +#define _CORE_H_ +#include + +/* + * Transport-agnostic command core (MERGE_PLAN §5). + * + * Each function takes already-normalized values (gains 0-indexed, real widths) + * and operates on the single global config owned by PAM + * (adpd_current_config / adpd_gains_config / adpd_mode). It does NO wire I/O: + * no Serial / printf / JSON. The frontends (text do_command, binary run_esp, + * and later the openJII json adapter) parse their own wire format, resolve + * their own conventions (sentinels, 1-indexing), call these, then format output. + * + * Increment 1 covers the operations whose orchestration was duplicated across + * the text parser's arrun / arrun1 / arrun2 / q / r / w and set_currents / + * set_gains cases. The binary frontend adopts the same functions in a later + * increment, gated on the §13 wire-conformance check. + */ + +// Config setters. Values are final/normalized; the caller has already resolved +// its wire conventions. Marks the config dirty (adpd_mode = MPF_MODE) so the +// next array run re-applies it — the text path's "set, then apply on run". +void core_set_currents(uint8_t i620, uint8_t i720, uint8_t ir); +void core_set_gains(uint8_t fluo, uint8_t fluo_ref, uint8_t ir, uint8_t ir_ref, + uint8_t sun, uint8_t leaf); + +// Apply the current global config to the detector. core_config_detector applies +// unconditionally + enters array mode (binary cmd 10). core_ensure_array_config +// applies only if not already in array mode — for callers (binary cmd 21) that +// interleave wire I/O between the config step and the run step. +void core_config_detector(void); +void core_ensure_array_config(void); + +// Array-mode run: the single copy of "ensure config, then run". `persist` is +// treated as a bool (nonzero = LEDs persist), matching the legacy run_arr_type1. +int core_run_array(uint8_t len, uint8_t* arr, uint8_t persist, bool allow_interrupt); + +// Multi-phase (trigger-spacer) run; always interruptible (the legacy constant). +int core_run_mpf(uint16_t length, uint8_t interval, bool change_act, uint8_t act); + +#endif diff --git a/src/data_utils.cpp b/src/data_utils.cpp new file mode 100644 index 0000000..e4aa991 --- /dev/null +++ b/src/data_utils.cpp @@ -0,0 +1,497 @@ +#include "data_utils.h" + + +static const char* TAG = "DATA_UTIL"; +extern bool FLAG_DEICE; + + +// return 1, 2, 3 +// or -1 +int serial_read_until(uint8_t target1, uint8_t target2 = 0, uint8_t target3 = 0, uint16_t timeout = 20, bool remove = false){ + unsigned long start_t = millis(); + uint8_t counter, b = 0; + + while (((millis() - start_t) < timeout)){ + if (Serial.available() > 0){ + b = Serial.peek(); + if (b == target1){ + if (remove) Serial.read(); + return 1; + } else if ((target2 > 0) && (b == target2)){ + if (remove) Serial.read(); + return 2; + } else if ((target3 > 0) && (b == target3)){ + if (remove) Serial.read(); + return 3; + } + + b = Serial.read(); // non-target byte: discard (do NOT echo onto the link — + // an echo here injects bytes into the binary FSM stream) + } + else{ + delayMicroseconds(10); + } + } + return -1; +} + +uint16_t flush_serial(uint8_t timeout){ + uint16_t c = 0; + uint8_t r = 0; + delay(1); + unsigned long start_t = millis(); + if (Serial.available() < 1) return 0; + + while (((millis() - start_t) < timeout)){ + if (Serial.available() > 0){ + r = Serial.read(); // discard (do NOT echo onto the link — see serial_read_until) + c += 1; + }else{ + break; + } + } + return c; +} + + + +dataclass::dataclass(){ + dataclass::available = false; + dataclass::write_available = false; + dataclass::read_available = false; + ESP_LOGV(TAG, "DATACLASS Created"); + return; +}; + +dataclass::~dataclass(void){ + ESP_LOGV(TAG, "DATACLASS destroied"); + if (dataclass::available) dataclass:clean(); + return; +}; + +void dataclass::clean(void){ + if (dataclass::available) free((void*) (dataclass::arr)); + ESP_LOGV(TAG, "Memory freed"); + dataclass::arr = NULL; + dataclass::available = false; + dataclass::write_available = false; + dataclass::read_available = false; + return; +}; + + +bool dataclass::init(uint16_t length){ + if ((length > 0) and length < MAX_DATACLASS_SIZE){ + ESP_LOGV(TAG, "Allocation %d bytes", length * 4); + (dataclass::arr) = (uint32_t*) heap_caps_calloc(length, sizeof(uint32_t), MALLOC_CAP_32BIT); + if (dataclass::arr == NULL){ + ESP_LOGE(TAG, "Allocation %d bytes failed", length * 4); + return false; + } + dataclass::_length = length; + dataclass::available = true; + dataclass::clear(); + dataclass::write_available = true; + dataclass::write_ptr = 0; + dataclass::read_ptr = 0; + dataclass::read_available = false; + dataclass::_overwritten_counter = 0; + dataclass::loop_ahead = false; + return true; + } + else{ + ESP_LOGE(TAG, "Out-of-range: %d bytes ", length * 4); + return false; + } + return false; +}; + +// set data array to 0 +void dataclass::clear(void){ + if (dataclass::available){ + for(uint16_t i = 0; i < dataclass::_length; i++) (dataclass::arr)[i] = 0; + dataclass::write_ptr = 0; + dataclass::read_ptr = 0; + dataclass::loop_ahead = false; + ESP_LOGV(TAG, "ARR reset to 0"); + }else{ + ESP_LOGE(TAG,"RESET failed, not initialized"); + } +} + +// add the next point to the data array +// update loop_over +void dataclass::put(uint32_t data){ + if (!(dataclass::available)){ + ESP_LOGE(TAG, "Add failed, not initialized"); + return; + } + + // write point cycling around and reach read pointer + if ((dataclass::write_ptr >= dataclass::read_ptr) && (dataclass::loop_ahead)){ + dataclass::read_ptr = dataclass::write_ptr + 1; + dataclass::length -= dataclass::write_ptr - dataclass::read_ptr + 1; + if (dataclass::read_ptr >= dataclass::_length) dataclass::read_ptr = 0; + dataclass::_overwritten_counter += 1; + ESP_LOGW(TAG, "Data over-written, total: %d", dataclass::_overwritten_counter); + } + + dataclass::arr[write_ptr] = data; + dataclass::length += 1; + dataclass::write_ptr += 1; + + dataclass::read_available = true; + if (dataclass::write_ptr >= dataclass::_length){ + dataclass::write_ptr = 0; + dataclass::loop_ahead = true; + } + +} + +uint16_t dataclass::get_length(void){ + if (!(dataclass::available)){ + ESP_LOGE(TAG, "Add failed, not initialized"); + return 0; + } + if (!(dataclass::read_available)){ + ESP_LOGW(TAG, "No data available"); + return 0; + } + if (dataclass::loop_ahead){ + if (dataclass::write_ptr <= dataclass::read_ptr) return + dataclass::_length - dataclass::read_ptr + dataclass::write_ptr; + ESP_LOGW(TAG, "write_ptr > read_ptr After loop around!!!!"); + return 0; + }else{ + if (dataclass::write_ptr > dataclass::read_ptr) return (dataclass::write_ptr - dataclass::read_ptr); + if (dataclass::write_ptr == dataclass::read_ptr){ + dataclass::read_available = false; + return 0; + } + ESP_LOGW(TAG, "write_ptr < read_ptr WITHOUT loop around!!!!"); + return 0; + } + return 0; // should not reach here +} + + + +/* Sum of the 4 little-endian bytes of v. The data checksum is a byte-sum of + * the payload (matching the bytes send_binary_array transmits and the ambyte's + * verification) — summing the uint32 value into a uint8 would only cover the + * low byte. */ +static inline uint8_t u32_byte_sum(uint32_t v) +{ + return (uint8_t)v + (uint8_t)(v >> 8) + (uint8_t)(v >> 16) + (uint8_t)(v >> 24); +} + +// Pack `len` uint32 values into the wire format, write them to Serial, and return the +// byte-sum of exactly the bytes written (defined below). elem_width 2|4, dtype 0|1. +static uint8_t send_binary_array(uint32_t* arr, uint16_t len, uint8_t elem_width, uint8_t dtype); + +uint32_t dataclass::send(uint8_t elem_width, uint8_t dtype, uint8_t* c){ + uint8_t checksum = 0; + if (dataclass::get_length() == 0){ + dataclass::read_available = false; + ESP_LOGW(TAG, "No data available"); + return 0; + } + + if (dataclass::loop_ahead){ + if (dataclass::write_ptr <= dataclass::read_ptr){ + checksum += send_binary_array(this->arr + this->read_ptr, this->_length - this->read_ptr, elem_width, dtype); + checksum += send_binary_array(this->arr, this->read_ptr, elem_width, dtype); + *c = checksum; + return 1; + } + ESP_LOGW(TAG, "write_ptr > read_ptr After loop around!!!!"); + return 0; + }else{ + if (dataclass::write_ptr > dataclass::read_ptr){ + checksum += send_binary_array(this->arr + this->read_ptr, this->write_ptr - this->read_ptr, elem_width, dtype); + *c = checksum; + return 1;} + + if (dataclass::write_ptr == dataclass::read_ptr){ + dataclass::read_available = false; + return 0; + } + ESP_LOGW(TAG, "write_ptr < read_ptr WITHOUT loop around!!!!"); + return 0; + } + return 0; +} + +bool dataclass::pop(uint32_t* data){ + if (!(dataclass::available)){ + ESP_LOGE(TAG, "Add failed, not initialized"); + return false; + } + if (!(dataclass::read_available)){ + ESP_LOGW(TAG, "No data available"); + return false; + } + + if (dataclass::get_length() == 0){ + dataclass::read_available = false; + ESP_LOGW(TAG, "No data available"); + return false; + } + + if (dataclass::read_ptr >= dataclass::_length){ + ESP_LOGE(TAG, "Read_pointer outside range"); + return false; + } + + *data = dataclass::arr[dataclass::read_ptr]; + dataclass::read_ptr += 1; + dataclass::length -= 1; + if (dataclass::read_ptr >= dataclass::_length){ + dataclass::read_ptr = 0; + dataclass::loop_ahead = false; + } + return true; +} + +uint32_t dataclass::pop(void){ + uint32_t a; + if (dataclass::pop(&a)) return a; + return 0xABCDEF01; +} + +void dataclass::send_serial(const char* tag){ + + if (!this->available) return; + uint16_t tmp_var = dataclass::get_length(); + Serial.printf("Data:%s,Length:%d\t", tag, tmp_var); + if (tmp_var == 0){ + Serial.println(); + return; + } + for (uint16_t i = 0; i < tmp_var; i++){ + Serial.printf("%d,", dataclass::pop()); + } + Serial.print("\n"); + return; +} + +// "tag":[v0,v1,...] — channel array for the cloud/openJII JSON measurement sink. +void dataclass::send_json(const char* tag){ + if (!this->available) return; + uint16_t n = dataclass::get_length(); + Serial.printf("\"%s\":[", tag); + for (uint16_t i = 0; i < n; i++){ + if (i > 0) Serial.print(','); + Serial.print(dataclass::pop()); + } + Serial.print(']'); +} + +// Pack each value into elem_width bytes (little-endian low bytes), write to Serial, and +// return the byte-sum of exactly those bytes (Change 1: the data-trailer checksum is over +// the bytes actually sent). elem_width 4 = full uint32 (legacy/v1); 2 = low 16 bits with +// unsigned values (dtype 0) CLAMPED to 0xFFFF (Change 2 — calc_signal can exceed 16 bits +// near saturation; clamp, don't wrap). Signed (dtype 1) passes the stored int16 bit-pattern +// through unchanged; the host interprets the 2 bytes per dtype. +static uint8_t send_binary_array(uint32_t* arr, uint16_t len, uint8_t elem_width, uint8_t dtype){ + uint8_t checksum = 0; + if (elem_width >= 4){ + Serial.write((uint8_t*) arr, (size_t) len * 4); + for (uint16_t i = 0; i < len; i++) checksum += u32_byte_sum(arr[i]); + return checksum; + } + // elem_width == 2: low 16 bits, batched into a small buffer to avoid per-byte writes. + uint8_t buf[128]; + uint16_t bi = 0; + for (uint16_t i = 0; i < len; i++){ + uint32_t v = arr[i]; + uint16_t w = ((dtype == 0) && (v > 0xFFFF)) ? 0xFFFF : (uint16_t) v; + uint8_t lo = (uint8_t) w; + uint8_t hi = (uint8_t) (w >> 8); + buf[bi++] = lo; + buf[bi++] = hi; + checksum += lo + hi; + if (bi >= sizeof(buf)){ + Serial.write(buf, bi); + bi = 0; + } + } + if (bi > 0) Serial.write(buf, bi); + return checksum; +} + +// Link-level wake handshake (Change 4: one wake per run, then arrays stream back-to-back). +// Sends WAKE_AMBYTE until the ambyte acks AMBYTE_AWAKE / AMBYTE_CALLS / AMBYTE_CALLFORRESET. +// Power saving preserved: light-sleep between pulses after the first 10 fast tries (unless +// FLAG_DEICE forces the busy "power wasting" path). Returns 1 awake, ERR_LOST_SYNC on +// AMBYTE_CALLS, 0 on AMBYTE_CALLFORRESET, ERR_NO_DATA_REQUEST on timeout. The leftover +// AMBYTE_AWAKE byte is left in the buffer; the first fsm_send_array flushes it. +int ambyte_wake(bool interrupt){ + (void) interrupt; // abort byte handled by the run loop, not the wake; kept for the API + unsigned int timer1 = millis(); + int ret = -1; + uint8_t wake_up_reason, cmd_wait_time; + int16_t wake_call_counter = 0; + int16_t max_wake_try = 2000; + cmd_wait_time = 15; + flush_serial(10); + // sending wake up calls, first 10 in 10ms, remaining pulse every second w/ light sleep + while (wake_call_counter < max_wake_try){ + Serial.write(WAKE_AMBYTE); + Serial.flush(); + ret = serial_read_until(AMBYTE_AWAKE, AMBYTE_CALLS, AMBYTE_CALLFORRESET, cmd_wait_time, false); + if (ret > 0) break; + // after first 10 fast tries, pulse every second with light sleep + if (wake_call_counter == 10){ + esp_sleep_enable_timer_wakeup(1000000); // set up light sleep timer + cmd_wait_time = 100; + } + if (wake_call_counter > 10){ + wake_up_reason = 0; + + if (!FLAG_DEICE){ // Power saving mode + esp_light_sleep_start(); + wake_up_reason = esp_sleep_get_wakeup_cause(); + }else{ // Power wasting mode + for (uint8_t j = 0; j < 10; j++){ + ret = serial_read_until(AMBYTE_AWAKE, AMBYTE_CALLS, AMBYTE_CALLFORRESET, 100, false); + if (ret > 0) break; + } + } + + if (wake_up_reason == 8){ // wake up by uart + max_wake_try -= wake_call_counter; + wake_call_counter = 0; + } + } + wake_call_counter += 1; + if (millis() - timer1 > 3600000) break; + } + + if (ret == 1){ // Normal: ambyte awake + ESP_LOGV(TAG, "Ambyte awake in %d ms with %d tries", millis() - timer1, wake_call_counter); + return 1; + }else if (ret == 2){ // Lost sync + ESP_LOGE(TAG, "ERR_LOST_SYNC"); + Serial.read(); + return ERR_LOST_SYNC; + }else if (ret == 3){ + ESP_LOGE(TAG, "Ambyte calls for re-start"); + Serial.read(); + return 0; + }else{ // no response within timeout + return ERR_NO_DATA_REQUEST; + } +} + +// Stream one array to an already-awake ambyte (Change 4): length header + data + trailer. +// Header (Change 1, self-describing): {212, 150, arr_idx, lenHi, lenLo, elem_width, dtype, +// csum}, csum = sum of bytes [0..6]. Then length*elem_width data bytes, then the trailer +// {212, 0, 0, csum} whose csum is the byte-sum of exactly those data bytes. The host loops +// on the 212 marker and stops when run_esp.cpp emits the run's trailing 240 instead. +// Returns 0 on success, ERR_CHECKSUM_FAILED if the host keeps rejecting the data. +int dataclass::fsm_send_array(uint8_t arr_idx, uint8_t elem_width, uint8_t dtype){ + if (!this->available) return 1; + + uint16_t n = dataclass::get_length(); + + // --- length header --- + uint8_t hdr[8] = {212, 150, arr_idx, 0, 0, elem_width, dtype, 0}; + hdr[3] = ((n >> 8) & 0xFF); + hdr[4] = (n & 0xFF); + for (uint8_t i = 0; i < 7; i++) hdr[7] += hdr[i]; + flush_serial(5); // also clears the leftover AMBYTE_AWAKE from the wake + Serial.write(hdr, 8); + Serial.flush(); + + int ret = serial_read_until(AMBYTE_READY_FOR_ARRAY, 0, AMBYTE_CALLFORRESET, 200, true); + if (ret != 1){ + ESP_LOGE(TAG, "Array %d: no READY_FOR_ARRAY (got %d)", arr_idx, ret); + return 0; + } + + // --- data + trailer (retry while the host requests a resend) --- + for (uint8_t attempt = 0; attempt < 6; attempt++){ + uint8_t trailer[4] = {212, 0, 0, 0}; + flush_serial(5); + this->send(elem_width, dtype, &trailer[3]); // write data, fill the trailer checksum + Serial.flush(); + Serial.write(trailer, 4); + Serial.flush(); + + ret = serial_read_until(AMBYTE_DATA_PASS, AMBYTE_READY_FOR_ARRAY, AMBYTE_CALLFORRESET, 500, true); + if (ret == 1){ // pass + ESP_LOGV(TAG, "Array %d sent: %d elems x %d B", arr_idx, n, elem_width); + return 0; + }else if (ret == 2){ // host asked to resend (sent READY_FOR_ARRAY again) + ESP_LOGW(TAG, "Array %d: resend requested", arr_idx); + continue; + }else if (ret == 3){ // call for restart + ESP_LOGE(TAG, "Ambyte calls for re-start"); + return 0; + }else{ + ESP_LOGE(TAG, "Array %d: no DATA_PASS", arr_idx); + return 0; + } + } + return ERR_CHECKSUM_FAILED; +} + +int dataclass::fsm_send_esp(uint8_t arr_idx){ + return this->fsm_send_esp(arr_idx, false); +} + +// Legacy single-array convenience: wake the link then send one uint32 array (elem_width 4, +// dtype 0 -> host decodes as the v1 format). The primary run path (pam_send_results) instead +// wakes once via ambyte_wake() and streams every array with fsm_send_array(). +int dataclass::fsm_send_esp(uint8_t arr_idx, bool use_interrupt){ + if (!this->available) return 1; + if (ambyte_wake(use_interrupt) != 1) return ERR_NO_DATA_REQUEST; + this->fsm_send_array(arr_idx, 4, 0); + return 1; +} + +/* + insert a number to an array with order + large number will insert towards the end + used for get median + @param arr: array with sorted data + @param length: length of the array + @param c: new data to be inserted +*/ +void sorted_insert(uint32_t arr[], uint16_t length, uint32_t c){ + uint16_t n = 0; + uint16_t nM = length - 1; + + while (n < nM){ + if (arr[n] == 0){ + arr[n] = c; + break; + } + else if (c < arr[n]){ + for (uint8_t g = nM; g > n; g--){ + arr[g] = arr[g - 1]; + } + arr[n] = c; + break; + } else if (arr[n + 1] == 0){ + arr[n + 1] = c; + break; + }else{ + n += 1; + } + } + return; +} + + +uint32_t calc_signal(const uint32_t dark, const uint32_t lit, const uint8_t num){ + if (lit + 250 < dark) return 0; + uint32_t a = (lit - dark + 250); + int32_t b = a + 98 - dark * 0.006 / num; + + //int32_t tmp_var = (lit - dark + 250) - (0.006 / p) * (dark - 16384UL * p); + if (b > 0) return b; + return 0; +} + diff --git a/data_utils.h b/src/data_utils.h similarity index 62% rename from data_utils.h rename to src/data_utils.h index fa50f13..fe880e6 100644 --- a/data_utils.h +++ b/src/data_utils.h @@ -21,14 +21,6 @@ #define ERR_LOST_SYNC -2 #define ERR_NO_DATA_REQUEST -5 -enum DATA_STATUS { - WAKEUPCALLS, - LENGTHARRAY, - SENDDATA, - COMPLETED -}; - - class dataclass{ public: @@ -40,8 +32,7 @@ class dataclass{ bool write_available = false; // available to add data bool read_available = false; // available to read data uint16_t length = 0; - uint8_t data_fsm_state = DATA_STATUS::WAKEUPCALLS; - + uint16_t num_retry = 0; @@ -56,33 +47,37 @@ class dataclass{ uint16_t get_length(void); bool pop(uint32_t* data); uint32_t pop(void); - uint32_t send(void (*func) (uint32_t*, uint16_t), uint8_t*); + // Pack each stored uint32 into elem_width (2|4) bytes — low bytes, unsigned values + // clamped to the field — write them to Serial and return the byte-sum of exactly + // those bytes in *c. dtype (0 unsigned | 1 signed) is wire metadata for the host. + uint32_t send(uint8_t elem_width, uint8_t dtype, uint8_t* c); bool init(uint16_t length); void print_all(); void send_serial(const char*); + void send_json(const char*); // "tag":[v0,v1,...] for the cloud/openJII sink - //-- FSM--// - int fsm_wake_up_calls(void); - int fsm_wake_up_calls(bool); - int fsm_send_length_info(uint8_t arr_idx); - int fsm_send_data(void); + //-- ambyte binary protocol --// + // Stream one array to an already-awake ambyte: length header + data + trailer. + // elem_width = 2|4 bytes/elem, dtype = 0 unsigned | 1 signed (Change 1/2/4). + int fsm_send_array(uint8_t arr_idx, uint8_t elem_width, uint8_t dtype); + // Legacy convenience: wake the link then send one uint32 array (elem_width 4, dtype 0). int fsm_send_esp(uint8_t arr_idx); int fsm_send_esp(uint8_t arr_idx, bool interrupt); - int fsm_send_waitesp(); - //int send_esp(uint8_t); private: bool loop_ahead = false; uint16_t _overwritten_counter = 0; uint16_t _length = 0; // size of data inside - uint16_t _num_wake_up_calls = 0; - uint16_t _num_checksum_err = 0; }; void sorted_insert(uint32_t arr[], uint16_t length, uint32_t c); uint32_t calc_signal(uint32_t, uint32_t, uint8_t); +// Link-level wake handshake for the ambyte binary protocol (Change 4: one wake per +// run, then arrays stream back-to-back). Returns 1 when the ambyte is awake. +int ambyte_wake(bool interrupt); + #endif \ No newline at end of file diff --git a/do_command.h b/src/do_command.h similarity index 51% rename from do_command.h rename to src/do_command.h index f6cbdf2..a4d0cd1 100644 --- a/do_command.h +++ b/src/do_command.h @@ -1,443 +1,585 @@ - -#include -#include -#include -#include - -#include "serial.h" -#include "src/mlx90632/u_mlx.h" -#include "src/as7341/spec_meas.h" -#include "src/wrench.h" -#include "data_utils.h" -#include "PAM.h" -#include "nvs1.h" -#include - -int check_connections(); -extern Preferences preferences; -extern bool FLAG_DEICE; -static const char* TAG1 = "do_Cmd"; -void do_c(const char* c); - -constexpr unsigned hash(const char *string) -{ - return *string == 0 ? 17325 : *string + (*string * hash(string+1)); -} - -void do_command(char *choose){ - - int setting; - // char choose[50]; //buffer to hold commands - - //Serial_Input_Chars(choose, "+", 500, sizeof(choose) - 1); - - for (unsigned i = 0; i < strlen(choose); ++i) { // remove ctrl characters - if (!isprint(choose[i])) - choose[i] = 0; - } - - if (strlen(choose) < 1) { // short or null command, quietly ignore it - return; - } - - if (!isalnum(choose[0])) { - // Serial_Printf("error: not a-n\n"); - return; // go read another command - } - - unsigned val; // we accept int or alpha commands - if (isdigit(choose[0])) { - val = atoi(choose); - } - else { - val = hash(choose); // convert alpha command to an int - } - - - //Serial.printf("cmd: %s\n", choose); - // process single commands - switch (val) { - case hash("C"):{ - - char c[500]; - Serial_Input_Chars(c, "?", 10000, 500); - do_c(c); - //Serial.println(); - } - break; - - - case hash("hello"): - { - Serial.print("NEW Name Here"); - Serial.println(" Ready"); - } - break; - - int external_trigger_run(void); - case hash("ww"): - { - Serial.println("Start"); - external_trigger_run(); - Serial.println("Exit"); - - } - break; - - int external_trigger_run_Flash(unsigned int gate_time, unsigned int dt, const uint16_t num); - case hash("ff"): - { - unsigned int t = Serial_Input_Long(",", 10); - unsigned int dt = Serial_Input_Long(",", 10); - unsigned int num = Serial_Input_Long(",", 10); - external_trigger_run_Flash(t, dt, num); - - } - break; - - - - case hash("set_currents"): - { - adpd_current_config.I620 = (uint8_t) Serial_Input_Long(",", 10); - adpd_current_config.I720 = (uint8_t) Serial_Input_Long(",", 10); - adpd_current_config.IR = (uint8_t) Serial_Input_Long(",", 10); - adpd_current_config.init = true; - Serial.printf("Currents set to %d, %d, %d\n", adpd_current_config.I620, adpd_current_config.I720, adpd_current_config.IR); - adpd_mode = ADPD_CONFIG_MODE::MPF_MODE; // not applied - } - break; - - case hash("set_gains"): - { - adpd_gains_config.Fluo = (uint8_t) Serial_Input_Long(",", 10); - adpd_gains_config.FluoRef = (uint8_t) Serial_Input_Long(",", 10); - adpd_gains_config.IR = (uint8_t) Serial_Input_Long(",", 10); - adpd_gains_config.IRRef = (uint8_t) Serial_Input_Long(",", 10); - adpd_gains_config.Sun = (uint8_t) Serial_Input_Long(",", 10); - adpd_gains_config.Leaf = (uint8_t) Serial_Input_Long(",", 10); - adpd_gains_config.init = true; - Serial.printf("Gains set to %d, %d, %d, %d, %d, %d\n", adpd_gains_config.Fluo, adpd_gains_config.FluoRef, adpd_gains_config.IR, adpd_gains_config.IRRef, adpd_gains_config.Sun, adpd_gains_config.Leaf); - adpd_mode = ADPD_CONFIG_MODE::MPF_MODE; // not applied - } - break; - - case hash("mlx"): - { - unsigned int timer = millis(); - for (uint8_t i = 0; i < 100; i++){ - Serial.println(mlx_measure()); - } - Serial.printf("Spend %f ms per measurement", (millis() - timer)/100.0); - } - break; - - case hash("temp"): - { - // uint32_t ret; - // uint8_t mode = 5; - // float_t temp = 0.0; - - - // ret = PAM_get_env(0, 500); - // Serial.print(PAM_retrieve_env(ret, &mode)); - // Serial.printf(" %d \n", mode); - - // ret = PAM_get_env(4, 600); - // Serial.println(PAM_retrieve_env(ret, &mode, &temp)); - // Serial.printf(" %d %f \n", mode, temp); - double obj,amb,obj_r; - int16_t a1,a2,a3,a4; - - mlx_measure(&obj, &amb, &obj_r, &a1, &a2, &a3, &a4); - Serial.printf("%.4f\t%.4f\t%.4f\n", obj, amb, obj_r); - - - } - break; - - case hash("clean_nvs"): - { - nvs_flash_erase(); - nvs_flash_init(); - Serial.println("NVS cleaned"); - } - break; - - case hash("baseline"): - { - conf_slow_FR_1(100, 20, 0, 1, 5, 5, 1, 5, 5); - uint8_t c = Serial_Input_Long(",", 10); - adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; - uint32_t ret[6] = {0}; - fluor_offset(ret); - Serial.printf("%d,%d,%d,%d,%d,%d\n", ret[0], ret[1], ret[2], ret[3], ret[4], ret[5]); - if (c == 1){ - if (ret[0] > 400){ - Serial.println("Baseline too high"); - break; - } - preferences.begin("config", false); - preferences.putUInt("adpd_dark", ret[0]); - preferences.end(); - Serial.println("Baseline saved"); - } - } - break; - - case hash("tttt"): - Serial.println(sizeof(ambit_calibration_info_t)); - break; - - - - case hash("arrun"): - { - uint8_t len = (uint8_t) Serial_Input_Long(",", 10); - uint8_t persist = (uint8_t) Serial_Input_Long(",", 10); - uint8_t arr[128] = {0}; - uint8_t tmp_8 = 0; - for (uint8_t i = 0; i < len; i++){ - for (uint8_t j = 0; j < 8; j++){ - arr[i * 8 + j] = (uint8_t) Serial_Input_Long(",", 10); - } - } - - if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1){ - conf_slow_FR_1(); - adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; - } - run_arr_type1(16, arr, persist); - } - break; - - case hash("arrun1"): - { - uint8_t len = (uint8_t) Serial_Input_Long(",", 10); - uint8_t persist = (uint8_t) Serial_Input_Long(",", 10); - uint8_t arr[128] = {0}; - uint8_t tmp_8 = 0; - CONNECTION_TYPE = CONNECTION_TYPES::PLOTTING; - - - for (uint8_t i = 0; i < len; i++){ - for (uint8_t j = 0; j < 8; j++){ - arr[i * 8 + j] = (uint8_t) Serial_Input_Long(",", 10); - } - } - - if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1){ - conf_slow_FR_1(); - adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; - } - run_arr_type1(16, arr, persist); - } - Serial.println("Done"); - break; - - - case hash("q"): - { - uint8_t a = (uint8_t) Serial_Input_Long(",", 10); - - uint8_t b = (uint8_t) Serial_Input_Long(",", 10); - uint8_t c = (uint8_t) Serial_Input_Long(",", 10); - - uint8_t arr[24] = {a, 0, 1, 0, 0, b, 0, 1, \ - a, 0, 1, 0, 0, b, c, 1,\ - a, 0, 1, 0, 0, b, 0, 1}; - - CONNECTION_TYPE = CONNECTION_TYPES::PLOTTING; - //CONNECTION_TYPE = CONNECTION_TYPES::COMPUTER; - if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1){ - conf_slow_FR_1(); - adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; - } - run_arr_type1(3, arr, 0); - CONNECTION_TYPE = CONNECTION_TYPES::COMPUTER; - - } - break; - - case hash("get_par"): - { - uint16_t spec[10]; - Serial.println(get_PAR(spec)); - Serial.printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",spec[0],spec[1],spec[2],spec[3],spec[4],spec[5],spec[6],spec[7],spec[8],spec[9]); - } - break; - - case hash("PAR"): - { - uint16_t spec[10]; - Serial.println(get_PAR(spec) * ambit_calibration_local.spec_coef); - Serial.printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",spec[0],spec[1],spec[2],spec[3],spec[4],spec[5],spec[6],spec[7],spec[8],spec[9]); - } - break; - - - case hash("awake"): - { - - while(1){ - uint16_t spec[10]; - get_PAR(spec); - } - - } - break; - - - case hash("r"): - { - uint8_t a = (uint8_t) Serial_Input_Long(",", 10); - uint8_t b = (uint8_t) Serial_Input_Long(",", 10); - uint8_t c = (uint8_t) Serial_Input_Long(",", 10); - - uint8_t arr[24] = {a, 0, 1, 0, 0, b, 0, 1, \ - a, 0, 1, 0, 0, b, c, 1,\ - a, 0, 1, 0, 0, b, 0, 1}; - - CONNECTION_TYPE = CONNECTION_TYPES::AMBYTE; - //CONNECTION_TYPE = CONNECTION_TYPES::COMPUTER; - if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1){ - conf_slow_FR_1(); - adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; - } - - FLAG_DEICE = true; - run_arr_type1(3, arr, 0, true); - FLAG_DEICE = false; - CONNECTION_TYPE = CONNECTION_TYPES::COMPUTER; - - } - break; - - case hash("w"): - { - CONNECTION_TYPE = CONNECTION_TYPES::PLOTTING; - uint8_t length = (uint16_t) Serial_Input_Long(",", 10); - uint8_t interval = (uint8_t) Serial_Input_Long(",", 10); - uint8_t act = (uint8_t) Serial_Input_Long(",", 10); - run_trigger_spacer(length, interval, false, act, true); - } - break; - - - case hash("a"): - { - uint8_t a = (uint16_t) Serial_Input_Long(",", 10); - uint8_t b = (uint16_t) Serial_Input_Long(",", 10); - - as7431_blink(a, b); - - - } - break; - - - case hash("aa"): - { - uint8_t a = (uint16_t) Serial_Input_Long(",", 10); - - for (uint8_t i = 0; i < 4; i++){ - as7431_blink(i, a); - } - - for (uint8_t i = 0; i < 4; i++){ - as7431_blink(i, a); - } - - for (uint8_t i = 0; i < 4; i++){ - as7431_blink(i, a); - } - - for (uint8_t i = 0; i < 4; i++){ - as7431_blink(i, a); - } - - - } - break; - - - - - - case hash("set_act"): - { - float_t a = (float_t) Serial_Input_Double(",", 10); - preferences.begin("config", false); - preferences.putFloat("actinic", a); - preferences.end(); - } - break; - - case hash("set_name"): - { - char s[16]; - - Serial_Input_Chars(s, ",\n\r", 10, 15); - preferences.begin("config", false); - preferences.putString("name", s); - preferences.end(); - } - break; - - case hash("set_emit"): - { - double a = Serial_Input_Double(",", 10); - preferences.begin("config", false); - preferences.putDouble("emit", a); - preferences.end(); - } - break; - - case hash("set_spec"): - { - float_t f = (float_t) Serial_Input_Double(",", 10); - preferences.begin("config", false); - preferences.putFloat("spec", f); - preferences.end(); - } - break; - - case hash("reboot"): - { - ESP.restart(); - } - break; - - case hash("check"): - { - check_connections(); - } - break; - - int optic_test(); - case hash("test"): - { - optic_test(); - } - break; - - int optic_test(uint8_t current, uint8_t num_integ, uint8_t lit_offset, uint8_t dark1_offset, uint8_t dark2_offset, uint8_t pulse_offset, uint8_t pulse_duration); - - case hash("test1"): - { - uint8_t current = (uint8_t) Serial_Input_Long(",", 10); - uint8_t num_integ = (uint8_t) Serial_Input_Long(",", 10); - uint8_t lit_offset = (uint8_t) Serial_Input_Long(",", 10); - uint8_t dark1_offset = (uint8_t) Serial_Input_Long(",", 10); - uint8_t dark2_offset = (uint8_t) Serial_Input_Long(",", 10); - uint8_t pulse_offset = (uint8_t) Serial_Input_Long(",", 10); - uint8_t pulse_duration = (uint8_t) Serial_Input_Long(",", 10); - optic_test(current, num_integ, lit_offset, dark1_offset, dark2_offset, pulse_offset, pulse_duration); - } - break; - default: - Serial.println("BAD COMMAND"); - break; - - } -} \ No newline at end of file + +#include +#include +#include +#include + +#include "serial.h" +#include "src/mlx90632/u_mlx.h" +#include "src/as7341/spec_meas.h" +#include "data_utils.h" +#include "PAM.h" +#include "core.h" +#include "nvs1.h" +#include + +int check_connections(); +extern Preferences preferences; +extern bool FLAG_DEICE; +static const char* TAG1 = "do_Cmd"; + +constexpr unsigned hash(const char *string) +{ + return *string == 0 ? 17325 : *string + (*string * hash(string+1)); +} + +/* Read a ten-element float vector as comma-separated text, the same shape as + * set_baseline. Strictly parsed: par_weight takes 0.0 as both its shipping + * default and a legitimate fitted value, so the loose Serial_Input_Double idiom + * — which cannot separate a deliberate zero from a strtod parse failure — is + * not good enough here. Consumes all ten tokens before returning either way, so + * a rejected line does not leave half a vector in the input buffer. */ +static bool read_spec_vector(float values[ambit_calibration::SPEC_CHANNEL_COUNT]){ + bool valid = true; + for (uint8_t i = 0; i < ambit_calibration::SPEC_CHANNEL_COUNT; ++i){ + char token[25] = {0}; + Serial_Input_Chars(token, ",", 10, sizeof(token) - 1); + if (!ambit_calibration::parse_calibration_float(token, &values[i])) valid = false; + } + return valid; +} + +static void report_spec_save(const char *what, esp_err_t err){ + if (err == ESP_OK) Serial.printf("%s saved and verified\n", what); + else Serial.printf("%s save failed: %s\n", what, esp_err_to_name(err)); +} + +void do_command(char *choose){ + + int setting; + // char choose[50]; //buffer to hold commands + + //Serial_Input_Chars(choose, "+", 500, sizeof(choose) - 1); + + for (unsigned i = 0; i < strlen(choose); ++i) { // remove ctrl characters + if (!isprint(choose[i])) + choose[i] = 0; + } + + if (strlen(choose) < 1) { // short or null command, quietly ignore it + return; + } + + if (!isalnum(choose[0])) { + // Serial_Printf("error: not a-n\n"); + return; // go read another command + } + + unsigned val; // we accept int or alpha commands + if (isdigit(choose[0])) { + val = atoi(choose); + } + else { + val = hash(choose); // convert alpha command to an int + } + + + //Serial.printf("cmd: %s\n", choose); + // process single commands + switch (val) { + case hash("hello"): + { + // "NEW" and "Ready" are the sentinels the openJII app driver and the + // Calibratron match (substring / \bready\b); the name and FW: + // tokens are additive, both hosts tolerate them. + Serial.print("NEW "); + Serial.print(ambit_calibration_local.ambit_name); + Serial.print(" Ready FW:"); + Serial.println(AMBIT_FW_VERSION); + } + break; + + int external_trigger_run(void); + case hash("ww"): + { + Serial.println("Start"); + external_trigger_run(); + Serial.println("Exit"); + + } + break; + + int external_trigger_run_Flash(unsigned int gate_time, unsigned int dt, const uint16_t num); + case hash("ff"): + { + unsigned int t = Serial_Input_Long(",", 10); + unsigned int dt = Serial_Input_Long(",", 10); + unsigned int num = Serial_Input_Long(",", 10); + external_trigger_run_Flash(t, dt, num); + + } + break; + + + + case hash("set_currents"): + { + uint8_t i620 = (uint8_t) Serial_Input_Long(",", 10); + uint8_t i720 = (uint8_t) Serial_Input_Long(",", 10); + uint8_t ir = (uint8_t) Serial_Input_Long(",", 10); + core_set_currents(i620, i720, ir); + Serial.printf("Currents set to %d, %d, %d\n", adpd_current_config.I620, adpd_current_config.I720, adpd_current_config.IR); + } + break; + + case hash("set_gains"): + { + uint8_t fluo = (uint8_t) Serial_Input_Long(",", 10); + uint8_t fluoRef = (uint8_t) Serial_Input_Long(",", 10); + uint8_t ir = (uint8_t) Serial_Input_Long(",", 10); + uint8_t irRef = (uint8_t) Serial_Input_Long(",", 10); + uint8_t sun = (uint8_t) Serial_Input_Long(",", 10); + uint8_t leaf = (uint8_t) Serial_Input_Long(",", 10); + core_set_gains(fluo, fluoRef, ir, irRef, sun, leaf); + Serial.printf("Gains set to %d, %d, %d, %d, %d, %d\n", adpd_gains_config.Fluo, adpd_gains_config.FluoRef, adpd_gains_config.IR, adpd_gains_config.IRRef, adpd_gains_config.Sun, adpd_gains_config.Leaf); + } + break; + + case hash("mlx"): + { + unsigned int timer = millis(); + for (uint8_t i = 0; i < 100; i++){ + Serial.println(mlx_measure()); + } + Serial.printf("Spend %f ms per measurement", (millis() - timer)/100.0); + } + break; + + case hash("temp"): + { + // uint32_t ret; + // uint8_t mode = 5; + // float_t temp = 0.0; + + + // ret = PAM_get_env(0, 500); + // Serial.print(PAM_retrieve_env(ret, &mode)); + // Serial.printf(" %d \n", mode); + + // ret = PAM_get_env(4, 600); + // Serial.println(PAM_retrieve_env(ret, &mode, &temp)); + // Serial.printf(" %d %f \n", mode, temp); + double obj,amb,obj_r; + int16_t a1,a2,a3,a4; + + mlx_measure(&obj, &amb, &obj_r, &a1, &a2, &a3, &a4); + Serial.printf("%.4f\t%.4f\t%.4f\n", obj, amb, obj_r); + + + } + break; + + case hash("clean_nvs"): + { + nvs_flash_erase(); + nvs_flash_init(); + Serial.println("NVS cleaned"); + } + break; + + case hash("baseline"): + { + uint8_t c = Serial_Input_Long(",", 10); + int acquisition_err = conf_slow_FR_1(100, 20, 0, 1, 5, 5, 1, 5, 5); + uint32_t ret[6] = {0}; + if (acquisition_err == 0) acquisition_err = fluor_offset(ret); + if (acquisition_err != 0){ + Serial.printf("Baseline acquisition failed: %d\n", acquisition_err); + break; + } + Serial.printf("%d,%d,%d,%d,%d,%d\n", ret[0], ret[1], ret[2], ret[3], ret[4], ret[5]); + if (c == 1){ + if (ret[0] > ambit_calibration::MAX_S630_BASELINE){ + Serial.println("Baseline too high"); + break; + } + esp_err_t baseline_err = save_adpd_baseline(ret); + if (baseline_err == ESP_OK) Serial.println("Baseline saved and verified"); + else Serial.printf("Baseline save failed: %s\n", esp_err_to_name(baseline_err)); + } + } + break; + + case hash("set_baseline"): + { + uint32_t values[ambit_calibration::CHANNEL_COUNT] = {0}; + bool valid = true; + for (uint8_t i = 0; i < ambit_calibration::CHANNEL_COUNT; ++i){ + char token[25] = {0}; + Serial_Input_Chars(token, ",", 10, sizeof(token) - 1); + if (!ambit_calibration::parse_adpd_baseline_value(token, &values[i])) valid = false; + } + if (!valid || !ambit_calibration::valid_adpd_baseline(values)){ + Serial.println("Baseline rejected"); + break; + } + esp_err_t baseline_err = save_adpd_baseline(values); + if (baseline_err == ESP_OK) Serial.println("Baseline saved and verified"); + else Serial.printf("Baseline save failed: %s\n", esp_err_to_name(baseline_err)); + } + break; + + case hash("tttt"): + Serial.println(sizeof(ambit_calibration_info_t)); + break; + + + + case hash("arrun"): + { + uint8_t len = (uint8_t) Serial_Input_Long(",", 10); + uint8_t persist = (uint8_t) Serial_Input_Long(",", 10); + uint8_t arr[128] = {0}; + uint8_t tmp_8 = 0; + for (uint8_t i = 0; i < len; i++){ + for (uint8_t j = 0; j < 8; j++){ + arr[i * 8 + j] = (uint8_t) Serial_Input_Long(",", 10); + } + } + + core_run_array(16, arr, persist, false); + } + break; + + case hash("arrun1"): + { + uint8_t len = (uint8_t) Serial_Input_Long(",", 10); + uint8_t persist = (uint8_t) Serial_Input_Long(",", 10); + uint8_t arr[128] = {0}; + uint8_t tmp_8 = 0; + CONNECTION_TYPE = CONNECTION_TYPES::PLOTTING; + + + for (uint8_t i = 0; i < len; i++){ + for (uint8_t j = 0; j < 8; j++){ + arr[i * 8 + j] = (uint8_t) Serial_Input_Long(",", 10); + } + } + + core_run_array(16, arr, persist, false); + } + Serial.println("Done"); + break; + + // arrun2 — like arrun1 but COMPUTER mode. Runs the whole trace, then dumps + // each data array as one ASCII block ("Data:,Length:N\t v,v,...,") + // ending with "Data sent". No per-point streaming → no inter-point UART + // gaps, so it stays in sync on the ambyte (device-to-device) link. + case hash("arrun2"): + { + uint8_t len = (uint8_t) Serial_Input_Long(",", 10); + uint8_t persist = (uint8_t) Serial_Input_Long(",", 10); + uint8_t arr[128] = {0}; + CONNECTION_TYPE = CONNECTION_TYPES::COMPUTER; + + for (uint8_t i = 0; i < len; i++){ + for (uint8_t j = 0; j < 8; j++){ + arr[i * 8 + j] = (uint8_t) Serial_Input_Long(",", 10); + } + } + + core_run_array(16, arr, persist, false); + } + break; + + + case hash("q"): + { + uint8_t a = (uint8_t) Serial_Input_Long(",", 10); + + uint8_t b = (uint8_t) Serial_Input_Long(",", 10); + uint8_t c = (uint8_t) Serial_Input_Long(",", 10); + + uint8_t arr[24] = {a, 0, 1, 0, 0, b, 0, 1, \ + a, 0, 1, 0, 0, b, c, 1,\ + a, 0, 1, 0, 0, b, 0, 1}; + + CONNECTION_TYPE = CONNECTION_TYPES::PLOTTING; + //CONNECTION_TYPE = CONNECTION_TYPES::COMPUTER; + core_run_array(3, arr, 0, false); + CONNECTION_TYPE = CONNECTION_TYPES::COMPUTER; + + } + break; + + case hash("get_par"): + { + uint16_t spec[10]; + Serial.println(get_PAR(spec)); + Serial.printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",spec[0],spec[1],spec[2],spec[3],spec[4],spec[5],spec[6],spec[7],spec[8],spec[9]); + } + break; + + case hash("PAR"): + { + uint16_t spec[10]; + Serial.println(get_PAR(spec) * ambit_calibration_local.spec_coef); + Serial.printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",spec[0],spec[1],spec[2],spec[3],spec[4],spec[5],spec[6],spec[7],spec[8],spec[9]); + } + break; + + + case hash("awake"): + { + + while(1){ + uint16_t spec[10]; + get_PAR(spec); + } + + } + break; + + + case hash("r"): + { + uint8_t a = (uint8_t) Serial_Input_Long(",", 10); + uint8_t b = (uint8_t) Serial_Input_Long(",", 10); + uint8_t c = (uint8_t) Serial_Input_Long(",", 10); + + uint8_t arr[24] = {a, 0, 1, 0, 0, b, 0, 1, \ + a, 0, 1, 0, 0, b, c, 1,\ + a, 0, 1, 0, 0, b, 0, 1}; + + CONNECTION_TYPE = CONNECTION_TYPES::AMBYTE; + //CONNECTION_TYPE = CONNECTION_TYPES::COMPUTER; + FLAG_DEICE = true; + core_run_array(3, arr, 0, true); + FLAG_DEICE = false; + CONNECTION_TYPE = CONNECTION_TYPES::COMPUTER; + + } + break; + + case hash("w"): + { + CONNECTION_TYPE = CONNECTION_TYPES::PLOTTING; + uint16_t length = Serial_Input_Long(",", 10); + uint8_t interval = (uint8_t) Serial_Input_Long(",", 10); + uint8_t act = (uint8_t) Serial_Input_Long(",", 10); + core_run_mpf(length, interval, false, act); + } + break; + + + case hash("a"): + { + uint8_t a = Serial_Input_Long(",", 10); + uint8_t b = Serial_Input_Long(",", 10); + + as7431_blink(a, b); + + + } + break; + + + case hash("aa"): + { + uint8_t a = Serial_Input_Long(",", 10); + + for (uint8_t i = 0; i < 4; i++){ + as7431_blink(i, a); + } + + for (uint8_t i = 0; i < 4; i++){ + as7431_blink(i, a); + } + + for (uint8_t i = 0; i < 4; i++){ + as7431_blink(i, a); + } + + for (uint8_t i = 0; i < 4; i++){ + as7431_blink(i, a); + } + + + } + break; + + + + + + case hash("set_act"): + { + float_t a = (float_t) Serial_Input_Double(",", 10); + if (ambit_calibration::valid_actinic_coefficient(a)){ + const esp_err_t err = save_actinic_coefficient(a); + if (err != ESP_OK) Serial.printf("Actinic coefficient save failed: %s\n", esp_err_to_name(err)); + }else{ + Serial.println("Actinic coefficient rejected"); + } + } + break; + + case hash("set_name"): + { + char s[16]; + + Serial_Input_Chars(s, ",\n\r", 10, 15); + preferences.begin("config", false); + preferences.putString("name", s); + preferences.end(); + } + break; + + case hash("set_emit"): + { + double a = Serial_Input_Double(",", 10); + preferences.begin("config", false); + preferences.putDouble("emit", a); + preferences.end(); + } + break; + + case hash("set_spec"): + { + float_t f = (float_t) Serial_Input_Double(",", 10); + if (ambit_calibration::valid_spec_coefficient(f)){ + const esp_err_t err = save_spec_coefficient(f); + if (err != ESP_OK) Serial.printf("PAR coefficient save failed: %s\n", esp_err_to_name(err)); + }else{ + Serial.println("PAR coefficient rejected"); + } + } + break; + + /* Spectral/PAR calibration — the three-tier chain cmd 35 computes. + * See plans/AMBIT_COMMAND35_SPECPAR.md. + * + * These live on the text console rather than as new binary cmd 17/18 + * subtypes: this is the dialect the Calibratron speaks, and the + * Calibratron is what runs the calibration. It also reports acceptance, + * where cmds 17/18 write nothing at all on an unrecognised subtype — no + * ESP_CMD_DONE, no ESP_CMD_END — so an older image costs the host its full + * read timeout and, for cmd 18, leaves the unread vector payload behind to + * desync the next frame. + * + * Vectors are ten comma-separated floats in wavelength order: + * F1..F8, NIR, Clear. Validation and persistence both happen inside the + * save_* helpers, so every transport shares one predicate. */ + case hash("set_spec_offset"): + { + float values[ambit_calibration::SPEC_CHANNEL_COUNT] = {0.0f}; + if (!read_spec_vector(values) || !ambit_calibration::valid_spec_offset(values)){ + Serial.println("Spectral offset rejected"); + break; + } + report_spec_save("Spectral offset", save_spec_offset(values)); + } + break; + + case hash("set_spec_sens"): + { + float values[ambit_calibration::SPEC_CHANNEL_COUNT] = {0.0f}; + if (!read_spec_vector(values) || !ambit_calibration::valid_spec_sens(values)){ + Serial.println("Spectral sensitivity rejected"); + break; + } + report_spec_save("Spectral sensitivity", save_spec_sens(values)); + } + break; + + case hash("set_par_weight"): + { + float values[ambit_calibration::SPEC_CHANNEL_COUNT] = {0.0f}; + if (!read_spec_vector(values) || !ambit_calibration::valid_par_weight(values)){ + Serial.println("PAR weight rejected"); + break; + } + report_spec_save("PAR weight", save_par_weight(values)); + } + break; + + case hash("set_par_slope"): + { + float value = 0.0f; + char token[25] = {0}; + Serial_Input_Chars(token, ",", 10, sizeof(token) - 1); + if (!ambit_calibration::parse_calibration_float(token, &value) || + !ambit_calibration::valid_par_slope(value)){ + Serial.println("PAR slope rejected"); + break; + } + report_spec_save("PAR slope", save_par_slope(value)); + } + break; + + case hash("set_par_icept"): + { + float value = 0.0f; + char token[25] = {0}; + Serial_Input_Chars(token, ",", 10, sizeof(token) - 1); + if (!ambit_calibration::parse_calibration_float(token, &value) || + !ambit_calibration::valid_par_intercept(value)){ + Serial.println("PAR intercept rejected"); + break; + } + report_spec_save("PAR intercept", save_par_intercept(value)); + } + break; + + /* Text mirror of cmd 33 subtype 4. Onboarding needs to confirm a vector + * actually landed in NVS, and the Calibratron should not have to speak + * binary to do it. */ + case hash("get_spec_cal"): + { + Serial.print("spec_offset:"); + for (uint8_t i = 0; i < ambit_calibration::SPEC_CHANNEL_COUNT; ++i){ + Serial.printf("%s%.9g", i ? "," : "", ambit_spec_calibration.spec_offset[i]); + } + Serial.print("\nspec_sens:"); + for (uint8_t i = 0; i < ambit_calibration::SPEC_CHANNEL_COUNT; ++i){ + Serial.printf("%s%.9g", i ? "," : "", ambit_spec_calibration.spec_sens[i]); + } + Serial.print("\npar_weight:"); + for (uint8_t i = 0; i < ambit_calibration::SPEC_CHANNEL_COUNT; ++i){ + Serial.printf("%s%.9g", i ? "," : "", ambit_spec_calibration.par_weight[i]); + } + Serial.printf("\npar_slope:%.9g\npar_intercept:%.9g\n", + ambit_spec_calibration.par_slope, + ambit_spec_calibration.par_intercept); + } + break; + + case hash("reboot"): + { + ESP.restart(); + } + break; + + case hash("check"): + { + check_connections(); + } + break; + + int optic_test(); + case hash("test"): + { + optic_test(); + } + break; + + int optic_test(uint8_t current, uint8_t num_integ, uint8_t lit_offset, uint8_t dark1_offset, uint8_t dark2_offset, uint8_t pulse_offset, uint8_t pulse_duration); + + case hash("test1"): + { + uint8_t current = (uint8_t) Serial_Input_Long(",", 10); + uint8_t num_integ = (uint8_t) Serial_Input_Long(",", 10); + uint8_t lit_offset = (uint8_t) Serial_Input_Long(",", 10); + uint8_t dark1_offset = (uint8_t) Serial_Input_Long(",", 10); + uint8_t dark2_offset = (uint8_t) Serial_Input_Long(",", 10); + uint8_t pulse_offset = (uint8_t) Serial_Input_Long(",", 10); + uint8_t pulse_duration = (uint8_t) Serial_Input_Long(",", 10); + optic_test(current, num_integ, lit_offset, dark1_offset, dark2_offset, pulse_offset, pulse_duration); + } + break; + default: + Serial.println("BAD COMMAND"); + break; + + } +} diff --git a/src/frontend_json.cpp b/src/frontend_json.cpp new file mode 100644 index 0000000..accc0a4 --- /dev/null +++ b/src/frontend_json.cpp @@ -0,0 +1,190 @@ +// frontend_json — wires the device's snapshot commands into the openJII protocol +// module (was frontend_cloud, gated on the deleted VARIANT_CLOUD). In the single +// image the main loop routes only '{'/'[' envelope traffic here; bare printable +// lines keep their legacy do_command console replies (that text console is what +// the openJII app driver and the Calibratron actually parse), and openJII LINE +// mode is intentionally unreachable — it had no live consumer. +// +// Measurement commands beyond arrun (q/mpf as one streamed JSON value per +// envelope position) are added once D6 — the app's exact in-envelope measurement +// shape — is settled. +#include +#include +#include "openjii_proto.h" +#include "src/devices_init.h" +#include "src/adpd/u_adpd6100.h" +#include "src/as7341/spec_meas.h" +#include "src/mlx90632/u_mlx.h" +#include "nvs1.h" +#include "config.h" +#include "PAM.h" +#include "core.h" + +extern ADPD6 adpd; // defined in ambit-1.ino + +// ── snapshot command handlers (command-as-root, §2.1) ─────────────────────── + +static void cmd_hello(const String& args, JsonVariant root) { + root["device"] = "Ambit"; + root["version"] = AMBIT_FW_VERSION; // build-injected, same source as cmd 33/2 and text hello + root["name"] = ambit_calibration_local.ambit_name; +} + +static void cmd_temp(const String& args, JsonVariant root) { + double leaf, ambient, reflect; + int16_t a1, a2, a3, a4; + mlx_measure(&leaf, &ambient, &reflect, &a1, &a2, &a3, &a4); + root["leaf"] = leaf; + root["ambient"] = ambient; + root["reflect"] = reflect; +} + +static void cmd_get_par(const String& args, JsonVariant root) { + uint16_t spec[10]; + float par = get_PAR(spec); + root["par_raw"] = par; + JsonArray ch = root["channels"].to(); + for (int i = 0; i < 10; i++) ch.add(spec[i]); +} + +static void cmd_PAR(const String& args, JsonVariant root) { + uint16_t spec[10]; + float par = get_PAR(spec) * ambit_calibration_local.spec_coef; + root["par"] = par; + JsonArray ch = root["channels"].to(); + for (int i = 0; i < 10; i++) ch.add(spec[i]); +} + +// ── measurement command (one streamed JSON value per envelope slot) ───────── +// n-th comma token of `a` (token 0 is before the first comma). +static long arg_long(const String& a, uint8_t n, long def = 0) { + int start = 0; + for (uint8_t i = 0; i < n; i++) { + start = a.indexOf(',', start); + if (start < 0) return def; + start++; + } + return a.substring(start).toInt(); +} + +// Parse comma-separated uint8 values from token index `from` into arr. Return +// capacity + 1 as soon as an extra token is seen so callers can distinguish an +// overlong payload without writing past the destination. +static size_t arg_array8(const String& a, uint8_t from, uint8_t* arr, size_t capacity) { + size_t count = 0; + int pos = 0; + for (uint8_t i = 0; i < from; i++) { + pos = a.indexOf(',', pos); + if (pos < 0) return 0; + pos++; + } + while (pos >= 0 && pos < (int)a.length()) { + if (count >= capacity) return capacity + 1; + int next = a.indexOf(',', pos); + if (next < 0) next = a.length(); + arr[count++] = (uint8_t) a.substring(pos, next).toInt(); + pos = next + 1; + if (next == (int)a.length()) break; + } + return count; +} + +static constexpr long ARRUN_MIN_LEN = 1; +static constexpr long ARRUN_MAX_LEN = 16; + +static constexpr bool valid_arrun_shape(long len, size_t parsed) { + // Validate before multiplying: `len` comes from the wire and must not be + // narrowed to uint8_t (or multiplied there) until its range is known. + return len >= ARRUN_MIN_LEN && len <= ARRUN_MAX_LEN && + parsed == static_cast(len) * 8U; +} + +// There is no host/unit harness in this firmware. Keep the boundary contract +// compiler-checked, including the one-point request that exposed the regression. +static_assert(valid_arrun_shape(1, 8), "one-point arrun must be accepted"); +static_assert(valid_arrun_shape(16, 128), "full arrun must be accepted"); +static_assert(!valid_arrun_shape(0, 0), "empty arrun must be rejected"); +static_assert(!valid_arrun_shape(17, 136), "oversized arrun must be rejected"); +static_assert(!valid_arrun_shape(1, 128), "arrun byte count must match len"); + +// arrun,,,<8*len array bytes> — runs an array-mode measurement and +// emits one JSON object {"env":[..],"s_630":[..],...}. run_arr_type1's json_output +// path writes it to Serial, which IS the openJII output stream in the cloud build. +static void cmd_arrun(const String& args, Print& out) { + long requested_len = arg_long(args, 0); + uint8_t persist = (uint8_t) arg_long(args, 1); + uint8_t arr[128] = {0}; + if (requested_len < ARRUN_MIN_LEN || requested_len > ARRUN_MAX_LEN) { + out.print("{\"error\":\"bad_arrun\"}"); + return; + } + const uint8_t len = static_cast(requested_len); + const size_t expected = static_cast(len) * 8U; + const size_t parsed = arg_array8(args, 2, arr, expected); + if (!valid_arrun_shape(requested_len, parsed)) { + out.print("{\"error\":\"bad_arrun\"}"); + return; + } + + if (!adpd_gains_config.init) adpd_gains_config.init = true; + if (!adpd_current_config.init) adpd_current_config.init = true; + if (adpd_mode != ADPD_CONFIG_MODE::ARRAY_MODE1) { + conf_slow_FR_1(); + adpd_mode = ADPD_CONFIG_MODE::ARRAY_MODE1; + } + CONNECTION_TYPE = CONNECTION_TYPES::COMPUTER; // no inline plotting; JSON emitted at the end + run_arr_type1(len, arr, persist, false, true); // json_output -> writes the JSON object to Serial +} + +// ── config setters (snapshot: ack with the applied values; command-as-root) ── +// 0-indexed values; they take effect on the next run (core marks config dirty). +static void cmd_set_currents(const String& args, JsonVariant root) { + core_set_currents((uint8_t) arg_long(args, 0), (uint8_t) arg_long(args, 1), (uint8_t) arg_long(args, 2)); + root["I620"] = adpd_current_config.I620; + root["I720"] = adpd_current_config.I720; + root["IR"] = adpd_current_config.IR; +} + +static void cmd_set_gains(const String& args, JsonVariant root) { + core_set_gains((uint8_t) arg_long(args, 0), (uint8_t) arg_long(args, 1), (uint8_t) arg_long(args, 2), + (uint8_t) arg_long(args, 3), (uint8_t) arg_long(args, 4), (uint8_t) arg_long(args, 5)); + root["Fluo"] = adpd_gains_config.Fluo; + root["FluoRef"] = adpd_gains_config.FluoRef; + root["IR"] = adpd_gains_config.IR; + root["IRRef"] = adpd_gains_config.IRRef; + root["Sun"] = adpd_gains_config.Sun; + root["Leaf"] = adpd_gains_config.Leaf; +} + +// ── config getters (read back the staged config; command-as-root) ─────────── +static void cmd_get_currents(const String& args, JsonVariant root) { + root["I620"] = adpd_current_config.I620; + root["I720"] = adpd_current_config.I720; + root["IR"] = adpd_current_config.IR; +} + +static void cmd_get_gains(const String& args, JsonVariant root) { + root["Fluo"] = adpd_gains_config.Fluo; + root["FluoRef"] = adpd_gains_config.FluoRef; + root["IR"] = adpd_gains_config.IR; + root["IRRef"] = adpd_gains_config.IRRef; + root["Sun"] = adpd_gains_config.Sun; + root["Leaf"] = adpd_gains_config.Leaf; +} + +// Register the openJII envelope surface. Called once from setup() in +// ambit-1.ino; the device init itself lives there (single shared boot path). +// No printf/markers here: UART0 is the protocol port, and ASCII in front of a +// binary frame breaks the Ambyte session. +void frontend_json_register() { + ojii::identity({ "Ambit", AMBIT_FW_VERSION, AMBIT_FW_VERSION }); + ojii::on("hello", cmd_hello); + ojii::on("temp", cmd_temp); + ojii::on("get_par", cmd_get_par); + ojii::on("PAR", cmd_PAR); + ojii::on("set_currents", cmd_set_currents); + ojii::on("set_gains", cmd_set_gains); + ojii::on("get_currents", cmd_get_currents); + ojii::on("get_gains", cmd_get_gains); + ojii::on_stream("arrun", cmd_arrun); +} diff --git a/src/nvs1.cpp b/src/nvs1.cpp new file mode 100644 index 0000000..fc133a0 --- /dev/null +++ b/src/nvs1.cpp @@ -0,0 +1,351 @@ +#include "nvs1.h" +#include "src/mlx90632/u_mlx.h" +#include +#include "nvs.h" +#include + +extern Preferences preferences; +struct ambit_calibration_info_t ambit_calibration_local, ambit_calibration_income; +struct ambit_FW_info_t ambit_FW_info; +struct metadata_t metadata_epprom, metadata_incoming; + +static const char *const ADPD_BASELINE_KEYS[ambit_calibration::CHANNEL_COUNT] = { + "adpd_dark", "adpd_lit", "adpd_sun", "adpd_leaf", "adpd_730", "adpd_730r" +}; +static bool adpd_baseline_present[ambit_calibration::CHANNEL_COUNT] = {false}; + +struct ambit_spec_calibration_t ambit_spec_calibration; +bool ambit_spec_tier3_stored = false; + +// NVS keys are capped at 15 characters. +static const char *const SPEC_OFFSET_KEY = "spec_off"; +static const char *const SPEC_SENS_KEY = "spec_sens"; +static const char *const PAR_WEIGHT_KEY = "par_w"; +static const char *const PAR_SLOPE_KEY = "par_slope"; +static const char *const PAR_INTERCEPT_KEY = "par_icept"; + +// Largest blob this module reads or writes: one ten-element float vector. +// Bounds the readback buffer in save_calibration_blob() so it can stay on stack. +#define SPEC_CALIBRATION_VECTOR_BYTES \ + (sizeof(float) * ambit_calibration::SPEC_CHANNEL_COUNT) +#define SPEC_CALIBRATION_BLOB_MAX SPEC_CALIBRATION_VECTOR_BYTES + +uint32_t apply_adpd_calibration(uint8_t channel, uint32_t sample){ + const bool present = channel < ambit_calibration::CHANNEL_COUNT + ? adpd_baseline_present[channel] : false; + return ambit_calibration::apply_adpd_offset( + channel, sample, ambit_calibration_local.adpd, present); +} + +esp_err_t save_adpd_baseline(const uint32_t values[ambit_calibration::CHANNEL_COUNT]){ + if (!ambit_calibration::valid_adpd_baseline(values)) return ESP_ERR_INVALID_ARG; + + nvs_handle_t handle; + esp_err_t err = nvs_open("config", NVS_READWRITE, &handle); + if (err != ESP_OK) return err; + + for (uint8_t i = 0; i < ambit_calibration::CHANNEL_COUNT; ++i){ + err = nvs_set_u32(handle, ADPD_BASELINE_KEYS[i], values[i]); + if (err != ESP_OK){ + nvs_close(handle); // uncommitted writes are discarded + return err; + } + } + err = nvs_commit(handle); // one atomic NVS commit for the complete vector + if (err != ESP_OK){ + nvs_close(handle); + return err; + } + + uint32_t readback[ambit_calibration::CHANNEL_COUNT] = {0}; + for (uint8_t i = 0; i < ambit_calibration::CHANNEL_COUNT; ++i){ + err = nvs_get_u32(handle, ADPD_BASELINE_KEYS[i], &readback[i]); + if (err != ESP_OK || readback[i] != values[i]){ + nvs_close(handle); + return err != ESP_OK ? err : ESP_ERR_INVALID_STATE; + } + } + nvs_close(handle); + memcpy(ambit_calibration_local.adpd, readback, sizeof(readback)); + memcpy(ambit_calibration_income.adpd, readback, sizeof(readback)); + for (uint8_t i = 0; i < ambit_calibration::CHANNEL_COUNT; ++i){ + adpd_baseline_present[i] = true; + } + return ESP_OK; +} + +// Generalised from the scalar version: same commit-then-verify contract, any +// length. Preferences::putFloat stores a four-byte NVS blob, so a single float +// written through here stays byte-compatible with existing installations. +// A vector is one blob rather than N keys, which makes it strictly more atomic +// than save_adpd_baseline()'s six-key loop — nvs_set_blob + nvs_commit is +// all-or-nothing by construction. +static esp_err_t save_calibration_blob(const char *key, const void *data, size_t len){ + if (key == nullptr || data == nullptr || len == 0) return ESP_ERR_INVALID_ARG; + if (len > SPEC_CALIBRATION_BLOB_MAX) return ESP_ERR_INVALID_SIZE; + + nvs_handle_t handle; + esp_err_t err = nvs_open("config", NVS_READWRITE, &handle); + if (err != ESP_OK) return err; + + err = nvs_set_blob(handle, key, data, len); + if (err == ESP_OK) err = nvs_commit(handle); + + uint8_t readback[SPEC_CALIBRATION_BLOB_MAX] = {0}; + size_t readback_size = len; + if (err == ESP_OK){ + err = nvs_get_blob(handle, key, readback, &readback_size); + if (err == ESP_OK && + (readback_size != len || memcmp(readback, data, len) != 0)){ + err = ESP_ERR_INVALID_STATE; + } + } + nvs_close(handle); + return err; +} + +static esp_err_t save_calibration_float(const char *key, float value){ + return save_calibration_blob(key, &value, sizeof(value)); +} + +esp_err_t save_actinic_coefficient(float value){ + if (!ambit_calibration::valid_actinic_coefficient(value)) return ESP_ERR_INVALID_ARG; + const esp_err_t err = save_calibration_float("actinic", value); + if (err == ESP_OK) ambit_calibration_local.actinic_coef = value; + return err; +} + +esp_err_t save_spec_coefficient(float value){ + if (!ambit_calibration::valid_spec_coefficient(value)) return ESP_ERR_INVALID_ARG; + const esp_err_t err = save_calibration_float("spec", value); + if (err == ESP_OK) ambit_calibration_local.spec_coef = value; + return err; +} + +/* Spectral/PAR calibration setters. Validation lives here rather than in the + * callers, following save_adpd_baseline(): one call site covers the text + * console, any future binary subtype, and anything else that ever writes these, + * which is what calibration_math.h's shared-predicate rule asks for. The runtime + * copy moves only after NVS has confirmed the write. */ +static esp_err_t save_spec_vector(const char *key, const float *values, float *destination){ + const esp_err_t err = save_calibration_blob(key, values, SPEC_CALIBRATION_VECTOR_BYTES); + if (err == ESP_OK) memcpy(destination, values, SPEC_CALIBRATION_VECTOR_BYTES); + return err; +} + +esp_err_t save_spec_offset(const float values[ambit_calibration::SPEC_CHANNEL_COUNT]){ + if (!ambit_calibration::valid_spec_offset(values)) return ESP_ERR_INVALID_ARG; + return save_spec_vector(SPEC_OFFSET_KEY, values, ambit_spec_calibration.spec_offset); +} + +esp_err_t save_spec_sens(const float values[ambit_calibration::SPEC_CHANNEL_COUNT]){ + if (!ambit_calibration::valid_spec_sens(values)) return ESP_ERR_INVALID_ARG; + return save_spec_vector(SPEC_SENS_KEY, values, ambit_spec_calibration.spec_sens); +} + +esp_err_t save_par_weight(const float values[ambit_calibration::SPEC_CHANNEL_COUNT]){ + if (!ambit_calibration::valid_par_weight(values)) return ESP_ERR_INVALID_ARG; + return save_spec_vector(PAR_WEIGHT_KEY, values, ambit_spec_calibration.par_weight); +} + +esp_err_t save_par_slope(float value){ + if (!ambit_calibration::valid_par_slope(value)) return ESP_ERR_INVALID_ARG; + const esp_err_t err = save_calibration_float(PAR_SLOPE_KEY, value); + if (err == ESP_OK){ + ambit_spec_calibration.par_slope = value; + ambit_spec_tier3_stored = true; // cmd 35 flags bit9 + } + return err; +} + +esp_err_t save_par_intercept(float value){ + if (!ambit_calibration::valid_par_intercept(value)) return ESP_ERR_INVALID_ARG; + const esp_err_t err = save_calibration_float(PAR_INTERCEPT_KEY, value); + if (err == ESP_OK){ + ambit_spec_calibration.par_intercept = value; + ambit_spec_tier3_stored = true; // cmd 35 flags bit9 + } + return err; +} + + + +static void get_FW_info(void){ + ambit_FW_info.MAC = ESP.getEfuseMac(); + ambit_FW_info.Size = ESP.getSketchSize(); + strncpy(ambit_FW_info.FW_date, __DATE__, 12); + preferences.begin("config", true); + ambit_FW_info.hw_rev = preferences.getUChar("hw_rev", 0); + preferences.end(); + ambit_FW_info.Checksum = 0xFF & ((MAJOR_VERSION << 4) | (MINOR_VERSION << 2) | (BATCH_VERSION << 0)); + + return; +} + + +static void load_metadata(void){ + preferences.begin("metadata", true); + if (preferences.isKey("lon")) metadata_epprom.lon = preferences.getDouble("lon", 1.0); + if (preferences.isKey("lat")) metadata_epprom.lat = preferences.getDouble("lat", 1.0); + if (preferences.isKey("alt")) metadata_epprom.alt = preferences.getFloat("alt", 1.0); + if (preferences.isKey("time")) metadata_epprom.time = preferences.getUInt("time", 1); + if (preferences.isKey("acc")) metadata_epprom.acc = preferences.getFloat("acc", 1.0); + if (preferences.isKey("vacc")) metadata_epprom.vacc = preferences.getFloat("vacc", 1.0); + if (preferences.isKey("info1")) preferences.getString("info1", metadata_epprom.info1, 200); + if (preferences.isKey("x")) metadata_epprom.x = preferences.getFloat("x", 0.0); + if (preferences.isKey("y")) metadata_epprom.y = preferences.getFloat("y", 0.0); + if (preferences.isKey("z")) metadata_epprom.z = preferences.getFloat("z", 0.0); + preferences.end(); + memcpy(&metadata_incoming, &metadata_epprom, sizeof(metadata_t)); + return; +} + +void save_metadata(void){ + //QC + if (metadata_incoming.lon < -180.0 || metadata_incoming.lon > 360.0) return; + if (metadata_incoming.lat < -90.0 || metadata_incoming.lat > 90.0) return; + if (metadata_incoming.alt < -500.0 || metadata_incoming.alt > 30000.0) return; + // Save metadata to NVS + preferences.begin("metadata", false); + preferences.putDouble("lon", metadata_incoming.lon); + preferences.putDouble("lat", metadata_incoming.lat); + preferences.putFloat("alt", metadata_incoming.alt); + preferences.putUInt("time", metadata_incoming.time); + preferences.putFloat("acc", metadata_incoming.acc); + preferences.putFloat("vacc", metadata_incoming.vacc); + preferences.putString("info1", metadata_incoming.info1); + preferences.putFloat("x", metadata_incoming.x); + preferences.putFloat("y", metadata_incoming.y); + preferences.putFloat("z", metadata_incoming.z); + preferences.end(); + return; +} + + +static void load_calibration_info(){ + preferences.begin("config", true); + for (uint8_t i = 0; i < ambit_calibration::CHANNEL_COUNT; ++i){ + adpd_baseline_present[i] = preferences.isKey(ADPD_BASELINE_KEYS[i]); + } + if (preferences.isKey("actinic")){ + const float value = preferences.getFloat("actinic", 0.1); + if (ambit_calibration::valid_actinic_coefficient(value)) ambit_calibration_local.actinic_coef = value; + } + if (preferences.isKey("spec")){ + const float value = preferences.getFloat("spec", 1.0); + if (ambit_calibration::valid_spec_coefficient(value)) ambit_calibration_local.spec_coef = value; + } + if (preferences.isKey("emit")) ambit_calibration_local.mlx_emissivity = preferences.getDouble("emit", 1.0); + if (preferences.isKey("sun")) ambit_calibration_local.sun_coef = preferences.getFloat("sun", 1.0); + if (preferences.isKey("name")) preferences.getString("name", ambit_calibration_local.ambit_name, 20); + if (preferences.isKey("temp_offset")) ambit_calibration_local.temp_offset = preferences.getFloat("temp_offset", 0.0); + if (preferences.isKey("temp_slope")) ambit_calibration_local.temp_slope = preferences.getFloat("temp_slope", 1.0); + + if (preferences.isKey("act_50")) ambit_calibration_local.act_50 = preferences.getUShort("act_50", 1); + if (preferences.isKey("act_100")) ambit_calibration_local.act_100 = preferences.getUShort("act_100", 2); + if (preferences.isKey("act_150")) ambit_calibration_local.act_150 = preferences.getUShort("act_150", 3); + if (preferences.isKey("act_200")) ambit_calibration_local.act_200 = preferences.getUShort("act_200", 4); + if (preferences.isKey("act_250")) ambit_calibration_local.act_250 = preferences.getUShort("act_250", 5); + + if (preferences.isKey("adpd_lit")) ambit_calibration_local.adpd[1] = preferences.getUInt("adpd_lit", 0); + if (preferences.isKey("adpd_dark")) ambit_calibration_local.adpd[0] = preferences.getUInt("adpd_dark", 0); + if (preferences.isKey("adpd_sun")) ambit_calibration_local.adpd[2] = preferences.getUInt("adpd_sun", 0); + if (preferences.isKey("adpd_leaf")) ambit_calibration_local.adpd[3] = preferences.getUInt("adpd_leaf", 0); + if (preferences.isKey("adpd_730")) ambit_calibration_local.adpd[4] = preferences.getUInt("adpd_730", 0); + if (preferences.isKey("adpd_730r")) ambit_calibration_local.adpd[5] = preferences.getUInt("adpd_730r", 0); + preferences.end(); + mlx_read_coe(ambit_calibration_local.mlx_coef); + memcpy(&ambit_calibration_income, &ambit_calibration_local, sizeof(ambit_calibration_info_t)); +} + + +/* Read one ten-element float vector, adopting it only if it is both the right + * size and valid. + * + * The size check is the part that is easy to skip and expensive to debug: + * Preferences::getBytes copies a SHORTER-than-expected stored blob and returns + * the short length, leaving the tail of the destination untouched. Reading + * straight into the live struct would silently blend stored elements with + * compiled-in defaults, with nothing afterwards able to tell which is which. So + * read into scratch, check the returned length, run the predicate, then adopt. + * (getBytes already calls getBytesLength internally and returns the real count, + * so comparing its return value is the single-call form of that check.) + * + * Re-validating on load is calibration_math.h's rule: a value must not be + * accepted over one frontend and rejected after a reboot. Note the adpd + * baselines in load_calibration_info() above are the standing exception — + * validated on save but read back unchecked — which is a bug to fix, not a + * pattern to copy. */ +static void load_spec_vector(const char *key, float *destination, + bool (*predicate)(const float *)){ + if (!preferences.isKey(key)) return; + + float scratch[ambit_calibration::SPEC_CHANNEL_COUNT] = {0.0f}; + if (preferences.getBytes(key, scratch, sizeof(scratch)) != sizeof(scratch)) return; + if (!predicate(scratch)) return; + + memcpy(destination, scratch, sizeof(scratch)); +} + +static void load_spec_calibration(){ + preferences.begin("config", true); + + load_spec_vector(SPEC_OFFSET_KEY, ambit_spec_calibration.spec_offset, + ambit_calibration::valid_spec_offset); + load_spec_vector(SPEC_SENS_KEY, ambit_spec_calibration.spec_sens, + ambit_calibration::valid_spec_sens); + load_spec_vector(PAR_WEIGHT_KEY, ambit_spec_calibration.par_weight, + ambit_calibration::valid_par_weight); + + /* Tier 3 counts as stored if either key was written and survived validation. + * Either alone is a legitimate calibration — a sweep through the origin has + * no intercept to store — so this is an OR, not an AND. */ + ambit_spec_tier3_stored = false; + if (preferences.isKey(PAR_SLOPE_KEY)){ + const float value = preferences.getFloat(PAR_SLOPE_KEY, 1.0f); + if (ambit_calibration::valid_par_slope(value)){ + ambit_spec_calibration.par_slope = value; + ambit_spec_tier3_stored = true; + } + } + if (preferences.isKey(PAR_INTERCEPT_KEY)){ + const float value = preferences.getFloat(PAR_INTERCEPT_KEY, 0.0f); + if (ambit_calibration::valid_par_intercept(value)){ + ambit_spec_calibration.par_intercept = value; + ambit_spec_tier3_stored = true; + } + } + + preferences.end(); +} + + + + + +void load_info_from_nvs(bool print){ + load_metadata(); + if (print){ + Serial.printf("Metadata: lon:%f\tlat:%f\talt:%f\ttime:%d\tacc:%f\tvacc:%f\tinfo1:%s\tx:%f\ty:%f\tz:%f\n", metadata_epprom.lon, metadata_epprom.lat, metadata_epprom.alt, metadata_epprom.time, metadata_epprom.acc, metadata_epprom.vacc, metadata_epprom.info1, metadata_epprom.x, metadata_epprom.y, metadata_epprom.z); + } + load_calibration_info(); + load_spec_calibration(); + if (print){ + // print all ambit_calibration_local + Serial.printf("Calibration: Name:%s\tActinic:%f\tSpec:%f\tEmit:%f\tSun:%f\tTemp_offset:%f\tTemp_slope:%f\n", ambit_calibration_local.ambit_name, ambit_calibration_local.actinic_coef, ambit_calibration_local.spec_coef, ambit_calibration_local.mlx_emissivity, ambit_calibration_local.sun_coef, ambit_calibration_local.temp_offset, ambit_calibration_local.temp_slope); + Serial.printf("Calibration: Act_50:%d\tAct_100:%d\tAct_150:%d\tAct_200:%d\tAct_250:%d\n", ambit_calibration_local.act_50, ambit_calibration_local.act_100, ambit_calibration_local.act_150, ambit_calibration_local.act_200, ambit_calibration_local.act_250); + Serial.printf("Calibration: ADPD: %d\t%d\t%d\t%d\t%d\t%d\n", ambit_calibration_local.adpd[0], ambit_calibration_local.adpd[1], ambit_calibration_local.adpd[2], ambit_calibration_local.adpd[3], ambit_calibration_local.adpd[4], ambit_calibration_local.adpd[5]); + + Serial.printf("MLX: "); + for (int i = 0; i < 14; i++){ + Serial.printf("%d\t", ambit_calibration_local.mlx_coef[i]); + } + Serial.printf("\n"); + } + get_FW_info(); + if (print){ + Serial.printf("FW: MAC:%012llx\tSize:%d\tDate:%s\n", ambit_FW_info.MAC, ambit_FW_info.Size, ambit_FW_info.FW_date); + Serial.printf("FW: %d.%d.%d\n", ambit_FW_info.Major, ambit_FW_info.Minor, ambit_FW_info.Batch); + } + return; +} diff --git a/src/nvs1.h b/src/nvs1.h new file mode 100644 index 0000000..7c59952 --- /dev/null +++ b/src/nvs1.h @@ -0,0 +1,251 @@ +#ifndef _NVS1_H_ +#define _NVS1_H_ +#include +#include "nvs_flash.h" +#include "calibration_math.h" + +// Version is build-injected by tools/version.py (semantic-release tag in CI, +// git describe locally). These fallbacks only apply if the pre-script did not +// run. A post-OTA cmd 33/2 version read confirms an update landed. +#ifndef AMBIT_FW_VERSION +#define AMBIT_FW_VERSION "0.0.0-dev" +#endif +#ifndef AMBIT_FW_MAJOR +#define AMBIT_FW_MAJOR 0 +#endif +#ifndef AMBIT_FW_MINOR +#define AMBIT_FW_MINOR 0 +#endif +#ifndef AMBIT_FW_BATCH +#define AMBIT_FW_BATCH 0 +#endif +// Legacy names, kept because the frozen cmd 33/2 wire struct fields use them. +#define MAJOR_VERSION AMBIT_FW_MAJOR +#define MINOR_VERSION AMBIT_FW_MINOR +#define BATCH_VERSION AMBIT_FW_BATCH + +struct ambit_calibration_info_t +{ + char ambit_name[20] = "AmbitV003"; + int32_t mlx_coef[14] = {0}; + uint32_t adpd[6] = {0}; + float_t temp_offset = 0.0; + float_t temp_slope = 1.0; + float_t actinic_coef = 0.1; + float_t spec_coef = 1.0; + uint16_t act_50 = 5; + uint16_t act_100 = 4; + uint16_t act_150 = 3; + uint16_t act_200 = 2; + uint16_t act_250 = 1; + float_t mlx_emissivity = 1.0; + float_t sun_coef = 1.0; + float_t tick_factor = 0.854; // PAM point-period scale (ms tick -> s); surfaced to the ambyte +}; + +extern struct ambit_calibration_info_t ambit_calibration_local, ambit_calibration_income; + +/* Spectral/PAR calibration — the three-tier chain cmd 35 computes. + * See plans/AMBIT_COMMAND35_SPECPAR.md. + * + * Deliberately NOT part of ambit_calibration_info_t. That struct is memcpy'd + * onto the wire at its full sizeof() by cmd 33 subtype 1, and is mirrored + * field-for-field in the ambyte's ambit_protocol.h under an explicit "MUST match + * ambit-1 nvs1.h — omitting it desyncs the framed read" warning. Growing it by + * 128 bytes would not even fail cleanly: an un-updated ambyte reads its 140 + * bytes correctly and then *scans* for the 0xF0 terminator, so it recovers + * unless one of the extra bytes happens to be 0xF0 — which the all-zero/1.0f + * defaults never are and real fitted coefficients often are. A bench test would + * pass and the field would corrupt later, only on calibrated devices. + * + * This struct is read back through a byte-explicit, versioned layout instead + * (cmd 33 subtype 4), so it can be extended without touching a deployed logger. + * + * Units: tier 1 normalises to basic counts with tint in MILLISECONDS + * (SPEC_TICK_MS), which is the convention every published ams constant uses. + * The coefficients are meaningless under any other one. */ +/* CHANNEL ORDER, for every vector below: F1..F8, NIR, Clear — ambit's reported + * order, set by SPEC_RAW_INDEX. miniPar's tooling (both notebooks and + * as7341_calibrate.py) ends "..., clear, nir", so every vector imported from + * there has ALREADY had its last two elements swapped here. Getting this wrong + * does not fail loudly: NIR and Clear carry the most dissimilar coefficients in + * each vector (5.78 vs 31.57, -37.7 vs +16.9), so a missed swap just produces a + * confidently wrong answer under any spectrum with NIR content. */ +struct ambit_spec_calibration_t +{ + // ams workbook AS7341_AD000198_3-00, sheet "used Correction Values" row 15. + // A property of the silicon, not of the optics, so this one is a real + // calibration rather than a seed. + float spec_offset[ambit_calibration::SPEC_CHANNEL_COUNT] = { + 0.00196979f, // F1 415 + 0.00724927f, // F2 445 + 0.00319381f, // F3 480 + 0.001314659f, // F4 515 + 0.001468153f, // F5 555 + 0.001858105f, // F6 590 + 0.001762778f, // F7 630 + 0.00521704f, // F8 680 + 0.001f, // NIR 910 + 0.003f, // Clear (broadband) + }; + + /* SEED, not an ambit calibration. From the miniPar LR1-B campaign + * (Scripts/spectralCalibration_miniPAR_LR1-B.ipynb): median per-channel + * reference/sensor ratio over 3 devices, where reference is the LR1-B + * irradiance projected onto the channels through the ams reconstruction + * matrix. Per-channel device spread 1.4-9.7%, which is what justifies one + * fleet vector rather than a per-serial lookup. + * + * Ports exactly: miniPar derived it under the same chain computed here — + * basic counts on the ms tick, ams offsets subtracted, then scale — so + * s[i] * spec_sens[i] is the expression that produced the constant. What + * does NOT port is ambit's window and diffuser; replace after an LR1-B + * session on ambit optics. */ + float spec_sens[ambit_calibration::SPEC_CHANNEL_COUNT] = { + 34.950663f, // F1 415 + 65.289484f, // F2 445 + 72.697997f, // F3 480 + 63.273264f, // F4 515 + 56.737110f, // F5 555 + 52.958660f, // F6 590 + 48.706781f, // F7 630 + 42.671670f, // F8 680 + 5.781618f, // NIR 910 + 31.565986f, // Clear (broadband) + }; + + /* SEED, not an ambit calibration. From the miniPar Li-250A campaign + * (new_calibration_miniPAR/regression_PAR_miniPAR.ipynb, exported as + * par_coeffs_fleet.json): lsq_linear of measured PAR on basic counts with + * F1-F8 >= 0 and Clear/NIR free, shrunk toward W_TARGET at lambda=0.03, all + * 462 samples over 6 devices, tier-2 intercept discarded because composing + * the tiers gives PAR = a*(w.x) + (a*b0 + b), so tier 3's intercept absorbs + * it. Leave-one-device-out median 4.38%. + * + * Generation "minipar-2026-08-18-constrained". It REPLACED a plain OLS fit on + * the same samples, whose signs were not physical: negative on F3, F5 and F8 + * and positive on Clear, which is backwards — the visible channels should + * lift PAR and Clear/NIR should subtract stray light. That was collinearity, + * not physics (condition number ~451 on a daylight-dominated set, so OLS + * spreads weight arbitrarily across correlated channels). The bound plus + * shrinkage costs almost nothing in domain (in-sample R^2 0.9982 against + * 0.9984) and buys the extrapolation back: canopy PAR spread 0.4% against + * 6.2%, coefficient direction spread p95 8 deg against 62 deg. The old vector + * had L1 norm 1172 against 457 here for a 1.8x larger net response — 2.6x the + * leverage on spectral shape for the same in-domain answer. + * + * Two things to know before trusting these numbers: + * + * (1) Still a seed, and still miniPar optics. The window and diffuser are not + * ambit's, so AMBIT_PAR_WEIGHT_IS_AMBIT_FIT stays false and cmd 35 keeps + * reporting PAR provisional. The shrinkage target is also still the + * PLACEHOLDER W_TARGET (par_coeffs_fleet.json carries + * "prior_is_placeholder": true), so expect one more generation once the + * CM weight vector is computed. + * + * (2) w was fitted against basic counts with NO dark offset subtracted, + * while this chain computes s = max(0, x - spec_offset). The difference + * is sum(w*offset) = 1.09 umol m-2 s-1 (2.40 under the OLS seed), a + * CONSTANT, so par_intercept absorbs it exactly and nothing needs + * rescaling here. The stronger form of that check — running this chain + * over miniPar's data and refitting tier 3, which returned a = 1.0000 and + * an intercept holding precisely that constant plus miniPar's discarded + * b0 = 7.983 total — was done on the OLS seed. par_coeffs_fleet.json does + * not export the b0 this fit discarded, so only the 1.09 half is known + * for this generation. Which is an argument for fitting the intercept, + * which tier 3 does, not for predicting it. + * + * Replace after a Li-250A campaign on ambit hardware, keeping the sign + * constraint, then set AMBIT_PAR_WEIGHT_IS_AMBIT_FIT. */ + float par_weight[ambit_calibration::SPEC_CHANNEL_COUNT] = { + 40.8063026f, // F1 415 + 42.1780116f, // F2 445 + 69.8493647f, // F3 480 + 85.8748691f, // F4 515 + 61.8955888f, // F5 555 + 43.742399f, // F6 590 + 51.5713801f, // F7 630 + 14.7054614f, // F8 680 + -27.2526003f, // NIR 910 + 19.3083196f, // Clear (broadband) + }; + + /* Tier 3, per device, from an intensity sweep against a Li-250A. Deliberately + * NOT seeded from miniPar: this is the term that carries optical throughput, + * and ambit's window and diffuser are not miniPar's. Until a sweep is run, + * par reports par_tier2 unscaled — it tracks light correctly but its + * MAGNITUDE is not meaningful, which is what flags bit9 exists to say. + * + * par_slope is spec_coef's successor, not a rename: spec_coef scales a + * raw-count integer-weighted PAR and stays with cmd 31. */ + float par_slope = 1.0f; + float par_intercept = 0.0f; +}; + +extern struct ambit_spec_calibration_t ambit_spec_calibration; + +/* Is the compiled-in par_weight an ambit fleet fit, or a borrowed seed? + * + * Flip to true in the same commit that lands a par_weight fitted against a + * Li-250A on ambit optics (plans/AMBIT_COMMAND35_SPECPAR.md §11). Until then cmd + * 35 must not claim tier 2 is calibrated: a seeded vector produces a PAR of the + * right order that was measured on a different window and diffuser, and the + * whole point of the flag is that this is not silently indistinguishable from + * the real thing. */ +static constexpr bool AMBIT_PAR_WEIGHT_IS_AMBIT_FIT = false; + +/* Has this device been through a tier-3 intensity sweep? Set by + * load_spec_calibration() from NVS key presence and by the tier-3 setters on a + * successful write — key presence, not a float comparison against the default, + * because a fitted slope of exactly 1.0 is legitimate. */ +extern bool ambit_spec_tier3_stored; + +struct ambit_FW_info_t +{ + uint8_t Major = MAJOR_VERSION; + uint8_t Minor = MINOR_VERSION; + uint8_t Batch = BATCH_VERSION; + uint32_t Size = 0; + uint64_t MAC = 0; + char FW_date[12]; + // hw_rev occupies the first byte of what was reserved[12]: same offsets, same + // 48-byte total, so the frozen cmd 33/2 wire layout is unchanged (old readers + // never looked at these bytes). 0 = unknown; NVS "config"/"hw_rev" once boards + // start being programmed with a hardware revision. + uint8_t hw_rev = 0; + char reserved[11]; + uint8_t Checksum = 0; +}; + +extern struct ambit_FW_info_t ambit_FW_info; + +struct metadata_t +{ + double lon = 1.0; + double lat = 1.0; + float alt = 1.0; + float acc = 1.0; + float vacc = 1.0; + uint32_t time = 0; + float x = 0.0; + float y = 0.0; + float z = 0.0; + char info1[200] = "New_Ambit"; + uint16_t EOF_MARK = 2025; // end of file marker, used to check if the metadata is valid +}; + +extern struct metadata_t metadata_epprom, metadata_incoming; + +void load_info_from_nvs(bool print); +void save_metadata(void); +esp_err_t save_adpd_baseline(const uint32_t values[ambit_calibration::CHANNEL_COUNT]); +esp_err_t save_actinic_coefficient(float value); +esp_err_t save_spec_coefficient(float value); +esp_err_t save_spec_offset(const float values[ambit_calibration::SPEC_CHANNEL_COUNT]); +esp_err_t save_spec_sens(const float values[ambit_calibration::SPEC_CHANNEL_COUNT]); +esp_err_t save_par_weight(const float values[ambit_calibration::SPEC_CHANNEL_COUNT]); +esp_err_t save_par_slope(float value); +esp_err_t save_par_intercept(float value); +uint32_t apply_adpd_calibration(uint8_t channel, uint32_t sample); + +#endif // _NVS1_H_ diff --git a/src/run_esp.cpp b/src/run_esp.cpp new file mode 100644 index 0000000..cc24389 --- /dev/null +++ b/src/run_esp.cpp @@ -0,0 +1,659 @@ +#include +#include "data_utils.h" +#include "src/as7341/spec_meas.h" +#include "src/mlx90632/u_mlx.h" +#include "PAM.h" +#include "core.h" +#include +#include "nvs1.h" +#include +#include "esp_ota_ops.h" + +#define TAG "ESP" +#define ESP_CMD_HEADER 160 +#define ESP_CMD_DONE 161 +#define ESP_CMD_END 240 +#define ESP_WAKE_FOR_CMD 170 +#define MAX_ARR_LEN 16 + + + + + +// Config is the single global owned by PAM (adpd_current_config / adpd_gains_config). +// The binary frontend reads/writes the SAME struct as the text frontend, so a value +// set over one transport is applied by a run triggered over the other. +extern Preferences preferences; + +extern uint8_t CONNECTION_TYPE; +int serial_read_until(uint8_t target1, uint8_t target2 = 0, uint8_t target3 = 0, uint16_t timeout = 20, bool remove = false); + +// ── OTA-over-UART receiver state (cmds 25-28) ─────────────────────────────── +// The ambyte streams a new firmware image in CRC16-checked, sequenced chunks; we +// write each straight into the spare OTA partition via Arduino's Update, then +// reboot into it on OTA_END. There is no HW watchdog here, so multi-ms flash +// writes are safe; USB-Serial-JTAG (GPIO18/19) is the recovery path for a bad +// image. Update verifies the whole image (MD5/size) in end() before switching. +static uint16_t ota_expected_seq = 0; + +static uint16_t crc16_ccitt(const uint8_t *data, size_t len) +{ + uint16_t crc = 0xFFFF; + for (size_t i = 0; i < len; i++) { + crc ^= (uint16_t)data[i] << 8; + for (uint8_t b = 0; b < 8; b++) + crc = (crc & 0x8000) ? (uint16_t)((crc << 1) ^ 0x1021) : (uint16_t)(crc << 1); + } + return crc; +} + +/* Rollback safety: defer OTA image confirmation. The Arduino core auto-marks a + * freshly-OTA'd PENDING_VERIFY image valid at boot unless this weak hook returns + * true. We return true so a new image stays UNCONFIRMED until the ambyte sends + * OTA_CONFIRM (cmd 29) — i.e. until the ambyte has seen the rebooted image + * answer. If it never confirms (bad/unreachable image), the bootloader + * (CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE) rolls back to the previous image on + * the next reboot. A USB-flashed image is VALID (not pending), so unaffected. */ +extern "C" bool verifyRollbackLater() { return true; } + +// extern char ambit_name[]; +// float_t actinic_coef = 1.0; +// float_t spec_coef = 1.0; +// extern double mlx_emissivity; + + +int do_esp_cmd(){ + uint8_t cmd_arr[8], c, ret; + ESP_LOGV(TAG, "parse ESP commands"); + + //search for cmd header + ret = serial_read_until(ESP_CMD_HEADER, 0, 0, 100, true); + if (ret != 1){ + ESP_LOGE(TAG, "ESP cmd header failed"); + return -1; + } + + CONNECTION_TYPE = CONNECTION_TYPES::AMBYTE; + + // the real cmd will follows + c = Serial.readBytes(cmd_arr, 8); + if (c < 8){ + ESP_LOGE(TAG, "ESP cmd parse failed"); + return -1; + } + //Serial.printf("cmd is %d, %d, %d, %d, %d, %d, %d, %d\n", cmd_arr[0], cmd_arr[1], cmd_arr[2], cmd_arr[3], cmd_arr[4], cmd_arr[5], cmd_arr[6], cmd_arr[7]); + + switch (cmd_arr[0]) + { + case 1: // set PD gains + // wire convention: gains 1-indexed (1..6), value outside that = "no change" + core_set_gains( + (cmd_arr[1] > 0 && cmd_arr[1] < 7) ? cmd_arr[1] - 1 : adpd_gains_config.Fluo, + (cmd_arr[2] > 0 && cmd_arr[2] < 7) ? cmd_arr[2] - 1 : adpd_gains_config.FluoRef, + (cmd_arr[3] > 0 && cmd_arr[3] < 7) ? cmd_arr[3] - 1 : adpd_gains_config.IR, + (cmd_arr[4] > 0 && cmd_arr[4] < 7) ? cmd_arr[4] - 1 : adpd_gains_config.IRRef, + (cmd_arr[5] > 0 && cmd_arr[5] < 7) ? cmd_arr[5] - 1 : adpd_gains_config.Sun, + (cmd_arr[6] > 0 && cmd_arr[6] < 7) ? cmd_arr[6] - 1 : adpd_gains_config.Leaf); + ESP_LOGV(TAG, "gains are %d, %d, %d, %d, %d, %d", adpd_gains_config.Fluo, adpd_gains_config.FluoRef, adpd_gains_config.IR, adpd_gains_config.IRRef, adpd_gains_config.Sun, adpd_gains_config.Leaf); + Serial.write(ESP_CMD_DONE); + break; + + case 2: // set currents + // wire convention: current byte >= 127 = "no change" + core_set_currents( + (cmd_arr[1] < 127) ? cmd_arr[1] : adpd_current_config.I620, + (cmd_arr[2] < 127) ? cmd_arr[2] : adpd_current_config.I720, + (cmd_arr[3] < 127) ? cmd_arr[3] : adpd_current_config.IR); + ESP_LOGV(TAG, "currents are %d, %d, %d", adpd_current_config.I620, adpd_current_config.I720, adpd_current_config.IR); + Serial.write(ESP_CMD_DONE); + break; + + case 10: // array run config + core_config_detector(); // applies the global config + enters array mode + Serial.write(ESP_CMD_DONE); + break; + + case 20: // run mpf + { + if (adpd_gains_config.init == false) ESP_LOGE(TAG, "Gain preset not initized, use default!"); + if (adpd_current_config.init == false) ESP_LOGE(TAG, "Current preset not initized, use default!"); + + uint16_t length = (((uint16_t) cmd_arr[1]) << 7) + cmd_arr[2]; + uint8_t interval = cmd_arr[3]; + bool change_act = (bool) cmd_arr[4]; + uint8_t act = cmd_arr[5]; + + //Serial.printf("%d, %d, %d, %d\n", length, interval, change_act, act); + + Serial.write(ESP_CMD_DONE); + core_run_mpf(length, interval, change_act, act); + Serial.write(ESP_CMD_END); + } + break; + + case 21:// run + { + uint8_t arr_length = cmd_arr[1]; + uint8_t led_persist = cmd_arr[2]; + bool allow_interrupt = (bool) cmd_arr[3]; + + uint8_t cc = 0; + if ((arr_length == 0) || (arr_length > MAX_ARR_LEN)){ + ESP_LOGE(TAG, "run array wrong length: %d", arr_length); + break; + } + uint8_t run_arr[arr_length * 8]; + core_ensure_array_config(); + + cc = Serial.readBytes(run_arr, arr_length * 8); + if (cc != (arr_length * 8)){ + ESP_LOGE(TAG, "run array elements count %d not match config %d", cc, arr_length); + break; + } + Serial.write(ESP_CMD_DONE); + run_arr_type1(arr_length, run_arr, led_persist, allow_interrupt); + Serial.write(ESP_CMD_END); + + } + break; + + // ── Parallel measurement protocol (trigger → poll → fetch) ────────────── + case 22: // run start (async): ack, then run to completion into retained + // buffers. Same payload as cmd 21. The host gets CMD_DONE and moves + // on to the next sensor; no CMD_END (host trigger is ACK_ONLY). + { + uint8_t arr_length = cmd_arr[1]; + uint8_t led_persist = cmd_arr[2]; + bool allow_interrupt = (bool) cmd_arr[3]; + + if ((arr_length == 0) || (arr_length > MAX_ARR_LEN)){ + ESP_LOGE(TAG, "run start wrong length: %d", arr_length); + break; + } + uint8_t run_arr[arr_length * 8]; + core_ensure_array_config(); + + uint8_t cc = Serial.readBytes(run_arr, arr_length * 8); + if (cc != (arr_length * 8)){ + ESP_LOGE(TAG, "run start elements count %d not match config %d", cc, arr_length); + break; + } + Serial.write(ESP_CMD_DONE); + Serial.flush(); // ack must reach the host before the line goes quiet for the run + ambit_async_run_start(arr_length, run_arr, led_persist, allow_interrupt); + } + break; + + case 23: // poll async state -> 1 status byte (0 IDLE | 1 DONE | 2 ERROR) + { + Serial.write(ESP_CMD_DONE); + Serial.write(ambit_async_get_state()); + Serial.write(ESP_CMD_END); + } + break; + + case 24: // fetch retained async result: stream arrays over the AMBYTE FSM + { + Serial.write(ESP_CMD_DONE); + ambit_async_fetch(); // streams the held arrays, or nothing if none ready + Serial.write(ESP_CMD_END); + } + break; + + case 31: // get spec + { + uint16_t spec[12] = {0}; + float par = get_PAR(spec) * ambit_calibration_local.spec_coef; + memcpy(spec + 10, &par, 4); + Serial.write(ESP_CMD_DONE); + Serial.write((uint8_t*) spec, 24); + Serial.write(ESP_CMD_END); + } + break; + + case 32: // get temp + { + double leaf, chip; + mlx_measure(&leaf, &chip); + Serial.write(ESP_CMD_DONE); + int16_t t1 = (int16_t) (leaf * 10); + int16_t t2 = (int16_t) (chip * 10); + Serial.write((uint8_t*) (&t1), 2); + Serial.write((uint8_t*) (&t2), 2); + Serial.write(ESP_CMD_END); + } + break; + + case 33: // retrieve ambit info + { + if (cmd_arr[1] == 1){ // send calibration info + Serial.write(ESP_CMD_DONE); + Serial.write((uint8_t*) &ambit_calibration_local, sizeof(ambit_calibration_info_t)); + Serial.write(ESP_CMD_END); + }else if (cmd_arr[1] == 2){ // send FW info + Serial.write(ESP_CMD_DONE); + Serial.write((uint8_t*) &ambit_FW_info, sizeof(ambit_FW_info_t)); + Serial.write(ESP_CMD_END); + }else if (cmd_arr[1] == 3){ // send metadata + Serial.write(ESP_CMD_DONE); + Serial.write((uint8_t*) &metadata_epprom, sizeof(metadata_t)); + Serial.write(ESP_CMD_END); + }else if (cmd_arr[1] == 4){ // send spectral/PAR calibration + /* Subtype 4 is what makes cmd 35's raw[] worth carrying: without the + * coefficients on the wire a host cannot recompute the chain, so it + * cannot tell a firmware bug from a bad NVS vector. Additive and safe + * against older images — they fall into the else below and answer a + * bare ESP_CMD_END rather than going silent. + * + * Byte-explicit and versioned rather than a struct blit, which is + * exactly why these fields do not live in ambit_calibration_info_t: + * subtypes 1/2/3 are frozen at 140/48/248 by their C layout and can + * never grow, this one can. All little-endian: + * + * 0 u8 format = 1 + * 1 u8 reserved + * 2 u16 reserved + * 4 f32 spec_offset[10] + * 44 f32 spec_sens[10] + * 84 f32 par_weight[10] + * 124 f32 par_slope + * 128 f32 par_intercept + * + * Tier 1 normalises with tint in MILLISECONDS; spec_offset is in + * those basic-count units and the vectors mean nothing without it. */ + const size_t vec = sizeof(float) * ambit_calibration::SPEC_CHANNEL_COUNT; + uint8_t payload[132] = {0}; + payload[0] = 1; + memcpy(payload + 4, ambit_spec_calibration.spec_offset, vec); + memcpy(payload + 44, ambit_spec_calibration.spec_sens, vec); + memcpy(payload + 84, ambit_spec_calibration.par_weight, vec); + memcpy(payload + 124, &ambit_spec_calibration.par_slope, 4); + memcpy(payload + 128, &ambit_spec_calibration.par_intercept, 4); + + Serial.write(ESP_CMD_DONE); + Serial.write(payload, sizeof(payload)); + Serial.write(ESP_CMD_END); + }else{ + Serial.write(ESP_CMD_END); + } + } + break; + + + + + case 34: // get temp and raw + { + double leaf, leaf_1, chip; + int16_t a1, a2, a3, a4; + mlx_measure(&leaf, &chip, &leaf_1, &a1, &a2, &a3, &a4); + Serial.write(ESP_CMD_DONE); + int16_t t1 = (int16_t) (leaf * 10); + int16_t t2 = (int16_t) (leaf_1 * 10); + int16_t t3 = (int16_t) (chip * 10); + Serial.write((uint8_t*) (&t1), 2); + Serial.write((uint8_t*) (&t2), 2); + Serial.write((uint8_t*) (&t3), 2); + Serial.write((uint8_t*) (&a1), 2); + Serial.write((uint8_t*) (&a2), 2); + Serial.write((uint8_t*) (&a3), 2); + Serial.write((uint8_t*) (&a4), 2); + Serial.write(ESP_CMD_END); + } + break; + + case 35: // get_spec_raw: unscaled counts, the parameters they were taken + // under, and the three-tier computed spectral/PAR values + { + /* Additive with respect to cmd 31, which keeps its exact 24-byte payload + * for deployed ambytes that have no way to negotiate. This command's own + * payload was redefined before it ever shipped (no tag contains it, and + * the ambyte protocol header has no opcode 35), so `format` stays 1 + * rather than burning a value on a layout no host ever observed. + * + * Layout is byte-explicit and naturally aligned (u16 on even offsets, + * f32 on multiples of 4) so it is padding-free without + * __attribute__((packed)) and without inheriting the ESP32-default- + * alignment coupling that froze the cmd 33 structs at 140/48/248. + * All fields little-endian: + * + * 0 u8 format = 1 bump only for a layout change + * 1 u8 atime + * 2 u8 gain_low as7341_gain_t ORDINAL, F1-F4 bank + * 3 u8 gain_high as7341_gain_t ORDINAL, F5-F8 + NIR + Clear + * 4 u16 astep + * 6 u16 flags Two zones with OPPOSITE polarity, kept in + * separate bytes so neither can be read as the + * other. Both fail safe: an all-zero flags word + * means "no fault reported, nothing confirmed + * calibrated", which is the pessimistic reading + * a truncated or zeroed frame should produce. + * + * low byte — conditions, 1 = needs attention: + * bit0 = a channel hit digital full scale + * bit1 = a channel clipped at its dark offset + * bit2 = AS7341 analog saturation (ASAT) + * bit3 = acquisition fault (I2C read failed) + * bits 4-7 reserved, sent as 0 + * + * high byte — calibration, 1 = confirmed: + * bit8 = par_weight is an ambit fleet fit + * (0 = borrowed seed, PAR provisional) + * bit9 = tier-3 slope/intercept stored for + * this device (0 = never swept) + * bits 10-15 reserved, sent as 0 + * + * A host that wants one "is this PAR + * trustworthy" test checks that BOTH high bits + * are set; treating an unset bit as trustworthy + * is the failure this zoning exists to prevent. + * 8 u16 sat_mask bit i = channel i at digital full scale + * 10 u16 clip_mask bit i = channel i clipped at its offset + * 12 u16 raw[10] unscaled counts: F1..F8, NIR, Clear + * 32 f32 chan[10] goal A: normalised, offset-corrected, + * spectrally scaled + * 72 f32 par goal B: par_slope * t2 + par_intercept + * 76 f32 par_tier2 goal B: t2, before slope/intercept + * + * raw[] stays on the wire so a host can recompute the whole chain and + * *verify* the firmware rather than trust it; cmd 33 subtype 4 returns + * the coefficients that recomputation needs. Full scale is + * (atime+1)*(astep+1), so raw[] cannot overflow its u16 — the original + * point of the command. + * + * The gain bytes are enum ORDINALS (n means 0.5 * 2^n), not multipliers: + * see as7341_gain_multiplier(). NIR and Clear come from the high SMUX + * read and so divide by gain_high, whatever the bank names suggest. + * + * par is NOT cmd 31's par any more. spec_coef stays with cmd 31; here + * tier 3's par_slope/par_intercept replace it. */ + spec_raw_t raw; + (void) get_PAR_raw(&raw); // legacy PAR belongs to cmd 31; not reported here + + const float tint_ms = spec_tint_ms(raw.atime, raw.astep); + const float gain_lo = as7341_gain_multiplier(raw.gain_low); + const float gain_hi = as7341_gain_multiplier(raw.gain_high); + + float chan[ambit_calibration::SPEC_CHANNEL_COUNT] = {0.0f}; + float par_tier2 = 0.0f; + uint16_t clip_mask = 0; + + for (uint8_t i = 0; i < ambit_calibration::SPEC_CHANNEL_COUNT; i++){ + /* Slots 0-3 are F1-F4 from the low read; 4-9 are F5-F8, NIR and + * Clear, all from the high read. Neither divisor can be zero: the + * gain multiplier bottoms out at 0.5x and tint at one tick. */ + const float gain = (i < 4) ? gain_lo : gain_hi; + const float x = (float) raw.raw[i] / (gain * tint_ms); // tier 1 + + float s = x - ambit_spec_calibration.spec_offset[i]; + if (s < 0.0f){ + /* The offset clip is the only nonlinearity in the chain, so + * report where it bit rather than letting a floor pass for a + * reading. */ + s = 0.0f; + clip_mask |= (uint16_t) (1U << i); + } + + chan[i] = s * ambit_spec_calibration.spec_sens[i]; // goal A + par_tier2 += ambit_spec_calibration.par_weight[i] * s; // goal B t2 + } + + /* PAR comes from s, never from chan[]: folding spec_sens into PAR would + * mean a future goal-A recalibration silently shifts PAR on every + * deployed device with no PAR measurement having changed. */ + const float par = ambit_spec_calibration.par_slope * par_tier2 + + ambit_spec_calibration.par_intercept; // goal B t3 + + uint16_t flags = 0; + if (raw.sat_mask != 0) flags |= 1U << 0; + if (clip_mask != 0) flags |= 1U << 1; + if (raw.analog_saturated) flags |= 1U << 2; + if (raw.fault) flags |= 1U << 3; + + /* Asserted only when the tier-2 vector is genuinely an ambit fit AND it + * is not all-zero. The second test matters because a host can write a + * zero vector over the setter: that collapses par to par_intercept, and + * a build claiming an ambit fit must not vouch for it. */ + if (AMBIT_PAR_WEIGHT_IS_AMBIT_FIT && + !ambit_calibration::par_weight_is_unset(ambit_spec_calibration.par_weight)){ + flags |= 1U << 8; + } + if (ambit_spec_tier3_stored) flags |= 1U << 9; + + uint8_t payload[80] = {0}; + payload[0] = 1; + payload[1] = raw.atime; + payload[2] = raw.gain_low; + payload[3] = raw.gain_high; + memcpy(payload + 4, &raw.astep, 2); + memcpy(payload + 6, &flags, 2); + memcpy(payload + 8, &raw.sat_mask, 2); + memcpy(payload + 10, &clip_mask, 2); + memcpy(payload + 12, raw.raw, sizeof(raw.raw)); + memcpy(payload + 32, chan, sizeof(chan)); + memcpy(payload + 72, &par, 4); + memcpy(payload + 76, &par_tier2, 4); + + Serial.write(ESP_CMD_DONE); + Serial.write(payload, sizeof(payload)); + Serial.write(ESP_CMD_END); + } + break; + + + case 37: // set metadata + { + Serial.write(ESP_CMD_DONE); + Serial.readBytes((uint8_t*) (&metadata_incoming), sizeof(metadata_t)); + Serial.write(ESP_CMD_END); + if (metadata_incoming.EOF_MARK == 2025) save_metadata(); + load_info_from_nvs(false); + } + break; + + + + case 4: // try/set actinic + { + AS_LED_OFF(); + Serial.write(ESP_CMD_DONE); + uint8_t type = cmd_arr[1]; + uint8_t var = cmd_arr[2]; + uint8_t var2 = cmd_arr[3]; + float_t _factor = 1.0; + if (type == 1){ // try actinics + AS_LED_Current(50); + AS_LED_ON(); + delay(3000); + AS_LED_Current(var); + delay(3000); + AS_LED_OFF(); + AS_LED_Current(0); + }else if (type == 2){ // set actinic offset + _factor = *((float *) &(cmd_arr[3])); + if (ambit_calibration::valid_actinic_coefficient(_factor)){ + const esp_err_t err = save_actinic_coefficient(_factor); + if (err != ESP_OK) ESP_LOGE(TAG, "Actinic coefficient save failed: %s", esp_err_to_name(err)); + } + }else if (type == 4){ // set spectral/PAR coefficient + _factor = *((float *) &(cmd_arr[3])); + if (ambit_calibration::valid_spec_coefficient(_factor)){ + // Frozen cmd-4 framing carries no status byte. Persist and + // verify first; only a successful commit may alter runtime. + const esp_err_t err = save_spec_coefficient(_factor); + if (err != ESP_OK) ESP_LOGE(TAG, "PAR coefficient save failed: %s", esp_err_to_name(err)); + } + }else if (type == 5){ + AS_LED_Current(var); + AS_LED_ON(); + delay(var2 * 100); + AS_LED_OFF(); + } + Serial.write(ESP_CMD_END); + } + break; + + + case 5: + { + uint8_t ambit_id = cmd_arr[1]; + uint8_t intensity = cmd_arr[2]; + Serial.write(ESP_CMD_DONE); + if ((ambit_id < 4) && (intensity > 4) && (intensity < 254)) as7431_blink(ambit_id, intensity); + Serial.write(ESP_CMD_END); + } + break; + + case 6: // do adpd baseline flash + { + // Frozen Ambyte semantics are deliberately silent: DONE then END with + // no result/status payload, even when acquisition or NVS persistence + // fails. Never add bytes here; the Calibratron text path is authoritative. + Serial.write(ESP_CMD_DONE); + uint32_t ret[6] = {0}; + const int acquisition_err = fluor_offset(ret); + if (acquisition_err == 0){ + const esp_err_t baseline_err = save_adpd_baseline(ret); + if (baseline_err != ESP_OK) ESP_LOGE(TAG, "ADPD baseline save failed: %s", esp_err_to_name(baseline_err)); + }else{ + ESP_LOGE(TAG, "ADPD baseline acquisition failed: %d", acquisition_err); + } + Serial.write(ESP_CMD_END); + + } + break; + + case 17: // nvs update scalar + { + uint8_t type = cmd_arr[1]; // 1: actinic + uint8_t dtype = cmd_arr[2]; // 1: float + if ((type == 1) && (dtype == 1)){ // update actinic coef + Serial.write(ESP_CMD_DONE); + float_t _factor = *((float *) &(cmd_arr[3])); + if (ambit_calibration::valid_actinic_coefficient(_factor)){ + const esp_err_t err = save_actinic_coefficient(_factor); + if (err == ESP_OK) load_info_from_nvs(false); + else ESP_LOGE(TAG, "Actinic coefficient save failed: %s", esp_err_to_name(err)); + } + Serial.write(ESP_CMD_END); + } + + } + break; + + case 18: // nvs update array + { + uint8_t type = cmd_arr[1]; // 1: actinic linear test + float_t _factor = 1.0; + if (type == 1){ // update actinic linear readings + uint16_t _readingsf[6] = {0}; + Serial.write(ESP_CMD_DONE); + Serial.readBytes((uint8_t*) _readingsf, 12); + uint16_t checksum = 0; + _factor = *((float *) &(cmd_arr[4])); + for (int i = 0; i < 5; i++){ + checksum += _readingsf[i]; + } + if (checksum == _readingsf[5] && + ambit_calibration::valid_actinic_coefficient(_factor)){ + preferences.begin("config", false); + preferences.putUShort("act_50", _readingsf[0]); + preferences.putUShort("act_100", _readingsf[1]); + preferences.putUShort("act_150", _readingsf[2]); + preferences.putUShort("act_200", _readingsf[3]); + preferences.putUShort("act_250", _readingsf[4]); + preferences.putFloat("actinic", _factor); + preferences.end(); + load_info_from_nvs(false); + } + Serial.write(ESP_CMD_END); + } + + } + break; + + + + + + + // ── OTA-over-UART (cmds 25-28): host streams a new image; write via Update ── + case 25: // OTA_BEGIN: cmd_arr[1..4] = total image size (LE u32) + { + uint32_t size = (uint32_t)cmd_arr[1] | ((uint32_t)cmd_arr[2] << 8) | + ((uint32_t)cmd_arr[3] << 16) | ((uint32_t)cmd_arr[4] << 24); + if (Update.isRunning()) Update.abort(); + bool ok = (size > 0) && Update.begin(size); // selects the next OTA partition + erases it + ota_expected_seq = 0; + Serial.write(ESP_CMD_DONE); + Serial.write(ok ? 0 : 1); // 0 ok | 1 begin-fail (size/partition) + Serial.write(ESP_CMD_END); + } + break; + + case 26: // OTA_DATA: cmd_arr[1]=len(1..200), cmd_arr[2..3]=seq(LE u16); + // extra payload = len data bytes + 2-byte CRC16-CCITT(LE) over the data + { + uint8_t len = cmd_arr[1]; + uint16_t seq = (uint16_t)cmd_arr[2] | ((uint16_t)cmd_arr[3] << 8); + uint8_t status; + if (len == 0 || len > 200) { + status = 4; // bad length (no payload consumed) + } else { + uint8_t buf[202]; + size_t got = Serial.readBytes(buf, (size_t)len + 2); + if (got != (size_t)len + 2) status = 5; // short read + else { + uint16_t rx_crc = (uint16_t)buf[len] | ((uint16_t)buf[len + 1] << 8); + if (crc16_ccitt(buf, len) != rx_crc) status = 1; // CRC fail -> host resends same seq + else if (seq != ota_expected_seq) status = 2; // out-of-order (desync) + else if (!Update.isRunning()) status = 6; // no OTA_BEGIN + else if (Update.write(buf, len) != len) status = 3; // flash write fail + else { ota_expected_seq++; status = 0; } // accepted + } + } + Serial.write(ESP_CMD_DONE); + Serial.write(status); + Serial.write(ESP_CMD_END); + } + break; + + case 27: // OTA_END: finalize + verify image + set boot partition, then reboot into it + { + bool ok = Update.isRunning() && Update.end(); // end() requires the full image (MD5/size check) + Serial.write(ESP_CMD_DONE); + Serial.write(ok ? 0 : 1); // 0 ok (about to reboot) | 1 verify-fail + Serial.write(ESP_CMD_END); + Serial.flush(); // ack must reach the host before we restart + if (ok) { delay(100); ESP.restart(); } // boots the freshly-written slot; no return + } + break; + + case 28: // OTA_ABORT: discard a partial update, stay on the current image + { + if (Update.isRunning()) Update.abort(); + ota_expected_seq = 0; + Serial.write(ESP_CMD_DONE); + Serial.write(0); + Serial.write(ESP_CMD_END); + } + break; + + case 29: // OTA_CONFIRM: mark the running (PENDING_VERIFY) image valid — cancels + // the pending rollback. Sent by the ambyte once it has seen this image + // answer after the OTA reboot. A no-op error if not pending (already valid). + { + esp_err_t err = esp_ota_mark_app_valid_cancel_rollback(); + Serial.write(ESP_CMD_DONE); + Serial.write(err == ESP_OK ? 0 : 1); + Serial.write(ESP_CMD_END); + } + break; + + default: + ESP_LOGE(TAG, "Bad command"); + break; + } + + return 0; +} diff --git a/src/self_test.cpp b/src/self_test.cpp new file mode 100644 index 0000000..e0e87f5 --- /dev/null +++ b/src/self_test.cpp @@ -0,0 +1,169 @@ +#include +#include "PAM.h" +#include "src/devices_init.h" +#include "src/adpd/u_adpd6100.h" +#include "src/as7341/spec_meas.h" +#include "src/mlx90632/u_mlx.h" + + +extern ADPD6 adpd; +static char STR_SUC[] = "Success"; +static char STR_FAIL[] = "Failed"; + + +char* str_results[3] = {STR_FAIL, STR_SUC, STR_FAIL}; + + +static int check_adpd(){ + uint8_t ret = (uint8_t)adpd.begin(); + if (ret < 2) Serial.printf("ADPD check %s", str_results[ret]); + return ret; +} + +static int check_spec(){ + uint8_t ret = (uint8_t)check_AS7341(); + if (ret < 2) Serial.printf("AS7341 check %s", str_results[ret]); + return ret; +} + + +static int check_mlx(){ + uint8_t ret = (uint8_t)mlx_init(); + if (ret < 2) Serial.printf("MLX90632 check %s", str_results[ret]); + return ret; +} + + +static int test_optic_path(){ + adpd.STOP(); + conf_slow_FR_1(40, 20, 0, 5, 5, 5, 5, 5, 3); + + adpd.num_ts(3); + uint8_t expected_readout_bytes = 24; + uint8_t expected_readout = 8; + uint32_t ret[expected_readout] = {0}; + uint16_t fifo_c = 0; + uint32_t counter = 0; + uint32_t num_ptx = 500; + + AS_LED_OFF(); + AS_LED_Current(50); + adpd.run_freq(50); + + adpd.RUN(); + while (counter < num_ptx){ + fifo_c = adpd.fifo_count(); + while (fifo_c >= expected_readout_bytes){ // read all bytes from FIFO + adpd.readfifo(expected_readout, 3, ret); + fifo_c -= expected_readout_bytes; + if (counter == num_ptx) break; + Serial.printf("%d,%d,%d,%d,%d,%d,%d,%d\n", ret[0],ret[1],ret[2],ret[3],ret[4],ret[5],ret[6],ret[7]); + counter++; + if (counter == 250) AS_LED_ON(); + if (counter == 480) AS_LED_OFF(); + } + } + + adpd.STOP(); + AS_LED_OFF(); + AS_LED_Current(0); + return 0; +} + + + + +int check_connections(){ + // check_adpd(); + // check_spec(); + // check_mlx(); + double temperature_before, temperature_after, t; + + + mlx_measure(&t, &temperature_before); + test_optic_path(); + mlx_measure(&t, &temperature_after); + + + Serial.printf("#obj:%f-%f-%f\n", t, temperature_before, temperature_after); + + + return 0; +} + + +int optic_test(){ + adpd.STOP(); + conf_slow_FR_1(100, 20, 0, 1, 5, 5, 5, 5, 1); + + adpd.num_ts(3); + uint8_t expected_readout_bytes = 24; + uint8_t expected_readout = 8; + uint32_t ret[expected_readout] = {0}; + uint16_t fifo_c = 0; + uint32_t counter = 0; + uint32_t num_ptx = 2000; + uint32_t sig = 0; + + AS_LED_OFF(); + AS_LED_Current(50); + adpd.run_freq(25); + + adpd.RUN(); + while (counter < num_ptx){ + fifo_c = adpd.fifo_count(); + while (fifo_c >= expected_readout_bytes){ // read all bytes from FIFO + adpd.readfifo(expected_readout, 3, ret); + fifo_c -= expected_readout_bytes; + if (counter == num_ptx) break; + sig = calc_signal(ret[2], ret[3], 1); + Serial.printf("%d,%d,%d,%d,%d,%d,%d\n", ret[0]-65000,ret[1]-65000,ret[2]-16000,ret[3]-16000,ret[4]-16000,ret[5]-16000,sig); + counter++; + } + } + + adpd.STOP(); + AS_LED_OFF(); + AS_LED_Current(0); + return 0; +} + + +int optic_test(uint8_t current, uint8_t num_integ, uint8_t lit_offset, uint8_t dark1_offset, uint8_t dark2_offset, uint8_t pulse_offset, uint8_t pulse_duration){ + adpd.STOP(); + fluor_offset_test(current, num_integ, lit_offset, dark1_offset, dark2_offset, pulse_offset, pulse_duration); + + adpd.num_ts(1); + uint8_t expected_readout_bytes = 12; + uint8_t expected_readout = 4; + uint32_t ret[expected_readout] = {0}; + uint16_t fifo_c = 0; + uint32_t counter = 0; + uint32_t num_ptx = 300; + uint32_t sig = 0; + + AS_LED_OFF(); + AS_LED_Current(100); + adpd.run_freq(25); + + adpd.RUN(); + while (counter < num_ptx){ + fifo_c = adpd.fifo_count(); + while (fifo_c >= expected_readout_bytes){ // read all bytes from FIFO + adpd.readfifo(expected_readout, 3, ret); + fifo_c -= expected_readout_bytes; + if (counter == num_ptx) break; + if (counter == 100) AS_LED_ON(); + if (counter == 200) AS_LED_OFF(); + sig = calc_signal(ret[0], ret[1], num_integ); + Serial.printf("%d,%d,%d,%d,%d\n", ret[0]-16000*num_integ, ret[1]-16000*num_integ,ret[2]-16000*num_integ,ret[3]-16000*num_integ, ret[1] - ret[0]); + counter++; + } + } + + adpd.STOP(); + AS_LED_OFF(); + AS_LED_Current(0); + return 0; +} + diff --git a/serial.cpp b/src/serial.cpp similarity index 96% rename from serial.cpp rename to src/serial.cpp index 2b59e4c..36c2be4 100644 --- a/serial.cpp +++ b/src/serial.cpp @@ -1,116 +1,116 @@ - -// Serial routines that can read/write to either or both Serial devices (Serial (USB) and Serial1 (BLE)) based on a setting -// It also maintains and prints a CRC value -// Can use a packet mode that resends - -// Jon Zeeff 2016 - -// WARNING: Serial_Printf() is limited to 200 characters per call -// Zero is used as a string terminator - it can't be sent. Nor can ETX. - -// Should change this to C++ and a class - -// Remove BLE related now, may add later HJC 2022 - -#include -#include - -int Serial_Peek() -{ - - if (Serial && Serial.available()) - return Serial.peek(); - - return -1; -} - - -char *Serial_Input_Chars(char *string, const char *terminators, long unsigned int timeout, unsigned int max_length) { // terminating characters must be single characters or a list of single characters (so "+" will terminal on +, while "+-" will terminal on either + or -). - unsigned long start = millis(); - unsigned count = 0; - - // loop forever or until a condition is met - for (;;) { - unsigned now = millis(); - - if (timeout > 0 && (now - start) > timeout) // timeout - break; // done - - int c = Serial_Peek(); - if (c == -1) { - //sleep_cpu(); - //sleep_cpu_2(); // save power - continue; // nothing available - } - - if (c > 127) break; - - char b = Serial.read(); - - if (strchr(terminators, b)) { - //test_char = b; - break; - - } // terminator char seen - throw it away //AND END THE LOOP! - - if (strchr("\r\n", b)) { // remove characetrs that can break a JSON. This occurs AFTER the detection of terminators - //test_char = b; - continue; - - } // terminator char seen - throw it away //AND END THE LOOP! - - start = now; // restart interval - - string[count] = b; // add to string - count++; - if (max_length > 0 && count >= max_length){ // too long - //Serial_Print("JSON too long"); - string[max_length - 1] = 0; - return string; - } - } // for ever - - string[count] = 0; // always null terminate the string - - return string; - -} // Serial_Input_Chars() - - -// read a double value (see above) -// empty string returns NAN - -double Serial_Input_Double(const char *terminators, long unsigned int timeout) { - char S[25]; - Serial_Input_Chars(S, terminators, timeout, sizeof(S) - 1); - if (strlen(S) == 0) - return NAN; - return strtod(S, 0); -} // Serial_Input_Double() - - -// read a long value (see above) -// invalid strings return 0 - -long Serial_Input_Long(const char *terminators, long unsigned int timeout) { - char S[25]; - Serial_Input_Chars(S, terminators, timeout, sizeof(S) - 1); - return atol(S); -} // Serial_Input_Long() - - -// Caution: only good for short strings - -String Serial_Input_String(const char *terminators, long unsigned int timeout) -{ - static String serial_string; - char str[100 + 1]; // caution - fixed size buffer - - Serial_Input_Chars(str, terminators, timeout, sizeof(str) - 1); - - serial_string = str; - - // assert(strlen(str) < 100); - - return serial_string; -} // user_enter_str() + +// Serial routines that can read/write to either or both Serial devices (Serial (USB) and Serial1 (BLE)) based on a setting +// It also maintains and prints a CRC value +// Can use a packet mode that resends + +// Jon Zeeff 2016 + +// WARNING: Serial_Printf() is limited to 200 characters per call +// Zero is used as a string terminator - it can't be sent. Nor can ETX. + +// Should change this to C++ and a class + +// Remove BLE related now, may add later HJC 2022 + +#include +#include + +int Serial_Peek() +{ + + if (Serial && Serial.available()) + return Serial.peek(); + + return -1; +} + + +char *Serial_Input_Chars(char *string, const char *terminators, long unsigned int timeout, unsigned int max_length) { // terminating characters must be single characters or a list of single characters (so "+" will terminal on +, while "+-" will terminal on either + or -). + unsigned long start = millis(); + unsigned count = 0; + + // loop forever or until a condition is met + for (;;) { + unsigned now = millis(); + + if (timeout > 0 && (now - start) > timeout) // timeout + break; // done + + int c = Serial_Peek(); + if (c == -1) { + //sleep_cpu(); + //sleep_cpu_2(); // save power + continue; // nothing available + } + + if (c > 127) break; + + char b = Serial.read(); + + if (strchr(terminators, b)) { + //test_char = b; + break; + + } // terminator char seen - throw it away //AND END THE LOOP! + + if (strchr("\r\n", b)) { // remove characetrs that can break a JSON. This occurs AFTER the detection of terminators + //test_char = b; + continue; + + } // terminator char seen - throw it away //AND END THE LOOP! + + start = now; // restart interval + + string[count] = b; // add to string + count++; + if (max_length > 0 && count >= max_length){ // too long + //Serial_Print("JSON too long"); + string[max_length - 1] = 0; + return string; + } + } // for ever + + string[count] = 0; // always null terminate the string + + return string; + +} // Serial_Input_Chars() + + +// read a double value (see above) +// empty string returns NAN + +double Serial_Input_Double(const char *terminators, long unsigned int timeout) { + char S[25]; + Serial_Input_Chars(S, terminators, timeout, sizeof(S) - 1); + if (strlen(S) == 0) + return NAN; + return strtod(S, 0); +} // Serial_Input_Double() + + +// read a long value (see above) +// invalid strings return 0 + +long Serial_Input_Long(const char *terminators, long unsigned int timeout) { + char S[25]; + Serial_Input_Chars(S, terminators, timeout, sizeof(S) - 1); + return atol(S); +} // Serial_Input_Long() + + +// Caution: only good for short strings + +String Serial_Input_String(const char *terminators, long unsigned int timeout) +{ + static String serial_string; + char str[100 + 1]; // caution - fixed size buffer + + Serial_Input_Chars(str, terminators, timeout, sizeof(str) - 1); + + serial_string = str; + + // assert(strlen(str) < 100); + + return serial_string; +} // user_enter_str() diff --git a/serial.h b/src/serial.h similarity index 97% rename from serial.h rename to src/serial.h index 632f038..4df211c 100644 --- a/serial.h +++ b/src/serial.h @@ -1,7 +1,7 @@ - - -int Serial_Peek(); -char *Serial_Input_Chars(char *string, const char *terminators, long unsigned int timeout, unsigned int max_length); -double Serial_Input_Double(const char *terminators, long unsigned int timeout); -long Serial_Input_Long(const char *terminators, long unsigned int timeout); - + + +int Serial_Peek(); +char *Serial_Input_Chars(char *string, const char *terminators, long unsigned int timeout, unsigned int max_length); +double Serial_Input_Double(const char *terminators, long unsigned int timeout); +long Serial_Input_Long(const char *terminators, long unsigned int timeout); + diff --git a/src/src/adpd/ADPD_platform_ops.cpp b/src/src/adpd/ADPD_platform_ops.cpp new file mode 100644 index 0000000..009cc54 --- /dev/null +++ b/src/src/adpd/ADPD_platform_ops.cpp @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: CERN-OHL-S-2.0 +// Copyright (c) 2026 Jan IngenHousz Institute + +#include "ADPD_platform_ops.h" + +#include "driver/spi_master.h" + +int32_t jii_adpd6000_spi_transact(void *context, uint16_t command, + const uint8_t *tx_data, uint8_t *rx_data, + size_t data_length) { + if (context == nullptr || data_length == 0) { + return ESP_ERR_INVALID_ARG; + } + + const spi_device_handle_t handle = + *static_cast(context); + if (handle == nullptr) { + return ESP_ERR_INVALID_STATE; + } + + spi_transaction_t transaction = {}; + transaction.addr = command; + transaction.length = data_length * 8; + transaction.tx_buffer = tx_data; + if (rx_data != nullptr) { + transaction.rxlength = data_length * 8; + transaction.rx_buffer = rx_data; + } + + // Return the ESP-IDF status unchanged. The clean-room driver deliberately + // propagates transport failures to its caller rather than turning them into + // successful register operations on a disconnected sensor. + return static_cast( + spi_device_polling_transmit(handle, &transaction)); +} diff --git a/src/src/adpd/ADPD_platform_ops.h b/src/src/adpd/ADPD_platform_ops.h new file mode 100644 index 0000000..3add7d8 --- /dev/null +++ b/src/src/adpd/ADPD_platform_ops.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: CERN-OHL-S-2.0 +// Copyright (c) 2026 Jan IngenHousz Institute + +#ifndef JII_ADPD_PLATFORM_OPS_H +#define JII_ADPD_PLATFORM_OPS_H + +#include +#include + +int32_t jii_adpd6000_spi_transact(void *context, uint16_t command, + const uint8_t *tx_data, uint8_t *rx_data, + size_t data_length); + +#endif // JII_ADPD_PLATFORM_OPS_H diff --git a/src/src/adpd/jii_adpd6000.cpp b/src/src/adpd/jii_adpd6000.cpp new file mode 100644 index 0000000..02672a5 --- /dev/null +++ b/src/src/adpd/jii_adpd6000.cpp @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: CERN-OHL-S-2.0 +// Copyright (c) 2026 Jan IngenHousz Institute + +#include "jii_adpd6000.h" + +#if defined(__has_include) +#if __has_include("esp_attr.h") +#include "esp_attr.h" +#endif +#endif + +// Host tests do not carry ESP-IDF's include path. Production builds use the +// exact WORD_ALIGNED_ATTR definition from esp_attr.h; this identical fallback +// keeps the transport-agnostic driver testable on the host. +#ifndef WORD_ALIGNED_ATTR +#define WORD_ALIGNED_ATTR __attribute__((aligned(4))) +#endif + +namespace jii { +namespace adpd6000 { + +namespace { + +constexpr uint16_t kReadCommandBit = 0; +constexpr uint16_t kWriteCommandBit = 1; +constexpr uint16_t kSoftwareReset = 0x8000; +constexpr uint16_t kOperatingModeMask = 0x0007; +constexpr uint16_t kOperatingModeRun = 0x0001; +constexpr uint16_t kOpticalSlotMask = 0x0F00; +constexpr uint16_t kClearFifo = 0x8000; +constexpr uint16_t kFifoByteCountMask = 0x07FF; + +} // namespace + +Driver::Driver() : transport_{nullptr, nullptr} {} + +Driver::Driver(Transport transport) : transport_(transport) {} + +void Driver::set_transport(Transport transport) { transport_ = transport; } + +int32_t Driver::encode_spi_command(uint16_t register_address, bool write, + uint8_t command_bytes[2]) { + if (command_bytes == nullptr || register_address > kMaxRegisterAddress) { + return kInvalidArgument; + } + + // Figures 38 and 39 of the Rev. 0 data sheet define a 15-bit address + // followed by R/W as the least-significant command bit: 0 reads, 1 writes. + const uint16_t command = static_cast( + (register_address << 1) | (write ? kWriteCommandBit : kReadCommandBit)); + command_bytes[0] = static_cast(command >> 8); + command_bytes[1] = static_cast(command & 0xFF); + return kOk; +} + +int32_t Driver::transact(uint16_t register_address, bool write, + const uint8_t *tx_data, uint8_t *rx_data, + size_t data_length) const { + if (transport_.transact == nullptr || register_address > kMaxRegisterAddress || + data_length == 0) { + return kInvalidArgument; + } + + uint8_t command_bytes[2]; + const int32_t encode_result = + encode_spi_command(register_address, write, command_bytes); + if (encode_result != kOk) { + return encode_result; + } + const uint16_t command = static_cast( + (static_cast(command_bytes[0]) << 8) | command_bytes[1]); + return transport_.transact(transport_.context, command, tx_data, rx_data, + data_length); +} + +int32_t Driver::read_register(uint16_t register_address, uint16_t *value) const { + if (value == nullptr) { + return kInvalidArgument; + } + + // ESP-IDF's SPI DMA engine writes RX data in four-byte units even though an + // ADPD register contributes only two bytes. Keep the on-wire length at two, + // but provide the explicitly aligned four-byte storage DMA requires. + WORD_ALIGNED_ATTR uint8_t bytes[4] = {0, 0, 0, 0}; + const int32_t result = transact(register_address, false, nullptr, bytes, 2); + if (result != kOk) { + return result; + } + *value = static_cast((static_cast(bytes[0]) << 8) | + bytes[1]); + return kOk; +} + +int32_t Driver::write_register(uint16_t register_address, uint16_t value) const { + const uint8_t bytes[2] = {static_cast(value >> 8), + static_cast(value & 0xFF)}; + return transact(register_address, true, bytes, nullptr, sizeof(bytes)); +} + +int32_t Driver::update_register(uint16_t register_address, uint16_t mask, + uint16_t value) const { + uint16_t current = 0; + int32_t result = read_register(register_address, ¤t); + if (result != kOk) { + return result; + } + const uint16_t updated = + static_cast((current & static_cast(~mask)) | + (value & mask)); + return write_register(register_address, updated); +} + +int32_t Driver::identify(uint8_t *chip_id, uint8_t *silicon_version) const { + if (chip_id == nullptr || silicon_version == nullptr) { + return kInvalidArgument; + } + uint16_t identity = 0; + const int32_t result = read_register(reg::kChipId, &identity); + if (result != kOk) { + return result; + } + *chip_id = static_cast(identity & 0xFF); + *silicon_version = static_cast(identity >> 8); + return kOk; +} + +int32_t Driver::software_reset() const { + return write_register(reg::kSystemControl, kSoftwareReset); +} + +int32_t Driver::set_running(bool running) const { + return update_register(reg::kOperatingMode, kOperatingModeMask, + running ? kOperatingModeRun : 0); +} + +int32_t Driver::set_slot_frequency(uint32_t timer_clock_hz, + uint32_t frequency_hz) const { + if (timer_clock_hz == 0 || frequency_hz == 0 || + frequency_hz > timer_clock_hz) { + return kOutOfRange; + } + const uint32_t period = timer_clock_hz / frequency_hz; + if (period == 0 || period > kMaxTimeslotPeriod) { + return kOutOfRange; + } + + int32_t result = write_register(reg::kTimeslotFrequencyLow, + static_cast(period & 0xFFFF)); + if (result != kOk) { + return result; + } + return write_register(reg::kTimeslotFrequencyHigh, + static_cast((period >> 16) & 0x7F)); +} + +int32_t Driver::set_optical_slot_count(uint8_t count) const { + if (count > 12) { + return kOutOfRange; + } + return update_register(reg::kOperatingMode, kOpticalSlotMask, + static_cast(count) << 8); +} + +int32_t Driver::clear_fifo() const { + int32_t result = write_register(reg::kFifoStatus, kClearFifo); + if (result != kOk) { + return result; + } + // CLEAR_FIFO is a command bit. The data sheet requires it to be returned to + // zero after the clear so the FIFO can resume operation. + return write_register(reg::kFifoStatus, 0); +} + +int32_t Driver::fifo_byte_count(uint16_t *count) const { + if (count == nullptr) { + return kInvalidArgument; + } + uint16_t status = 0; + const int32_t result = read_register(reg::kFifoStatus, &status); + if (result != kOk) { + return result; + } + *count = static_cast(status & kFifoByteCountMask); + return kOk; +} + +int32_t Driver::read_fifo(uint8_t *data, size_t length) const { + if (data == nullptr || length == 0 || length > kFifoCapacityBytes) { + return kInvalidArgument; + } + // The FIFO address does not auto-increment like normal registers. A single + // byte-granular transaction drains consecutive bytes while CS stays low. + return transact(reg::kFifoData, false, nullptr, data, length); +} + +int32_t Driver::read_fifo_samples(uint16_t num_samples, uint8_t width, + uint32_t *data) const { + uint8_t readout[4]; + if (data == nullptr || num_samples == 0 || width == 0 || + width > sizeof(readout)) { + return kInvalidArgument; + } + + for (uint16_t i = 0; i < num_samples; ++i) { + data[i] = 0; + const int32_t result = read_fifo(readout, width); + if (result != kOk) { + // Callers historically ignore readfifo's status. Clear the failed and + // unattempted samples so a transport fault cannot republish values left + // in the caller's reusable buffer by a prior measurement point. + for (uint16_t remaining = i; remaining < num_samples; ++remaining) { + data[remaining] = 0; + } + return result; + } + for (uint8_t byte = 0; byte < width; ++byte) { + data[i] |= static_cast(readout[byte]) + << ((width - byte - 1) * 8); + } + } + return kOk; +} + +} // namespace adpd6000 +} // namespace jii diff --git a/src/src/adpd/jii_adpd6000.h b/src/src/adpd/jii_adpd6000.h new file mode 100644 index 0000000..93ea305 --- /dev/null +++ b/src/src/adpd/jii_adpd6000.h @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: CERN-OHL-S-2.0 +// Copyright (c) 2026 Jan IngenHousz Institute + +#ifndef JII_ADPD6000_H +#define JII_ADPD6000_H + +#include +#include + +namespace jii { +namespace adpd6000 { + +constexpr int32_t kOk = 0; +constexpr int32_t kInvalidArgument = -1; +constexpr int32_t kOutOfRange = -2; +constexpr uint16_t kMaxRegisterAddress = 0x7FFF; +constexpr uint16_t kFifoCapacityBytes = 640; +constexpr uint32_t kMaxTimeslotPeriod = 0x7FFFFF; + +enum class Slot : uint8_t { + A = 0, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, +}; + +namespace reg { + +// ADPD6000 Rev. 0 data sheet, Table 20 and Table 21. Register names and +// numeric addresses are public hardware facts. AMBIT uses only the optical, +// GPIO, clock, and FIFO subset below. +constexpr uint16_t kFifoStatus = 0x0000; +constexpr uint16_t kChipId = 0x0008; +constexpr uint16_t kTimeslotFrequencyLow = 0x000D; +constexpr uint16_t kTimeslotFrequencyHigh = 0x000E; +constexpr uint16_t kSystemControl = 0x000F; +constexpr uint16_t kOperatingMode = 0x0010; +constexpr uint16_t kInputSleep = 0x0020; +constexpr uint16_t kInputConfig = 0x0021; +constexpr uint16_t kGpioConfig = 0x0022; +constexpr uint16_t kGpio01 = 0x0023; +constexpr uint16_t kGpio23 = 0x0024; +constexpr uint16_t kGpioExternal = 0x0026; +constexpr uint16_t kFifoData = 0x002F; +constexpr uint16_t kAdcControl = 0x0046; +constexpr uint16_t kGlobalBiasControl = 0x004C; +constexpr uint16_t kIoAdjust = 0x0057; + +constexpr uint16_t kSlotStride = 0x0020; +constexpr uint16_t kTimeslotControlA = 0x0120; +constexpr uint16_t kTimeslotPathA = 0x0121; +constexpr uint16_t kInputsA = 0x0122; +constexpr uint16_t kCathodeA = 0x0123; +constexpr uint16_t kAfeTrim1A = 0x0124; +constexpr uint16_t kAfeTrim2A = 0x0125; +constexpr uint16_t kAfeDac1A = 0x0126; +constexpr uint16_t kAfeDac2A = 0x0127; +constexpr uint16_t kLedPower12A = 0x0128; +constexpr uint16_t kLedModeA = 0x0129; +constexpr uint16_t kCountsA = 0x012A; +constexpr uint16_t kPeriodA = 0x012B; +constexpr uint16_t kLedPulse1A = 0x012C; +constexpr uint16_t kLedPulse2A = 0x012D; +constexpr uint16_t kIntegratorWidthA = 0x012E; +constexpr uint16_t kIntegratorOffsetA = 0x012F; +constexpr uint16_t kModulationPulseA = 0x0130; +constexpr uint16_t kPattern1A = 0x0131; +constexpr uint16_t kData1A = 0x0135; +constexpr uint16_t kData2A = 0x0136; +constexpr uint16_t kDigitalIntegrationLitA = 0x0138; +constexpr uint16_t kDigitalIntegrationDarkA = 0x0139; + +constexpr uint16_t for_slot(uint16_t slot_a_register, Slot slot) { + return static_cast(slot_a_register + + static_cast(slot) * kSlotStride); +} + +} // namespace reg + +struct Transport { + void *context; + // The 16-bit command is shifted MSB first while CS is asserted, followed by + // data_length bytes. Reads leave tx_data null; writes leave rx_data null. + int32_t (*transact)(void *context, uint16_t command, + const uint8_t *tx_data, uint8_t *rx_data, + size_t data_length); +}; + +class Driver { + public: + Driver(); + explicit Driver(Transport transport); + + void set_transport(Transport transport); + + static int32_t encode_spi_command(uint16_t register_address, bool write, + uint8_t command_bytes[2]); + + int32_t read_register(uint16_t register_address, uint16_t *value) const; + int32_t write_register(uint16_t register_address, uint16_t value) const; + int32_t update_register(uint16_t register_address, uint16_t mask, + uint16_t value) const; + + int32_t identify(uint8_t *chip_id, uint8_t *silicon_version) const; + int32_t software_reset() const; + int32_t set_running(bool running) const; + int32_t set_slot_frequency(uint32_t timer_clock_hz, + uint32_t frequency_hz) const; + int32_t set_optical_slot_count(uint8_t count) const; + + int32_t clear_fifo() const; + int32_t fifo_byte_count(uint16_t *count) const; + int32_t read_fifo(uint8_t *data, size_t length) const; + int32_t read_fifo_samples(uint16_t num_samples, uint8_t width, + uint32_t *data) const; + + private: + int32_t transact(uint16_t register_address, bool write, + const uint8_t *tx_data, uint8_t *rx_data, + size_t data_length) const; + + Transport transport_; +}; + +} // namespace adpd6000 +} // namespace jii + +#endif // JII_ADPD6000_H diff --git a/src/src/adpd/u_adpd6100.cpp b/src/src/adpd/u_adpd6100.cpp new file mode 100644 index 0000000..2e2a40e --- /dev/null +++ b/src/src/adpd/u_adpd6100.cpp @@ -0,0 +1,918 @@ +#include "u_adpd6100.h" + +static const char* TAG = "ADPD"; + + +ADPD6::ADPD6(void){ + ADPD6::adpd1_spi_handle= NULL; // ESP32 Spi device handle + + ADPD6::adpd_spi_dev = { // spi configuration + .command_bits = 0, + .address_bits = 16, + .dummy_bits = 0, + .mode = 3, + .clock_speed_hz = SPI_MASTER_FREQ_10M, + .spics_io_num = ADPD1_CS, + .queue_size=1 + }; + ADPD6::driver.set_transport({&adpd1_spi_handle, + jii_adpd6000_spi_transact}); + + ADPD6::load_default(&(ADPD6::sys_config)); + ADPD6::load_default(&(ADPD6::led_config)); + ADPD6::load_default(&(ADPD6::SNR_config)); + ADPD6::load_default(&(ADPD6::DI_config)); + ADPD6::load_default(&(ADPD6::AI_config)); + ADPD6::load_default(&(ADPD6::signal_config)); +}; + +ADPD6::~ADPD6(void){ + +}; + + +bool ADPD6::begin(void){ + esp_err_t err = 1; + if (_spi_device_attached){ + spi_bus_remove_device(ADPD6::adpd1_spi_handle); + ADPD6::_spi_device_attached = false; + ADPD6::chip_check = false; + ESP_LOGI(TAG, "ADPD reset"); + } + + ESP_LOGV(TAG, "ADPD Initate:"); + + err = spi_bus_add_device(SPI2_HOST, &(ADPD6::adpd_spi_dev), &(ADPD6::adpd1_spi_handle)); + if (err != ESP_OK){ + ESP_LOGE(TAG, "SPI Bus error: %s", esp_err_to_name(err)); + return false; + } + ESP_LOGI(TAG, "ADPD attached as SPI device"); + + ADPD6::_spi_device_attached = true; + uint8_t ret1 = 0; + uint8_t ret2 = 0; + err = static_cast(ADPD6::driver.identify(&ret1, &ret2)); + if (err != ESP_OK) { + ADPD6::chip_check = false; + ESP_LOGE(TAG, "ADPD identity read failed: %s", esp_err_to_name(err)); + return false; + } + + if (ret1 == 0xC4) { + ADPD6::chip_check = true; + Serial.printf("ADPD Found, chip version: %d\n", ret2); + err = static_cast(ADPD6::driver.software_reset()); + if (err != ESP_OK) { + ADPD6::chip_check = false; + return false; + } + delay(1); + ADPD6::load_default(&(ADPD6::sys_config)); + if (ADPD6::global_setup(&(ADPD6::sys_config)) != jii::adpd6000::kOk) { + ADPD6::chip_check = false; + return false; + } + return true; + }else{ + ADPD6::chip_check = false; + ESP_LOGE(TAG, "ADPD not found"); + return false; + } + return false; + +} + +int32_t ADPD6::clear_fifo(void){ + return ADPD6::driver.clear_fifo(); +} + +int32_t ADPD6::run_freq(uint32_t freq){ + return ADPD6::driver.set_slot_frequency(960000, freq); +} + +int32_t ADPD6::RUN(){ + return ADPD6::driver.set_running(true); +} + +int32_t ADPD6::STOP(){ + return ADPD6::driver.set_running(false); +} + +int32_t ADPD6::fifo_count(uint16_t *count){ + if (count == nullptr) return jii::adpd6000::kInvalidArgument; + *count = 0; + const int32_t result = ADPD6::driver.fifo_byte_count(count); + if (result != jii::adpd6000::kOk) return result; + if (*count > jii::adpd6000::kFifoCapacityBytes){ + *count = 0; + return jii::adpd6000::kOutOfRange; + } + return jii::adpd6000::kOk; +} + +uint16_t ADPD6::fifo_count(){ + uint16_t count = 0; + return fifo_count(&count) == jii::adpd6000::kOk ? count : 0; +} +int32_t ADPD6::readfifo(uint16_t num_samples, uint8_t width, uint32_t* data){ + return ADPD6::driver.read_fifo_samples(num_samples, width, data); +} + +int32_t ADPD6::write_reg(uint32_t reg_addr, uint16_t *reg_data){ + if (reg_data == nullptr || reg_addr > jii::adpd6000::kMaxRegisterAddress) { + return jii::adpd6000::kInvalidArgument; + } + return ADPD6::driver.write_register(static_cast(reg_addr), + *reg_data); +} + +int32_t ADPD6::read_reg(uint32_t reg_addr){ + uint16_t reg_data = 0; + if (reg_addr > jii::adpd6000::kMaxRegisterAddress) { + return jii::adpd6000::kInvalidArgument; + } + const int32_t result = ADPD6::driver.read_register( + static_cast(reg_addr), ®_data); + if (result != jii::adpd6000::kOk) return result; + return reg_data; +} +int32_t ADPD6::num_ts(uint8_t mode){ + return ADPD6::driver.set_optical_slot_count(mode); +} + + +void ADPD6::load_default(struct ts_led *ts){ + ts->driver1_current = 0; + ts->driver2_current = 0; + ts->led1_channel = LED_A; + ts->led2_channel = LED_A; + ts->led1_mode = 0; + ts->led2_mode = 0; +} + +int32_t ADPD6::ts_setup(jii::adpd6000::Slot timeslot_no, struct ts_led *init) +{ + int32_t ret; + uint16_t data = 0; + + /* Set LED power and A/B channels*/ + data = init->led2_channel << 15| + init->driver2_current << 8| + init->led1_channel << 7| + init->driver1_current << 0; + + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kLedPower12A, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + /* Set LED power and A/B channels*/ + data = init->led2_mode << 1| + init->led1_mode << 0; + + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kLedModeA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + return jii::adpd6000::kOk; +} + + +void ADPD6::load_default(struct ts_SNR *ts){ + ts->ch1_dac = 0; + ts->ch1_led_offset = 0; + ts->ch2_dac = 0; + ts->ch2_led_offset = 0; + ts->TIA_ceiling_det = 0; + ts->Ch2_R_int = 0; + ts->Ch1_R_int = 0; + ts->Vref_pulse = 0; + ts->Vref_VPD = 2; + ts->Vref_VPD_pulse = 3; + ts->TIA_gain_CH2 = 1; + ts->TIA_gain_CH1 = 1; + ts->C_int_CH2 = 0; + ts->C_int_CH1 = 0; +} + + +int32_t ADPD6::ts_setup(jii::adpd6000::Slot timeslot_no, struct ts_SNR *init) +{ + int32_t ret; + uint16_t data = 0; + + /* Set TIA AC Dac for channel 1*/ + data = init->ch1_dac << 7| + init->ch1_led_offset << 0; + + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kAfeDac1A, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + /* Set TIA AC Dac for channel 1*/ + data = init->ch2_dac << 7| + init->ch2_led_offset << 0; + + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kAfeDac2A, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + + data = init->TIA_ceiling_det << 15| + init->Ch2_R_int << 13| + init->Ch1_R_int << 11| + init->Vref_pulse << 10| + init->Vref_VPD << 8| + init->Vref_pulse << 6| + init->TIA_gain_CH2 << 3| + init->TIA_gain_CH1 << 0; + + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kAfeTrim1A, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->C_int_CH2 << 13| + init->C_int_CH1 << 12; + + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kAfeTrim2A, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + return jii::adpd6000::kOk; +} + +void ADPD6::load_default(struct ts_signal *ts){ + ts->pre_condition_width = 4; + ts->ac_type = 0; + ts->gpio_out = 0; + ts->INT2BUT = 0; + ts->AFE_PATh = 0x28; // 0x28 + ts->IN34 = 0; + ts->IN12 = 1; + ts->pre_condition_type = 0; + ts->VC2_pulse_type = 0; + ts->VC2_pulse_to = 0; + ts->VC2_active = 2; + ts->VC1_pulse_type = 0; + ts->VC1_pulse_to = 0; + ts->VC1_active = 2; // set vc1 to vref + 215mV +} + + +int32_t ADPD6::ts_setup(jii::adpd6000::Slot timeslot_no, struct ts_signal *init) +{ + int32_t ret; + uint16_t data = 0; + + /* Set TIA AC Dac for channel 1*/ + data = init->pre_condition_width << 12| + init->ac_type << 10| + init->gpio_out << 9| + init->INT2BUT << 8| + init->AFE_PATh << 0; + + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kTimeslotPathA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + /* Set TIA AC Dac for channel 1*/ + data = init->IN34 << 4| + init->IN12 << 0; + + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kInputsA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + + data = init->pre_condition_type << 12| + init->VC2_pulse_type << 10| + init->VC2_pulse_to << 8| + init->VC2_active << 6| + init->VC1_pulse_type << 4| + init->VC1_pulse_to << 2| + init->VC1_active << 0; + + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kCathodeA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + return jii::adpd6000::kOk; +} + +void ADPD6::load_default(struct ts_DI_timing *ts){ + ts->num_integration = 1; + ts->num_repeats = 1; + ts->Modulation_type = 0; + ts->period_min = 0; + ts->LED_pulse_width = 2; + ts->LED_pulse_offset = 0x10; + ts->LED_pulse2_offset = 0x13; + ts->LIT_OFFSET = 0x26; + ts->DARK_OFFSET1 = 0x06; + ts->DARK_OFFSET2 = 0x40; + ts->subsample_en = 0; + ts->ch2_en = 0; + ts->sample_type = 3; // two-region digital integration + ts->ts_offset = 0; + ts->dark_shift = 0; + ts->dark_size = 0; + ts->signal_shift = 0; + ts->signal_size = 3; + ts->lit_shift = 0; + ts->lit_size = 0; +} + + +int32_t ADPD6::ts_setup(jii::adpd6000::Slot timeslot_no, struct ts_DI_timing *init){ + int32_t ret; + uint16_t data = 0; + + + data = init->num_integration << 8| + init->num_repeats << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kCountsA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = init->Modulation_type << 12| + init->period_min << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kPeriodA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->LED_pulse_width << 8| + init->LED_pulse_offset << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kLedPulse1A, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + data = 0; + data = init->LED_pulse2_offset << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kLedPulse2A, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->LIT_OFFSET << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kDigitalIntegrationLitA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->DARK_OFFSET2 << 7| + init->DARK_OFFSET1 << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kDigitalIntegrationDarkA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->subsample_en << 15| + init->ch2_en << 14| + init->sample_type << 11| + init->ts_offset << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kTimeslotControlA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->dark_shift << 11| + init->dark_size << 8| + init->signal_shift << 3| + init->signal_size << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kData1A, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->lit_shift << 3| + init->lit_size << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kData2A, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + return jii::adpd6000::kOk; +} + +void ADPD6::load_default(struct ts_AI_timing *ts){ + ts->num_integration = 1; + ts->num_repeats = 1; + ts->Modulation_type = 0; + ts->period_min = 0; + ts->LED_pulse_width = 2; + ts->LED_pulse_offset = 0x10; + ts->LED_pulse2_offset = 0x13; + ts->SINGLE_INTEG = 0; + ts->CH2_DIS_AMP = 0; + ts->CH1_DIS_AMP = 0; + ts->ADC_count = 0; + ts->integration_width = 3; + ts->offset_us = 0xD; + ts->offset_ns = 0; + ts->pattern_LED_disable = 0; + ts->pattern_MOD_disable = 0; + ts->pattern_subtract = 0; + ts->pattern_reverse_int = 0; + ts->subsample_en = 0; + ts->ch2_en = 0; + ts->sample_type = 0; + ts->ts_offset = 0; + +} + +int32_t ADPD6::ts_setup(jii::adpd6000::Slot timeslot_no, struct ts_AI_timing *init){ + int32_t ret; + uint16_t data = 0; + + data = init->Modulation_type << 12| + init->period_min << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kPeriodA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->LED_pulse_width << 8| + init->LED_pulse_offset << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kLedPulse1A, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + data = 0; + data = init->LED_pulse2_offset << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kLedPulse2A, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + //0x012E INTEG_WIDTH_A + data = 0; + data = init->SINGLE_INTEG << 15| + init->CH2_DIS_AMP << 11| + init->CH1_DIS_AMP << 7| + init->ADC_count << 5| + init->integration_width << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kIntegratorWidthA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + //0x012F INTEG_OFFSET_A + data = 0; + data = init->offset_us << 5| + init->offset_ns << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kIntegratorOffsetA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + //0x130 MOD_PULSE_A + data = 0; + data = init->modulation_width << 8| + init->modulation_offset << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kModulationPulseA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + //0x0131 PATTERN1_A + data = 0; + data = init->pattern_LED_disable << 12| + init->pattern_MOD_disable << 8| + init->pattern_subtract << 4| + init->pattern_reverse_int << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kPattern1A, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + + + //0x0120 TS_CTRL_A + data = 0; + data = init->subsample_en << 15| + init->ch2_en << 14| + init->sample_type << 11| + init->ts_offset << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kTimeslotControlA, timeslot_no), &data); + if(ret != jii::adpd6000::kOk) + return ret; + return jii::adpd6000::kOk; +} + +void ADPD6::load_default(struct system_config *ts){ + ts->IN34_sleep = 0; + ts->IN12_sleep = 0; + ts->VC2_sleep = 0; + ts->VC1_sleep = 0; + ts->PAIR34 = 0; + ts->PAIR12 = 0; + ts->GPIO_slew = 0; + ts->GPIO_drv = 0; + ts->GPIO2_cfg = 0; + ts->GPIO1_cfg = 0; + ts->GPIO0_cfg = 0; + ts->GPIO1_output = 0; + ts->GPIO0_output = 0; + ts->GPIO2_output = 0; + ts->IOVDD = 0; + ts->SPI_drv = 0; + ts->SPI_slew = 0; + ts->alt_clock = 0; + ts->alt_clock_gpio = 0; + ts->ext_clock_freq = 0; + ts->internal_clock_en = 1; +} + + +int32_t ADPD6::global_setup(struct system_config *init){ + int32_t ret; + uint16_t data = 0; + + data = 0x2004; + ret = ADPD6::write_reg(jii::adpd6000::reg::kAdcControl, &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0x400b; + ret = ADPD6::write_reg(jii::adpd6000::reg::kGlobalBiasControl, &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->IN34_sleep << 4| + init->IN12_sleep << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::kInputSleep, &data); + if(ret != jii::adpd6000::kOk) + return ret; + + + data = 0; + data = init->VC2_sleep << 6| + init->VC1_sleep << 4| + init->PAIR34 << 1| + init->PAIR12 << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::kInputConfig, &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->GPIO_slew << 14| + init->GPIO_drv << 12| + init->GPIO2_cfg << 6| + init->GPIO1_cfg << 3| + init->GPIO0_cfg << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::kGpioConfig, &data); + if(ret != jii::adpd6000::kOk) + return ret; + + + data = 0; + data = init->GPIO1_output << 8| + init->GPIO0_output << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::kGpio01, &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->GPIO2_output << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::kGpio23, &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->IOVDD << 6| + 1 << 4| + init->SPI_slew << 2| + init->SPI_drv << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::kIoAdjust, &data); + if(ret != jii::adpd6000::kOk) + return ret; + + data = 0; + data = init->alt_clock << 8| + init->alt_clock_gpio << 6| + init->ext_clock_freq << 2| + init->internal_clock_en << 1; + ret = ADPD6::write_reg(jii::adpd6000::reg::kSystemControl, &data); + if(ret != jii::adpd6000::kOk) + return ret; + return jii::adpd6000::kOk; +} + +void ADPD6::load_default(struct GPIO0_config *ts){ + ts->GPIO0_cfg = 0; + ts->GPIO0_output = 0; + ts->EXT_SYNC_EN = 0; + ts->SYNC_GPIO = 0; +} + +int32_t ADPD6::gpio_setup(struct GPIO0_config *init){ + int32_t ret; + uint16_t data = 0; + data = init->EXT_SYNC_EN << 2| + init->SYNC_GPIO << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::kGpioExternal, &data); + if(ret != jii::adpd6000::kOk) return ret; + + data = 0; + data = init->GPIO0_output << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::kGpio01, &data); + if(ret != jii::adpd6000::kOk) return ret; + + data = 0; + data = init->GPIO0_cfg; + ret = ADPD6::write_reg(jii::adpd6000::reg::kGpioConfig, &data); + if(ret != jii::adpd6000::kOk) return ret; + + return 0; +} +int32_t ADPD6::preset_config_1(uint8_t ts, uint8_t num_integ){ + if (!(ADPD6::chip_check)){ + ESP_LOGE(TAG, "ADPD Not init in preset1"); + return -2; + } + + ESP_LOGV(TAG, "Preset_config 1 set for timeslot:%d. Two ambient channels. Total 2 x 3 bytes", ts); + // Channel 1: PD1 (sun-facing), channel 2: PD2 (leaf-facing IR) + + ADPD6::STOP(); + ADPD6::DI_config.period_min = 58; + ADPD6::DI_config.LIT_OFFSET = 64; + ADPD6::DI_config.DARK_OFFSET1 = 48; + ADPD6::DI_config.DARK_OFFSET2 = 90; + ADPD6::DI_config.LED_pulse_offset = 60; + ADPD6::DI_config.LED_pulse_width = 19; + ADPD6::DI_config.sample_type = 3; + ADPD6::DI_config.signal_size = 0; + ADPD6::DI_config.num_integration = num_integ; + ADPD6::DI_config.num_repeats = 1; + ADPD6::DI_config.dark_size = 3; + ADPD6::DI_config.lit_size = 0; + + ADPD6::signal_config.pre_condition_type = 5; + ADPD6::SNR_config.Ch1_R_int = 1; + ADPD6::SNR_config.C_int_CH1 = 1; + ADPD6::SNR_config.Ch2_R_int = 1; + ADPD6::SNR_config.C_int_CH2 = 1; + ADPD6::signal_config.INT2BUT = 1; + ADPD6::signal_config.ac_type = 0; + + ADPD6::DI_config.ch2_en = true; + ADPD6::signal_config.IN12 = 0B0101; + ADPD6::signal_config.IN34 = 0B0000; + + ADPD6::num_ts(ts + 1); + + ADPD6::ts_setup(static_cast(ts), &(ADPD6::DI_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::signal_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::SNR_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::led_config)); + + return 0; + +} + + +// fluo and ref channel setup +int32_t ADPD6::preset_config_2(uint8_t ts, uint8_t num_integ){ + if (!(ADPD6::chip_check)){ + ESP_LOGE(TAG, "ADPD Not init in preset 2"); + return -2; + } + + ESP_LOGV(TAG, "Preset_config 2 set for timeslot:%d. Total 2 x 2 x 3 bytes", ts); + // Channel 1: PD2 (leaf-facing IR), channel 2: PD4 (leaf-facing Vis) + + ADPD6::STOP(); + ADPD6::DI_config.period_min = 58; + ADPD6::DI_config.LIT_OFFSET = 72;//64; + ADPD6::DI_config.DARK_OFFSET1 = 48; + ADPD6::DI_config.DARK_OFFSET2 = 90; + ADPD6::DI_config.LED_pulse_offset = 60; + ADPD6::DI_config.LED_pulse_width = 15;//19; + ADPD6::DI_config.sample_type = 3; + ADPD6::DI_config.signal_size = 0; + ADPD6::DI_config.num_integration = num_integ; + ADPD6::DI_config.num_repeats = 1; + ADPD6::DI_config.dark_size = 3; + ADPD6::DI_config.lit_size = 3; + + ADPD6::signal_config.pre_condition_type = 5; + ADPD6::SNR_config.Ch1_R_int = 1; + ADPD6::SNR_config.C_int_CH1 = 1; + ADPD6::SNR_config.Ch2_R_int = 2; // 1 + ADPD6::SNR_config.C_int_CH2 = 1; + ADPD6::signal_config.INT2BUT = 1; + ADPD6::signal_config.ac_type = 2; + + ADPD6::DI_config.ch2_en = true; + ADPD6::signal_config.IN12 = 0B0011; + ADPD6::signal_config.IN34 = 0B0100; + + + + ADPD6::num_ts(ts + 1); + + ADPD6::ts_setup(static_cast(ts), &(ADPD6::DI_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::signal_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::SNR_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::led_config)); + + return 0; +} + +int32_t ADPD6::preset_config_2x(uint8_t ts, uint8_t num_integ, uint8_t lit_offset, uint8_t dark1_offset, uint8_t dark2_offset, uint8_t pulse_offset, uint8_t pulse_duration){ + if (!(ADPD6::chip_check)){ + ESP_LOGE(TAG, "ADPD Not init in preset 2"); + return -2; + } + + ESP_LOGV(TAG, "Preset_config 2 set for timeslot:%d. Total 2 x 2 x 3 bytes", ts); + // Channel 1: PD2 (leaf-facing IR), channel 2: PD4 (leaf-facing Vis) + + ADPD6::STOP(); + ADPD6::DI_config.period_min = 58; + ADPD6::DI_config.LIT_OFFSET = lit_offset; + ADPD6::DI_config.DARK_OFFSET1 = dark1_offset; + ADPD6::DI_config.DARK_OFFSET2 = dark2_offset; + ADPD6::DI_config.LED_pulse_offset = pulse_offset; + ADPD6::DI_config.LED_pulse_width = pulse_duration; + ADPD6::DI_config.sample_type = 3; + ADPD6::DI_config.signal_size = 0; + ADPD6::DI_config.num_integration = num_integ; + ADPD6::DI_config.num_repeats = 1; + ADPD6::DI_config.dark_size = 3; + ADPD6::DI_config.lit_size = 3; + + ADPD6::signal_config.pre_condition_type = 5; + ADPD6::SNR_config.Ch1_R_int = 1; + ADPD6::SNR_config.C_int_CH1 = 1; + ADPD6::SNR_config.Ch2_R_int = 1; + ADPD6::SNR_config.C_int_CH2 = 1; + ADPD6::signal_config.INT2BUT = 1; + ADPD6::signal_config.ac_type = 2; + + ADPD6::DI_config.ch2_en = true; + ADPD6::signal_config.IN12 = 0B0011; + ADPD6::signal_config.IN34 = 0B0100; + + + + ADPD6::num_ts(ts + 1); + + ADPD6::ts_setup(static_cast(ts), &(ADPD6::DI_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::signal_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::SNR_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::led_config)); + + return 0; +} + +// reflection channel setup +int32_t ADPD6::preset_config_3(uint8_t ts, uint8_t num_integ){ + if (!(ADPD6::chip_check)){ + ESP_LOGE(TAG, "ADPD Not init in preset 2"); + return -2; + } + + ESP_LOGV(TAG, "Preset_config 3 set for timeslot:%d. Total 2 x 3 bytes", ts); + // Channel 1: PD2 (leaf-facing IR), channel 2: PD4 (leaf-facing Vis) + + ADPD6::STOP(); + ADPD6::DI_config.period_min = 58; + ADPD6::DI_config.LIT_OFFSET = 72;//64; + ADPD6::DI_config.DARK_OFFSET1 = 48; + ADPD6::DI_config.DARK_OFFSET2 = 90; + ADPD6::DI_config.LED_pulse_offset = 60; + ADPD6::DI_config.LED_pulse_width = 19; + ADPD6::DI_config.sample_type = 3; + ADPD6::DI_config.signal_size = 3; + ADPD6::DI_config.num_integration = num_integ; + ADPD6::DI_config.num_repeats = 1; + ADPD6::DI_config.dark_size = 0; + ADPD6::DI_config.lit_size = 0; + + ADPD6::signal_config.pre_condition_type = 5; + ADPD6::SNR_config.Ch1_R_int = 1; + ADPD6::SNR_config.C_int_CH1 = 1; + ADPD6::SNR_config.Ch2_R_int = 1; + ADPD6::SNR_config.C_int_CH2 = 1; + ADPD6::signal_config.INT2BUT = 1; + ADPD6::signal_config.ac_type = 2; + + ADPD6::DI_config.ch2_en = true; + ADPD6::signal_config.IN12 = 0B0011; + ADPD6::signal_config.IN34 = 0B0100; + + + ADPD6::num_ts(ts + 1); + + ADPD6::ts_setup(static_cast(ts), &(ADPD6::DI_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::signal_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::SNR_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::led_config)); + + return 0; +} + + + +// far-red +int32_t ADPD6::preset_config_4(uint8_t ts){ + if (!(ADPD6::chip_check)){ + ESP_LOGE(TAG, "ADPD Not init in preset 3"); + return -2; + } + + ESP_LOGV(TAG, "Preset_config 4 set for timeslot:%d. Total 0 bytes", ts); + // Channel 1: PD2 (leaf-facing IR), channel 2: PD4 (leaf-facing Vis) + + ADPD6::STOP(); + ADPD6::DI_config.period_min = 320; + ADPD6::DI_config.LIT_OFFSET = 100; + ADPD6::DI_config.DARK_OFFSET1 = 48; + ADPD6::DI_config.DARK_OFFSET2 = 300; + ADPD6::DI_config.LED_pulse_offset = 60; + ADPD6::DI_config.LED_pulse_width = 200; + ADPD6::DI_config.sample_type = 3; + ADPD6::DI_config.signal_size = 0; + ADPD6::DI_config.num_integration = 1; + ADPD6::DI_config.num_repeats = 1; + ADPD6::DI_config.dark_size = 0; + ADPD6::DI_config.lit_size = 0; + + ADPD6::signal_config.pre_condition_type = 5; + ADPD6::SNR_config.Ch1_R_int = 1; + ADPD6::SNR_config.C_int_CH1 = 1; + ADPD6::SNR_config.Ch2_R_int = 1; + ADPD6::SNR_config.C_int_CH2 = 1; + ADPD6::signal_config.INT2BUT = 1; + ADPD6::signal_config.ac_type = 0; + + ADPD6::DI_config.ch2_en = false; + ADPD6::signal_config.IN12 = 0B0011; + ADPD6::signal_config.IN34 = 0B0100; + + + + ADPD6::num_ts(ts + 1); + + ADPD6::ts_setup(static_cast(ts), &(ADPD6::DI_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::signal_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::SNR_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::led_config)); + + return 0; +} + +int32_t ADPD6::repeats_only(uint8_t ts, uint16_t num_integration, uint8_t num_repeats){ + uint16_t data = 0; + int32_t ret; + data = num_integration << 8| + num_repeats << 0; + ret = ADPD6::write_reg(jii::adpd6000::reg::for_slot(jii::adpd6000::reg::kCountsA, static_cast(ts)), &data); + if(ret != jii::adpd6000::kOk) + return ret; + return jii::adpd6000::kOk; +} + +// external trigger +int32_t ADPD6::preset_config_ext_fast(uint8_t ts){ + return ADPD6::preset_config_ext_fast(ts, 1); + +} + +int32_t ADPD6::preset_config_ext_fast(uint8_t ts, uint8_t integ){ + if (!(ADPD6::chip_check)){ + ESP_LOGE(TAG, "ADPD Not init in preset 2"); + return -2; + } + + ESP_LOGV(TAG, "External trigger test: %d", ts); + // Channel 1: PD2 (leaf-facing IR), channel 2: PD4 (leaf-facing Vis) + + ADPD6::STOP(); + ADPD6::DI_config.period_min = 50; + ADPD6::DI_config.LIT_OFFSET = 70; + ADPD6::DI_config.DARK_OFFSET1 = 48; + ADPD6::DI_config.DARK_OFFSET2 = 85; + ADPD6::DI_config.LED_pulse_offset = 60; + ADPD6::DI_config.LED_pulse_width = 15; + ADPD6::DI_config.sample_type = 3; + ADPD6::DI_config.signal_size = 0; + ADPD6::DI_config.num_integration = integ; + ADPD6::DI_config.num_repeats = 1; + ADPD6::DI_config.dark_size = 3; + ADPD6::DI_config.lit_size = 3; + + ADPD6::signal_config.pre_condition_type = 5; + ADPD6::SNR_config.Ch1_R_int = 1; + ADPD6::SNR_config.C_int_CH1 = 1; + ADPD6::SNR_config.Ch2_R_int = 2; + ADPD6::SNR_config.C_int_CH2 = 1; + ADPD6::signal_config.INT2BUT = 1; + ADPD6::signal_config.ac_type = 2; + + ADPD6::DI_config.ch2_en = true; + ADPD6::signal_config.IN12 = 0B0011; + ADPD6::signal_config.IN34 = 0B0100; + + + + ADPD6::num_ts(ts + 1); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::DI_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::signal_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::SNR_config)); + ADPD6::ts_setup(static_cast(ts), &(ADPD6::led_config)); + + return 0; +} diff --git a/src/adpd/u_adpd6100.h b/src/src/adpd/u_adpd6100.h similarity index 80% rename from src/adpd/u_adpd6100.h rename to src/src/adpd/u_adpd6100.h index f946204..b559f1e 100644 --- a/src/adpd/u_adpd6100.h +++ b/src/src/adpd/u_adpd6100.h @@ -1,25 +1,25 @@ /*! - * @brief ADPD6000 configuration header + * @brief AMBIT ADPD6000/6100 optical configuration wrapper * @copyright MSU-PRL Kramer Lab - * @author Jingcheng Huang - * @date 20230224 + * @author Jingcheng Huang; Jan IngenHousz Institute + * @date 20230224; clean-room driver migration 20260805 */ #ifndef __U_ADPD6000_H__ #define __U_ADPD6000_H__ #include -#include "lib/ADPD6000/adi_adpd6000.h" #include "driver/spi_common.h" #include "driver/spi_master.h" #include "../pin_config.h" +#include "ADPD_platform_ops.h" +#include "jii_adpd6000.h" #include "u_adpd6100_typedef.h" class ADPD6; class ADPD6{ public: - adi_adpd6000_device_t handle; ADPD6(); ~ADPD6(); bool begin(); @@ -30,6 +30,7 @@ class ADPD6{ int32_t num_ts(uint8_t); int32_t RUN(); int32_t STOP(); + int32_t fifo_count(uint16_t *count); uint16_t fifo_count(); int32_t readfifo(uint16_t num_samples, uint8_t width, uint32_t* data); bool chip_check = false; @@ -42,11 +43,11 @@ class ADPD6{ struct GPIO0_config gpio_config; - int32_t ts_setup(adi_adpd6000_slot_e timeslot_no,struct ts_led *init); - int32_t ts_setup(adi_adpd6000_slot_e timeslot_no, struct ts_SNR *init); - int32_t ts_setup(adi_adpd6000_slot_e timeslot_no, struct ts_signal *init); - int32_t ts_setup(adi_adpd6000_slot_e timeslot_no, struct ts_DI_timing *init); - int32_t ts_setup(adi_adpd6000_slot_e timeslot_no, struct ts_AI_timing *init); + int32_t ts_setup(jii::adpd6000::Slot timeslot_no, struct ts_led *init); + int32_t ts_setup(jii::adpd6000::Slot timeslot_no, struct ts_SNR *init); + int32_t ts_setup(jii::adpd6000::Slot timeslot_no, struct ts_signal *init); + int32_t ts_setup(jii::adpd6000::Slot timeslot_no, struct ts_DI_timing *init); + int32_t ts_setup(jii::adpd6000::Slot timeslot_no, struct ts_AI_timing *init); int32_t gpio_setup(struct GPIO0_config *init); int32_t global_setup(struct system_config *init); @@ -82,6 +83,7 @@ class ADPD6{ bool _spi_device_attached = false; spi_device_interface_config_t adpd_spi_dev; spi_device_handle_t adpd1_spi_handle = NULL; + jii::adpd6000::Driver driver; void load_default(struct ts_led *ts); @@ -94,4 +96,4 @@ class ADPD6{ }; -#endif \ No newline at end of file +#endif diff --git a/src/adpd/u_adpd6100_typedef.h b/src/src/adpd/u_adpd6100_typedef.h similarity index 99% rename from src/adpd/u_adpd6100_typedef.h rename to src/src/adpd/u_adpd6100_typedef.h index d05e977..73cc740 100644 --- a/src/adpd/u_adpd6100_typedef.h +++ b/src/src/adpd/u_adpd6100_typedef.h @@ -126,7 +126,7 @@ struct ts_SNR{ struct ts_signal{ - // 0x0121 REG_TS_PATH_A_ADDR + // Time Slot A path register (0x0121). /** Precondition duration for this time slot. This value is in 2 μs * increments. A value of 0 skips the precondition state. 4 = 8us @@ -607,4 +607,3 @@ struct GPIO0_config{ }; - diff --git a/src/as7341/Adafruit_AS7341.cpp b/src/src/as7341/Adafruit_AS7341.cpp similarity index 100% rename from src/as7341/Adafruit_AS7341.cpp rename to src/src/as7341/Adafruit_AS7341.cpp diff --git a/src/as7341/Adafruit_AS7341.h b/src/src/as7341/Adafruit_AS7341.h similarity index 100% rename from src/as7341/Adafruit_AS7341.h rename to src/src/as7341/Adafruit_AS7341.h diff --git a/src/as7341/README.md b/src/src/as7341/README.md similarity index 100% rename from src/as7341/README.md rename to src/src/as7341/README.md diff --git a/src/as7341/code-of-conduct.md b/src/src/as7341/code-of-conduct.md similarity index 100% rename from src/as7341/code-of-conduct.md rename to src/src/as7341/code-of-conduct.md diff --git a/src/as7341/library.properties b/src/src/as7341/library.properties similarity index 100% rename from src/as7341/library.properties rename to src/src/as7341/library.properties diff --git a/src/as7341/license.txt b/src/src/as7341/license.txt similarity index 100% rename from src/as7341/license.txt rename to src/src/as7341/license.txt diff --git a/src/as7341/spec_meas.cpp b/src/src/as7341/spec_meas.cpp similarity index 56% rename from src/as7341/spec_meas.cpp rename to src/src/as7341/spec_meas.cpp index eee21ea..e5de6e1 100644 --- a/src/as7341/spec_meas.cpp +++ b/src/src/as7341/spec_meas.cpp @@ -130,14 +130,35 @@ void AS_all_channel(uint16_t T1, uint16_t T2){ // return get_PAR(spec); // } -void dual_exposure(as7341_gain_t gain1,as7341_gain_t gain2, uint16_t readings_buffer[]){ +/* STATUS2 (0xA3) analog-saturation bit. The digital full-scale check downstream + * compares counts against (atime+1)*(astep+1); ASAT_ANALOG is the case that + * check cannot see — the front end pegs before the accumulator does, so the + * counts look plausible and are not. Read straight off the bus rather than + * adding a method to the vendored driver. */ +#define AS7341_STATUS2_ASAT_ANALOG 0x08 + +static bool as7341_analog_saturated(){ + Adafruit_BusIO_Register status2_reg = + Adafruit_BusIO_Register(as7341.i2c_dev, AS7341_STATUS2); + uint8_t status2 = 0; + if (!status2_reg.read(&status2)) return false; + return (status2 & AS7341_STATUS2_ASAT_ANALOG) != 0; +} + +/* Returns false if either bank's I2C read failed. Both results used to be + * discarded — the second assignment overwrote the first and nothing was ever + * tested — which left a dead bus indistinguishable from a dark reading. That + * matters more now: cmd 35 divides by gain and integration time, so junk counts + * come out amplified rather than obviously zero. */ +bool dual_exposure(as7341_gain_t gain1,as7341_gain_t gain2, uint16_t readings_buffer[], + bool *analog_saturated = nullptr){ if (!as7341_setup){ check_AS7341(); } - - as7341.setATIME(99); - as7341.setASTEP(499); + + as7341.setATIME(SPEC_ATIME); + as7341.setASTEP(SPEC_ASTEP); as7341.setGain(gain1); as7341.setSMUXLowChannels(true); // Configure SMUX to read low channels @@ -147,13 +168,19 @@ void dual_exposure(as7341_gain_t gain1,as7341_gain_t gain2, uint16_t readings_bu Adafruit_BusIO_Register channel_data_reg = Adafruit_BusIO_Register(as7341.i2c_dev, AS7341_CH0_DATA_L, 2); bool low_success = channel_data_reg.read((uint8_t *)readings_buffer, 12); + // Sampled per bank, while the flag still refers to the integration just read. + bool saturated = as7341_analog_saturated(); as7341.setGain(gain2); as7341.setSMUXLowChannels(false); // Configure SMUX to read low channels as7341.enableSpectralMeasurement(true); // Start integration as7341.delayForData(0); // I'll wait for you for all time - low_success = channel_data_reg.read((uint8_t *)&readings_buffer[6], 12); + const bool high_success = channel_data_reg.read((uint8_t *)&readings_buffer[6], 12); + saturated = saturated || as7341_analog_saturated(); + + if (analog_saturated != nullptr) *analog_saturated = saturated; + return low_success && high_success; } void cali_PAR(){ @@ -186,33 +213,78 @@ void cali_PAR(){ return; } -double get_PAR(uint16_t *calc_spec){ +/* dual_exposure() returns the two SMUX banks back to back: the low bank lands in + * spec[0..5] (F1-F4, Clear, NIR) and the high bank in spec[6..11] (F5-F8, Clear, + * NIR). Every frontend reports the ten channels in wavelength order, so map once + * here instead of restating the index arithmetic at each call site. */ +static const uint8_t SPEC_RAW_INDEX[10] = {0, 1, 2, 3, 6, 7, 8, 9, 11, 10}; + +/* Per-channel response weights. calc_spec[9] (Clear) has always been reported + * unweighted; it is spelled 1 here rather than left implicit. */ +static const uint8_t SPEC_WEIGHT[10] = { + Spec_COE1, Spec_COE2, Spec_COE3, Spec_COE4, Spec_COE5, + Spec_COE6, Spec_COE7, Spec_COE8, Spec_COE9, 1}; + +/* Unscaled acquisition. This is where PAR is actually computed, deliberately + * from the raw counts in a uint32 accumulator: the weighted sum peaks at + * 50000 * 73 = 3.65e6, which the old float accumulator handled fine but the + * uint16 calc_spec[] it summed did not — a wrapped channel used to drag PAR + * down with it. Sum, coefficients and operation order are otherwise unchanged, + * so PAR is bit-identical to the pre-fix value whenever nothing overflowed. */ +double get_PAR_raw(spec_raw_t *out){ if (!as7341_setup){ check_AS7341(); } - uint16_t spec[12]; + uint16_t spec[12] = {0}; float calc_par = 0; + bool analog_saturated = false; + + const bool ok = dual_exposure(AS7341_GAIN_2X, AS7341_GAIN_2X, spec, &analog_saturated); - //uint16_t calc_spec[10]; + out->atime = SPEC_ATIME; + out->astep = SPEC_ASTEP; + out->gain_low = AS7341_GAIN_2X; + out->gain_high = AS7341_GAIN_2X; + out->sat_mask = 0; + out->analog_saturated = analog_saturated; + out->fault = !ok; - dual_exposure(AS7341_GAIN_2X, AS7341_GAIN_2X, spec); + /* From the reported exposure, not SPEC_FULL_SCALE: the macro is only correct + * while ATIME/ASTEP stay pinned, and reporting them exists so they need not. */ + const uint32_t full_scale = ((uint32_t) out->atime + 1U) * ((uint32_t) out->astep + 1U); - calc_spec[0] = spec[0] * Spec_COE1 / 2; - calc_spec[1] = spec[1] * Spec_COE2 / 2; - calc_spec[2] = spec[2] * Spec_COE3 / 2; - calc_spec[3] = spec[3] * Spec_COE4 / 2; - calc_spec[4] = spec[6] * Spec_COE5 / 2; - calc_spec[5] = spec[7] * Spec_COE6 / 2; - calc_spec[6] = spec[8] * Spec_COE7 / 2; - calc_spec[7] = spec[9] * Spec_COE8 / 2; - calc_spec[8] = spec[11] * Spec_COE9 / 2; - calc_spec[9] = spec[10] / 2; + uint32_t weighted_sum = 0; + uint32_t nir_weighted = 0; - for (uint8_t n = 0; n < 8; n++){ - calc_par += calc_spec[n]; + for (uint8_t n = 0; n < 10; n++){ + const uint16_t counts = spec[SPEC_RAW_INDEX[n]]; + out->raw[n] = counts; + if (counts >= full_scale) out->sat_mask |= (uint16_t) (1U << n); + if (n < 8) weighted_sum += (uint32_t) counts * SPEC_WEIGHT[n]; + else if (n == 8) nir_weighted = (uint32_t) counts * SPEC_WEIGHT[n]; } - calc_par = calc_par * 0.006 - calc_spec[8] * 0.0075; - return calc_par * PAR_OFFSET * 2; + + calc_par = weighted_sum; + calc_par = calc_par * 0.006 - nir_weighted * 0.0075; + return calc_par * PAR_OFFSET; +} + +/* Scaled view for the frozen 16-bit channel words (binary cmd 31, text PAR / + * get_par, JSON par / par_raw). counts * Spec_COE* exceeds 16 bits from ~11% of + * ADC full scale, and the old assignment let it wrap silently to near zero — a + * bright-light reading looked like a dark one. Saturate instead: a pegged 65535 + * is monotonic and visibly at the rail, and it cannot be mistaken for darkness. + * The returned PAR comes from the unclamped counts, so it stays correct past the + * point where these words peg. Hosts wanting unclamped data use cmd 35. */ +double get_PAR(uint16_t *calc_spec){ + spec_raw_t raw; + const double par = get_PAR_raw(&raw); + + for (uint8_t n = 0; n < 10; n++){ + const uint32_t scaled = (uint32_t) raw.raw[n] * SPEC_WEIGHT[n]; + calc_spec[n] = (scaled > 0xFFFFU) ? 0xFFFFU : (uint16_t) scaled; + } + return par; } @@ -275,6 +347,4 @@ void as7431_blink(uint8_t n, uint8_t intensity){ as7431_reg_write(0xA9, 0x0); return; -} - - +} \ No newline at end of file diff --git a/src/src/as7341/spec_meas.h b/src/src/as7341/spec_meas.h new file mode 100644 index 0000000..0fe5745 --- /dev/null +++ b/src/src/as7341/spec_meas.h @@ -0,0 +1,113 @@ +#ifndef _SPEC_MEAS_H_ +#define _SPEC_MEAS_H_ +/* This header went unguarded while it held only macros, externs and prototypes, + * all of which tolerate repeated inclusion. spec_raw_t does not, and several + * translation units pull this in twice (directly and via PAM.h). */ +#include "Adafruit_AS7341.h" +#include "../pin_config.h" + +#define PAR_COE1 -0.515 +#define PAR_COE2 0.801 +#define PAR_COE3 -0.385 +#define PAR_COE4 -0.202 +#define PAR_COE5 0.301 +#define PAR_COE6 0.183 +#define PAR_COE7 0.055 +#define PAR_COE8 0.081 +#define PAR_COE9 -0.033 + +#define Spec_COE1 12 +#define Spec_COE2 10 +#define Spec_COE3 11 +#define Spec_COE4 10 +#define Spec_COE5 10 +#define Spec_COE6 9 +#define Spec_COE7 7 +#define Spec_COE8 4 +#define Spec_COE9 1 + +#define PAR_OFFSET 4 + +/* Acquisition parameters dual_exposure() programs for every spectral read. + * Full-scale ADC count is (SPEC_ATIME + 1) * (SPEC_ASTEP + 1) = 50000 — below + * the 16-bit register ceiling, which is why *unscaled* counts always fit a + * uint16 while the Spec_COE*-weighted ones overflow from ~11% of full scale + * (Spec_COE1 = 12 wraps above 5461 counts). One definition so the parameters + * reported to the host cannot drift from the ones actually programmed. */ +#define SPEC_ATIME 99 +#define SPEC_ASTEP 499 +#define SPEC_FULL_SCALE (((uint32_t) SPEC_ATIME + 1) * ((uint32_t) SPEC_ASTEP + 1)) + +/* Integration-time tick, in MILLISECONDS. Three conventions are in circulation + * (ams AN000633 and the correction workbook use ms; miniPar's firmware uses + * seconds; cmd 35's original comment prescribed no tick at all). ms is the one + * every published ams constant is expressed in — the dark offsets and the 721x10 + * reconstruction matrix both — so picking anything else means fixing up every + * vendor number, in opposite directions for offsets and weights. Sanity anchor: + * ATIME 99 / ASTEP 499 must give tint = 139 ms. */ +#define SPEC_TICK_MS 2.78e-3f + +/* Integration time in ms for a given ATIME/ASTEP pair. Takes the *reported* + * values rather than the SPEC_* macros so the arithmetic stays correct if the + * exposure ever stops being pinned — which is the whole reason cmd 35 puts them + * on the wire. */ +static inline float spec_tint_ms(uint8_t atime, uint16_t astep) { + return ((float) atime + 1.0f) * ((float) astep + 1.0f) * SPEC_TICK_MS; +} + +/* as7341_gain_t is an ORDINAL, not a multiplier: n means 0.5 * 2^n, so ordinal + * 0 is 0.5x and ordinal 10 is 512x. Dividing counts by the enum value directly + * would divide by ZERO at AS7341_GAIN_0_5X and be silently wrong at every other + * setting except ordinal 2 — which happens to be the pinned AS7341_GAIN_2X, so + * the mistake is invisible in every test at the current exposure. The wire + * carries the ordinal (compact, and the host already derives a multiplier); + * every divide goes through here. Lives in spec_meas rather than the vendored + * Adafruit driver, whose own ordinal->multiplier switch is dead code. */ +static inline float as7341_gain_multiplier(uint8_t gain_ordinal) { + if (gain_ordinal > AS7341_GAIN_512X) gain_ordinal = AS7341_GAIN_512X; + return 0.5f * (float) (1UL << gain_ordinal); +} + +/* One spectral read in unscaled counts, plus the settings it was taken under. + * A host needs gain/atime/astep to normalise counts across exposures, and will + * need them per-measurement the day autoranging lands. We report what we wrote + * rather than reading the registers back: getGain()/getATIME()/getASTEP() each + * cost an I2C transaction to return a value we already know. */ +typedef struct { + uint16_t raw[10]; // F1,F2,F3,F4,F5,F6,F7,F8,NIR,Clear — unscaled counts + uint16_t astep; + uint8_t atime; + /* Gain is per SMUX bank, and which channels belong to which bank is not what + * the SMUX setup functions are *named*. Both configurations sample Clear and + * NIR, so the buffer holds two copies of each; SPEC_RAW_INDEX reports slots 8 + * and 9 from spec[11] and spec[10] — the HIGH read. So NIR and Clear divide + * by gain_high, not gain_low. Latent today (both banks run at 2X), a silent + * data error the moment autoranging makes them differ. */ + uint8_t gain_low; // as7341_gain_t ordinal for F1-F4 + uint8_t gain_high; // as7341_gain_t ordinal for F5-F8, NIR and Clear + uint16_t sat_mask; // bit i = channel i reached digital full scale + bool analog_saturated; // AS7341 ASAT: pegged below digital full scale + bool fault; // an I2C channel read failed — treat the counts as junk +} spec_raw_t; + +extern Adafruit_AS7341 as7341; + +extern bool as7341_setup; +bool check_AS7341(); +void AS_LED_Current(uint16_t current); +void AS_LED_ON(); +void AS_LED_OFF(); + +void AS_all_channel(uint16_t T1, uint16_t T2, uint16_t *spec); +void AS_all_channel(uint16_t T1, uint16_t T2); +double get_PAR(); +double get_PAR(uint16_t *spec); +double get_PAR_raw(spec_raw_t *out); + + + +uint8_t as7431_reg_write(uint8_t reg, uint8_t data); +uint8_t as7431_reg_read(uint8_t reg, uint8_t* data, uint16_t len); +void as7431_blink(uint8_t n, uint8_t intensity); + +#endif // _SPEC_MEAS_H_ diff --git a/src/devices_init.cpp b/src/src/devices_init.cpp similarity index 100% rename from src/devices_init.cpp rename to src/src/devices_init.cpp diff --git a/src/devices_init.h b/src/src/devices_init.h similarity index 100% rename from src/devices_init.h rename to src/src/devices_init.h diff --git a/src/mlx90632/mlx_dev.cpp b/src/src/mlx90632/mlx_dev.cpp similarity index 96% rename from src/mlx90632/mlx_dev.cpp rename to src/src/mlx90632/mlx_dev.cpp index 44fb00c..4803b60 100644 --- a/src/mlx90632/mlx_dev.cpp +++ b/src/src/mlx90632/mlx_dev.cpp @@ -726,29 +726,4 @@ mlx90632_meas_t mlx90632_get_refresh_rate(void) return (mlx90632_meas_t)MLX90632_REFRESH_RATE(meas1); } -int32_t mlx90632_check_and_set_refresh_rate_16Hz(void) -{ - int32_t ret; - uint16_t meas1, meas2; - ret = mlx90632_i2c_read(MLX90632_EE_MEDICAL_MEAS1, &meas1); - if (ret < 0) - return ret; - ret = mlx90632_i2c_read(MLX90632_EE_MEDICAL_MEAS2, &meas2); - if (ret < 0) - return ret; - - // Serial.printf("MEAS_1: 0x%04X, MEAS_2: 0x%04X\n", meas1, meas2); - - if (meas1 != 0x850D){ - ret = mlx90632_write_eeprom(MLX90632_EE_MEDICAL_MEAS1, 0x850D); - // Serial.printf("Write MEAS_1 = %d\n", ret); - } - if (meas2 != 0x851D){ - ret = mlx90632_write_eeprom(MLX90632_EE_MEDICAL_MEAS2, 0x851D); - // Serial.printf("Write MEAS_2 = %d\n", ret); - } - - return 0; -} - ///@} diff --git a/src/mlx90632/mlx_dev.h b/src/src/mlx90632/mlx_dev.h similarity index 99% rename from src/mlx90632/mlx_dev.h rename to src/src/mlx90632/mlx_dev.h index bda2a12..da194eb 100644 --- a/src/mlx90632/mlx_dev.h +++ b/src/src/mlx90632/mlx_dev.h @@ -453,7 +453,6 @@ int32_t mlx90632_set_refresh_rate(mlx90632_meas_t measRate); */ mlx90632_meas_t mlx90632_get_refresh_rate(void); int32_t mlx90632_get_meas_type(void); -int32_t mlx90632_check_and_set_refresh_rate_16Hz(void); ///@} diff --git a/src/mlx90632/u_mlx.cpp b/src/src/mlx90632/u_mlx.cpp similarity index 98% rename from src/mlx90632/u_mlx.cpp rename to src/src/mlx90632/u_mlx.cpp index b48d5f1..a0896cb 100644 --- a/src/mlx90632/u_mlx.cpp +++ b/src/src/mlx90632/u_mlx.cpp @@ -119,8 +119,8 @@ bool mlx_init(void){ mlx_cali_Ka = mlx90632_i2c_read16(0x242F); mlx_cali_Ha = mlx90632_i2c_read16(0x2481); mlx_cali_Hb = mlx90632_i2c_read16(0x2482); - // mlx90632_set_refresh_rate(MLX90632_MEAS_HZ_16); - mlx90632_check_and_set_refresh_rate_16Hz(); + mlx90632_set_refresh_rate(MLX90632_MEAS_HZ_16); + mlx90632_set_emissivity(mlx_emissivity); ESP_LOGI(TAG, "MLX90632 initate OK"); diff --git a/src/mlx90632/u_mlx.h b/src/src/mlx90632/u_mlx.h similarity index 100% rename from src/mlx90632/u_mlx.h rename to src/src/mlx90632/u_mlx.h diff --git a/src/pin_config.h b/src/src/pin_config.h similarity index 100% rename from src/pin_config.h rename to src/src/pin_config.h diff --git a/upload.bat b/src/upload.bat similarity index 97% rename from upload.bat rename to src/upload.bat index 3959e8d..53024a8 100644 --- a/upload.bat +++ b/src/upload.bat @@ -1,2 +1 @@ -..\esptool.exe --chip esp32c3 --port "COM10" --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode keep --flash_freq keep --flash_size keep 0x0 "../tmp/ambit-1.ino.bootloader.bin" 0x8000 "../tmp/ambit-1.ino.partitions.bin" 0xe000 "%localappdata%/Arduino15\packages\esp32\hardware\esp32\3.1.1/tools/partitions/boot_app0.bin" 0x10000 "../tmp/ambit-1.ino.bin" -pause \ No newline at end of file +..\esptool.exe --chip esp32c3 --port "COM10" --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode keep --flash_freq keep --flash_size keep 0x0 "../tmp/ambit-1.ino.bootloader.bin" 0x8000 "../tmp/ambit-1.ino.partitions.bin" 0xe000 "%localappdata%/Arduino15\packages\esp32\hardware\esp32\3.1.1/tools/partitions/boot_app0.bin" 0x10000 "../tmp/ambit-1.ino.bin" \ No newline at end of file diff --git a/uploader.py b/src/uploader.py similarity index 100% rename from uploader.py rename to src/uploader.py diff --git a/src/wrench.cpp b/src/wrench.cpp deleted file mode 100644 index 537226a..0000000 --- a/src/wrench.cpp +++ /dev/null @@ -1,17420 +0,0 @@ -#include "wrench.h" -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ -#ifndef _UTILS_H -#define _UTILS_H -/*------------------------------------------------------------------------------*/ - - -int wr_itoa( int i, char* string, size_t len ); -int wr_ftoa( float f, char* string, size_t len ); - -unsigned char* wr_pack16( int16_t i, unsigned char* buf ); -unsigned char* wr_pack32( int32_t l, unsigned char* buf ); - -#ifndef WRENCH_WITHOUT_COMPILER - -//----------------------------------------------------------------------------- -template class WRarray -{ -private: - - T* m_list; - unsigned int m_elementsTotal; - unsigned int m_elementsAllocated; - -public: - - //------------------------------------------------------------------------------ - void clear() { delete[] m_list; m_list = 0; m_elementsTotal = 0; m_elementsAllocated = 0; } - unsigned int count() const { return m_elementsAllocated; } - void setCount( unsigned int count ) - { - if ( count >= m_elementsTotal ) - { - alloc( count + 1 ); - } - m_elementsAllocated = count; - } - - //------------------------------------------------------------------------------ - unsigned int remove( unsigned int location, unsigned int count =1 ) - { - if ( location >= m_elementsAllocated ) - { - return m_elementsAllocated; - } - - unsigned int newCount; - if ( count > m_elementsAllocated ) - { - if ( location == 0 ) - { - clear(); - return 0; - } - else - { - newCount = location + 1; - } - } - else - { - newCount = m_elementsAllocated - count; - } - - T* newArray = new T[newCount]; - - if ( location == 0 ) - { - for( unsigned int i=0; i<(unsigned int)newCount; ++i ) - { - newArray[i] = m_list[i+count]; - } - } - else - { - unsigned int j = 0; - - unsigned int skipEnd = location + count; - for( unsigned int i=0; i= skipEnd ) - { - newArray[j++] = m_list[i]; - } - } - } - - delete[] m_list; - m_list = newArray; - m_elementsAllocated = newCount; - m_elementsTotal = newCount; - - return m_elementsAllocated; - } - - //------------------------------------------------------------------------------ - void alloc( const unsigned int size ) - { - if ( size > m_elementsTotal ) - { - unsigned int newSize = size + (size/2) + 1; - T* newArray = new T[newSize]; - - if ( m_list ) - { - for( unsigned int i=0; i 0 ) { --m_elementsAllocated; } } - - T& append() { return get( m_elementsAllocated ); } - T& get( const unsigned int l ) - { - if ( l >= m_elementsTotal ) - { - alloc(l + 1); - m_elementsAllocated = l + 1; - } - else if ( l >= m_elementsAllocated ) - { - m_elementsAllocated = l + 1; - } - - return m_list[l]; - } - - //------------------------------------------------------------------------------ - WRarray( const WRarray& A ) - { - clear(); - m_list = new T[A.m_elementsAllocated]; - for( unsigned int i=0; i class WRHashTable -{ -public: - WRHashTable( int sizeHint =0 ) - { - for( uint16_t i=0; c_primeTable[i]; ++i ) - { - if ( sizeHint < c_primeTable[i] ) - { - m_mod = (int)c_primeTable[i]; - m_list = new Node[m_mod]; - break; - } - } - } - ~WRHashTable() { delete[] m_list; } - - //------------------------------------------------------------------------------ - void clear() - { - delete[] m_list; - m_mod = (int)c_primeTable[0]; - m_list = new Node[m_mod]; - for( int i=0; i= m_mod ) - { - m_mod = newMod; - delete[] m_list; - m_list = newList; - return set( hash, value ); - } - - delete[] newList; // try again - } - } - - struct Node - { - T value; - uint32_t hash; - Node() { hash = 0; } - }; - - friend struct WRCompilationContext; - -private: - - int m_mod; - Node* m_list; -}; - -#endif - -WRGCObject* wr_growValueArray( WRGCObject* va, int newSize ); - -#define IS_SVA_VALUE_TYPE(V) ((V)->m_type & 0x1) - -#define INIT_AS_LIB_CONST 0xFFFFFFFC -#define INIT_AS_DEBUG_BREAK (((uint32_t)WR_EX) | ((uint32_t)WR_EX_DEBUG_BREAK<<24)) -#define INIT_AS_ARRAY (((uint32_t)WR_EX) | ((uint32_t)WR_EX_ARRAY<<24)) -#define INIT_AS_USR (((uint32_t)WR_EX) | ((uint32_t)WR_EX_USR<<24)) -#define INIT_AS_RAW_ARRAY (((uint32_t)WR_EX) | ((uint32_t)WR_EX_RAW_ARRAY<<24)) -#define INIT_AS_ARRAY_MEMBER (((uint32_t)WR_EX) | ((uint32_t)WR_EX_ARRAY_MEMBER<<24)) -#define INIT_AS_STRUCT (((uint32_t)WR_EX) | ((uint32_t)WR_EX_STRUCT<<24)) -#define INIT_AS_HASH_TABLE (((uint32_t)WR_EX) | ((uint32_t)WR_EX_HASH_TABLE<<24)) -#define INIT_AS_ITERATOR (((uint32_t)WR_EX) | ((uint32_t)WR_EX_ITERATOR<<24)) - -#define INIT_AS_REF WR_REF -#define INIT_AS_INT WR_INT -#define INIT_AS_FLOAT WR_FLOAT - -#define ENCODE_ARRAY_ELEMENT_TO_P2(E) ((E)<<8) -#define DECODE_ARRAY_ELEMENT_FROM_P2(E) (((E)&0x1FFFFF00) >> 8) - -#define IS_EXARRAY_TYPE(P) ((P)&0xC0) -#define EX_RAW_ARRAY_SIZE_FROM_P2(P) (((P)&0x1FFFFF00) >> 8) -#define IS_EX_SINGLE_CHAR_RAW_P2(P) ((P) == (((uint32_t)WR_EX) | (((uint32_t)WR_EX_RAW_ARRAY<<24)) | (1<<8))) - -int wr_addI( int a, int b ); - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ -#ifndef _SERIALIZER_H -#define _SERIALIZER_H -/*------------------------------------------------------------------------------*/ - -struct WRValue; -struct WRContext; - -//------------------------------------------------------------------------------ -class WRValueSerializer -{ -public: - - WRValueSerializer() : m_pos(0), m_size(0), m_buf(0) {} - WRValueSerializer( const char* data, const int size ) : m_pos(0), m_size(size), m_buf((char *)malloc(size)) { memcpy(m_buf, data, size); } - ~WRValueSerializer() { free(m_buf); } - - void getOwnership( char** buf, int* len ) - { - *buf = m_buf; - *len = m_pos; - m_buf = 0; - } - - int size() const { return m_pos; } - - bool read( char* data, const int size ) - { - if ( m_pos + size > m_size ) - { - return false; - } - - memcpy( data, m_buf + m_pos, size ); - m_pos += size; - return true; - } - - void write( const char* data, const int size ) - { - if ( m_pos + size >= m_size ) - { - m_size += (size*2) + 8; - m_buf = (char *)realloc( m_buf, m_size ); - } - - memcpy( m_buf + m_pos, data, size ); - m_pos += size; - } - -private: - - int m_pos; - int m_size; - char* m_buf; -}; - -bool wr_serialize( WRValueSerializer& serializer, const WRValue& value ); -bool wr_deserialize( WRValue& value, WRValueSerializer& serializer, WRContext* context ); - -#endif - -#ifndef SIMPLE_LL_H -#define SIMPLE_LL_H -/*------------------------------------------------------------------------------*/ - -// for queue's and stacks implemented as a LL - -//----------------------------------------------------------------------------- -template class SimpleLL -{ -public: - - SimpleLL() : m_iter(0), m_head(0), m_tail(0) {} - ~SimpleLL() { clear(); } - - //------------------------------------------------------------------------------ - L* first() { return m_head ? &(m_iter = m_head)->item : 0; } - L* next() { return m_iter ? &((m_iter = m_iter->next)->item) : 0; } - - //------------------------------------------------------------------------------ - L* operator[]( const int i ) - { - int cur = 0; - for( Node* N = m_head; N ; N = N->next, ++cur ) - { - if( cur == i ) - { - return &(N->item); - } - } - - return 0; - } - - //------------------------------------------------------------------------------ - L* addHead() - { - if ( !m_head ) - { - m_head = new Node; - m_tail = m_head; - } - else - { - Node* N = new Node( m_head ); - m_head = N; - } - return &(m_head->item); - } - //------------------------------------------------------------------------------ - L* addTail() - { - if ( !m_tail ) - { - return addHead(); - } - else - { - Node* N = new Node; - m_tail->next = N; - m_tail = N; - return &(m_tail->item); - } - } - - //------------------------------------------------------------------------------ - L* head() { return m_head ? &(m_head->item) : 0; } - L* tail() { return m_tail ? &(m_tail->item) : 0; } - - //------------------------------------------------------------------------------ - void popHead() - { - if ( m_head ) - { - if ( m_tail == m_head ) - { - delete m_head; - m_head = 0; - m_tail = 0; - } - else - { - Node* N = m_head; - m_head = m_head->next; - delete N; - } - } - } - - //------------------------------------------------------------------------------ - void popTail() - { - if ( m_head == m_tail ) - { - clear(); - } - else - { - for( Node* N = m_head; N; N = N->next ) - { - if ( N->next == m_tail ) - { - delete N->next; - N->next = 0; - m_tail = N; - break; - } - } - } - } - - //------------------------------------------------------------------------------ - void clear() { while( m_head ) { popHead(); } } - -private: - - struct Node - { - Node( Node* n=0 ) : next(n) {} - L item; - Node *next; - }; - - Node* m_iter; - Node* m_head; - Node* m_tail; -}; - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#ifndef _GC_ARRAY_H -#define _GC_ARRAY_H - -#include "wrench.h" - -#include - -/* the what? in order for hash '0' to be both the number (integers are their own hashes) AND "no hash" I intgroduce a scrambler - constant that is XORed with all hashes. That way "0" is the scrambler itself, and actual 0 is the null hash. The only - danger with this is if your string happens to collide with the scrambler value, a 1:4bil chance but that same possibility - exists with using null as the null hash, thats what bitcoin is :) - I guess it would be annoying if you wanted to use the numeric value of the scrambler as an integer hash key, so I chose - a very large+negative value to minimize that risk*/ -#define HASH_SCRAMBLER 0xA656ABAB // 2790697899 or -1504269397 or -7.44788182526385e-16 all unlikely intentional constants - - -//------------------------------------------------------------------------------ -class WRGCObject -{ -public: - - // the order here matters for data alignment - -#if (__cplusplus <= 199711L) - int8_t m_type; // carries the type -#else - WRGCObjectType m_type; -#endif - - int8_t m_skipGC; - uint16_t m_mod; - uint32_t m_size; - - union - { - uint32_t* m_hashTable; - const unsigned char* m_ROMHashTable; - WRContext* m_creatorContext; - }; - - union - { - const void* m_constData; - void* m_data; - int* m_Idata; - char* m_SCdata; - unsigned char* m_Cdata; - WRValue* m_Vdata; - float* m_Fdata; - }; - - union - { - WRGCObject* m_next; // for gc - void* m_vNext; - }; - - - //------------------------------------------------------------------------------ - void init( const unsigned int size, const WRGCObjectType type ) - { - memset( (unsigned char*)this, 0, sizeof(WRGCObject) ); - - if ( (m_type = type) == SV_VALUE ) - { - m_size = size; - m_Vdata = (WRValue*)malloc( m_size * sizeof(WRValue) ); - } - else if ( m_type == SV_CHAR ) - { - m_size = size; - m_Cdata = (unsigned char*)malloc( m_size + 1 ); - m_Cdata[size] = 0; - } - else - { - growHash( 0, size ); - } - } - - //------------------------------------------------------------------------------ - void clear() - { - if ( m_type == SV_VALUE || m_type == SV_CHAR ) - { - free( m_Cdata ); - } - else - { - free( m_hashTable ); - } - } - - //------------------------------------------------------------------------------ - WRValue* getAsRawValueHashTable( const uint32_t hash ) - { -#ifdef WRENCH_COMPACT - uint32_t index = getIndexOfHit( hash, false ); - // m_Vdata MIGHT CHANGE and therefore the index has to be - // computed before the return, the obvious optimization of - // m_Vdata + getIndexOfHit( hash, false ); - // will FAIL - return m_Vdata + index; -#else - uint32_t index = hash % m_mod; - if ( m_hashTable[index] != hash ) - { - if ( m_hashTable[(index = (index + 1) % m_mod)] != hash ) - { - if ( m_hashTable[(index = (index + 1) % m_mod)] != hash ) - { - if ( m_hashTable[(index = (index + 1) % m_mod)] != hash ) - { - index = getIndexOfHit(hash, true); - } - } - } - } -#endif - return m_Vdata + index; - } - - //------------------------------------------------------------------------------ - WRValue* exists( const uint32_t hash, bool rawValueHash, bool removeIfPresent ) - { - const uint32_t h = rawValueHash ? hash : hash ^ HASH_SCRAMBLER; - uint32_t index = h % m_mod; - - if ( m_hashTable[index] != h ) - { - int tries = 3; - do - { - index = (index + 1) % m_mod; - if ( m_hashTable[index] == h ) - { - goto foundExists; - } - - } while( tries-- ); - - return 0; - } - -foundExists: - - if ( removeIfPresent ) - { - m_hashTable[index] = 0; - } - - return m_Vdata + index; - } - - //------------------------------------------------------------------------------ - void* get( const uint32_t l ) - { - int s = l < m_size ? l : m_size - 1; - - if ( m_type == SV_CHAR ) - { - return m_Cdata + s; - } - else if ( m_type == SV_HASH_TABLE ) - { - // zero remains "null hash" by scrambling. Which means - // a hash of HASH_SCRAMBLER will break the system. If you - // are reading this comment then I'm sorry, the only - // way out is to re-architect so this hash is not - // generated by your program - assert( l != HASH_SCRAMBLER ); - - uint32_t hash = l ^ HASH_SCRAMBLER; - - s = getIndexOfHit(hash, false) << 1; - } - else if ( m_type == SV_VOID_HASH_TABLE ) - { - return getAsRawValueHashTable( l ); - } - // else it must be SV_VALUE - - return m_Vdata + s; - } - -private: - - //------------------------------------------------------------------------------ - uint32_t getIndexOfHit( const uint32_t hash, const bool insert ) - { - uint32_t index = hash % m_mod; - if ( m_hashTable[index] == hash ) - { - return index; // immediate hits should be cheap - } - - int tries = 3; - do - { - if ( insert && m_hashTable[index] == 0 ) - { - m_hashTable[index] = hash; - return index; - } - - index = (index + 1) % m_mod; - - if ( m_hashTable[index] == hash ) - { - return index; - } - - } while( tries-- ); - - return insert ? growHash(hash) : getIndexOfHit( hash, true ); - } - - //------------------------------------------------------------------------------ - uint32_t growHash( const uint32_t hash, const uint16_t sizeHint =0 ) - { - // there was a collision with the passed hash, grow until the - // collision disappears - - uint16_t start = (sizeHint > m_mod) ? sizeHint : m_mod; - int t = 0; - for( ; c_primeTable[t] <= start ; ++t ); - - for(;;) - { -tryAgain: - int newMod = c_primeTable[t]; - - int newSize; - if ( m_type == SV_VOID_HASH_TABLE ) - { - newSize = newMod; - } - else - { - newSize = newMod << 1; - } - - int total = newMod*sizeof(uint32_t) + newSize*sizeof(WRValue); - uint32_t* proposed = (uint32_t*)malloc( total ); - - memset( (unsigned char *)proposed, 0, total ); - - proposed[ hash % newMod ] = hash; - - for( int h=0; hp2 = from->p2; - to->p = from->p; - from->p2 = INIT_AS_INT; - - // key - ++to; - ++from; - to->p2 = from->p2; - to->p = from->p; - from->p2 = INIT_AS_INT; - } - } - - free( oldHashTable ); - - m_Vdata = newValues; - - return getIndexOfHit( hash, true ); - } - } - - WRGCObject& operator= ( WRGCObject& A ); - WRGCObject(WRGCObject& A); -}; - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#ifndef _VM_H -#define _VM_H -/*------------------------------------------------------------------------------*/ - -//------------------------------------------------------------------------------ -struct WRFunction -{ - char arguments; - char frameSpaceNeeded; - char frameBaseAdjustment; - uint32_t hash; - union - { - const unsigned char* offset; - int offsetI; - }; -}; - -//------------------------------------------------------------------------------ -struct WRContext -{ - uint16_t gcPauseCount; - uint16_t globals; - - union - { - WRFunction* localFunctions; - uint32_t contextId; - }; - - union - { - const unsigned char* bottom; - const unsigned char* bytes; - }; - union - { - int bottomSize; - int bytesLen; - }; - - const unsigned char* stopLocation; - - WRGCObject* svAllocated; - -#ifdef WRENCH_INCLUDE_DEBUG_CODE - WRDebugServerInterface* debugInterface; -#endif - - WRState* w; - - WRGCObject registry; // the 'next' pointer in this registry is used as the context LL next - - void mark( WRValue* s ); - void gc( WRValue* stackTop ); - WRGCObject* getSVA( int size, WRGCObjectType type, bool init ); -}; - -//------------------------------------------------------------------------------ -struct WRState -{ - uint16_t stackSize; - int8_t err; - - WRValue* stack; - - WRContext* contextList; - - WRGCObject globalRegistry; -}; - -void wr_intValueToArray( const WRValue* array, int32_t I ); -void wr_floatValueToArray( const WRValue* array, float F ); -void wr_countOfArrayElement( WRValue* array, WRValue* target ); - -typedef void (*WRVoidFunc)( WRValue* to, WRValue* from ); -extern WRVoidFunc wr_assign[16]; -extern WRVoidFunc wr_SubtractAssign[16]; -extern WRVoidFunc wr_AddAssign[16]; -extern WRVoidFunc wr_ModAssign[16]; -extern WRVoidFunc wr_MultiplyAssign[16]; -extern WRVoidFunc wr_DivideAssign[16]; -extern WRVoidFunc wr_ORAssign[16]; -extern WRVoidFunc wr_ANDAssign[16]; -extern WRVoidFunc wr_XORAssign[16]; -extern WRVoidFunc wr_RightShiftAssign[16]; -extern WRVoidFunc wr_LeftShiftAssign[16]; -extern WRVoidFunc wr_postinc[4]; -extern WRVoidFunc wr_postdec[4]; -extern WRVoidFunc wr_pushIterator[4]; - - -typedef void (*WRVoidPlusFunc)( WRValue* to, WRValue* from, int add ); -extern WRVoidPlusFunc m_unaryPost[4]; - - -typedef int (*WRFuncIntCall)( int a, int b ); -typedef float (*WRFuncFloatCall)( float a, float b ); -typedef void (*WRTargetCallbackFunc)( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall I, WRFuncFloatCall F ); -typedef void (*WRFuncAssignFunc)( WRValue* to, WRValue* from, WRFuncIntCall I, WRFuncFloatCall F ); -extern WRFuncAssignFunc wr_FuncAssign[16]; -extern WRTargetCallbackFunc wr_funcBinary[16]; - -typedef bool (*WRCompareFuncIntCall)( int a, int b ); -typedef bool (*WRCompareFuncFloatCall)( float a, float b ); -typedef bool (*WRBoolCallbackReturnFunc)( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ); -extern WRBoolCallbackReturnFunc wr_Compare[16]; - - -typedef void (*WRTargetFunc)( WRValue* to, WRValue* from, WRValue* target ); -extern WRTargetFunc wr_AdditionBinary[16]; -extern WRTargetFunc wr_MultiplyBinary[16]; -extern WRTargetFunc wr_SubtractBinary[16]; -extern WRTargetFunc wr_DivideBinary[16]; -extern WRTargetFunc wr_LeftShiftBinary[16]; -extern WRTargetFunc wr_RightShiftBinary[16]; -extern WRTargetFunc wr_ModBinary[16]; -extern WRTargetFunc wr_ANDBinary[16]; -extern WRTargetFunc wr_ORBinary[16]; -extern WRTargetFunc wr_XORBinary[16]; - -typedef void (*WRStateFunc)( WRContext* c, WRValue* to, WRValue* from, WRValue* target ); -extern WRStateFunc wr_index[16]; -extern WRStateFunc wr_assignAsHash[4]; -extern WRStateFunc wr_assignToArray[16]; - -typedef bool (*WRReturnFunc)( WRValue* to, WRValue* from ); -extern WRReturnFunc wr_CompareEQ[16]; -extern WRReturnFunc wr_CompareGT[16]; -extern WRReturnFunc wr_CompareLT[16]; -extern WRReturnFunc wr_LogicalAND[16]; -extern WRReturnFunc wr_LogicalOR[16]; - -typedef void (*WRSingleTargetFunc)( WRValue* value, WRValue* target ); -extern WRSingleTargetFunc wr_negate[4]; - -typedef void (*WRUnaryFunc)( WRValue* value ); -extern WRUnaryFunc wr_preinc[4]; -extern WRUnaryFunc wr_predec[4]; -extern WRUnaryFunc wr_toInt[4]; -extern WRUnaryFunc wr_toFloat[4]; - -typedef uint32_t (*WRUint32Call)( WRValue* value ); -extern WRUint32Call wr_bitwiseNot[4]; - -typedef bool (*WRReturnSingleFunc)( WRValue* value ); -extern WRReturnSingleFunc wr_LogicalNot[4]; - -void wr_assignToHashTable( WRContext* c, WRValue* index, WRValue* value, WRValue* table ); - -extern WRReturnFunc wr_CompareEQ[16]; - -uint32_t wr_hash_read8( const void* dat, const int len ); -uint32_t wr_hashStr_read8( const char* dat ); - -// if the current + native match then great it's a simple read, it's -// only when they differ that we need bitshiftiness -#ifdef WRENCH_LITTLE_ENDIAN - - #define wr_x32(P) P - #define wr_x16(P) P - -#ifndef READ_32_FROM_PC - #ifdef WRENCH_UNALIGNED_READS - #define READ_32_FROM_PC(P) ((int32_t)(*(int32_t *)(P))) - #else - #define READ_32_FROM_PC(P) ((int32_t)((int32_t)*(P) | ((int32_t)*(P+1))<<8 | ((int32_t)*(P+2))<<16 | ((int32_t)*(P+3))<<24)) - #endif -#endif -#ifndef READ_16_FROM_PC - #ifdef WRENCH_UNALIGNED_READS - #define READ_16_FROM_PC(P) ((int16_t)(*(int16_t *)(P))) - #else - #define READ_16_FROM_PC(P) ((int16_t)((int16_t)*(P) | ((int16_t)*(P+1))<<8)) - #endif -#endif - -#else - - int32_t wr_x32( const int32_t val ); - int16_t wr_x16( const int16_t val ); - - #ifdef WRENCH_COMPACT - - #ifndef READ_32_FROM_PC - int32_t READ_32_FROM_PC_func( const unsigned char* P ); - #define READ_32_FROM_PC(P) READ_32_FROM_PC_func(P) - #endif - #ifndef READ_16_FROM_PC - int16_t READ_16_FROM_PC_func( const unsigned char* P ); - #define READ_16_FROM_PC(P) READ_16_FROM_PC_func(P) - #endif - - #else - - #ifndef READ_32_FROM_PC - #define READ_32_FROM_PC(P) ((int32_t)((int32_t)*(P) | ((int32_t)*(P+1))<<8 | ((int32_t)*(P+2))<<16 | ((int32_t)*(P+3))<<24)) - #endif - #ifndef READ_16_FROM_PC - #define READ_16_FROM_PC(P) ((int16_t)((int16_t)*(P) | ((int16_t)*(P+1))<<8)) - #endif - - #endif -#endif - - #ifndef READ_8_FROM_PC - #define READ_8_FROM_PC(P) (*(P)) - #endif - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ -#ifndef _OPCODE_H -#define _OPCODE_H -/*------------------------------------------------------------------------------*/ - -//------------------------------------------------------------------------------ -enum WROpcode -{ - O_RegisterFunction = 0, - - O_LiteralInt32, - O_LiteralZero, - O_LiteralFloat, - O_LiteralString, - - O_CallFunctionByHash, - O_CallFunctionByHashAndPop, - O_CallFunctionByIndex, - O_PushIndexFunctionReturnValue, - - O_CallLibFunction, - O_CallLibFunctionAndPop, - - O_NewObjectTable, - O_AssignToObjectTableByOffset, - - O_AssignToHashTableAndPop, - O_Remove, - O_HashEntryExists, - - O_PopOne, - O_ReturnZero, - O_Return, - O_Stop, - - O_Dereference, - O_Index, - O_IndexSkipLoad, - O_CountOf, - O_HashOf, - - O_StackIndexHash, - O_GlobalIndexHash, - O_LocalIndexHash, - - O_StackSwap, - O_SwapTwoToTop, - - O_LoadFromLocal, - O_LoadFromGlobal, - - O_LLValues, - O_LGValues, - O_GLValues, - O_GGValues, - - O_BinaryRightShiftSkipLoad, - O_BinaryLeftShiftSkipLoad, - O_BinaryAndSkipLoad, - O_BinaryOrSkipLoad, - O_BinaryXORSkipLoad, - O_BinaryModSkipLoad, - - O_BinaryMultiplication, - O_BinarySubtraction, - O_BinaryDivision, - O_BinaryRightShift, - O_BinaryLeftShift, - O_BinaryMod, - O_BinaryOr, - O_BinaryXOR, - O_BinaryAnd, - O_BinaryAddition, - - O_BitwiseNOT, - - O_RelativeJump, - O_RelativeJump8, - - O_BZ, - O_BZ8, - - O_LogicalAnd, - O_LogicalOr, - O_CompareLE, - O_CompareGE, - O_CompareGT, - O_CompareLT, - O_CompareEQ, - O_CompareNE, - - O_GGCompareGT, - O_GGCompareGE, - O_GGCompareLT, - O_GGCompareLE, - O_GGCompareEQ, - O_GGCompareNE, - - O_LLCompareGT, - O_LLCompareGE, - O_LLCompareLT, - O_LLCompareLE, - O_LLCompareEQ, - O_LLCompareNE, - - O_GSCompareEQ, - O_LSCompareEQ, - O_GSCompareNE, - O_LSCompareNE, - O_GSCompareGE, - O_LSCompareGE, - O_GSCompareLE, - O_LSCompareLE, - O_GSCompareGT, - O_LSCompareGT, - O_GSCompareLT, - O_LSCompareLT, - - O_GSCompareEQBZ, - O_LSCompareEQBZ, - O_GSCompareNEBZ, - O_LSCompareNEBZ, - O_GSCompareGEBZ, - O_LSCompareGEBZ, - O_GSCompareLEBZ, - O_LSCompareLEBZ, - O_GSCompareGTBZ, - O_LSCompareGTBZ, - O_GSCompareLTBZ, - O_LSCompareLTBZ, - - O_GSCompareEQBZ8, - O_LSCompareEQBZ8, - O_GSCompareNEBZ8, - O_LSCompareNEBZ8, - O_GSCompareGEBZ8, - O_LSCompareGEBZ8, - O_GSCompareLEBZ8, - O_LSCompareLEBZ8, - O_GSCompareGTBZ8, - O_LSCompareGTBZ8, - O_GSCompareLTBZ8, - O_LSCompareLTBZ8, - - O_LLCompareLTBZ, - O_LLCompareLEBZ, - O_LLCompareGTBZ, - O_LLCompareGEBZ, - O_LLCompareEQBZ, - O_LLCompareNEBZ, - - O_GGCompareLTBZ, - O_GGCompareLEBZ, - O_GGCompareGTBZ, - O_GGCompareGEBZ, - O_GGCompareEQBZ, - O_GGCompareNEBZ, - - O_LLCompareLTBZ8, - O_LLCompareLEBZ8, - O_LLCompareGTBZ8, - O_LLCompareGEBZ8, - O_LLCompareEQBZ8, - O_LLCompareNEBZ8, - - O_GGCompareLTBZ8, - O_GGCompareLEBZ8, - O_GGCompareGTBZ8, - O_GGCompareGEBZ8, - O_GGCompareEQBZ8, - O_GGCompareNEBZ8, - - O_PostIncrement, - O_PostDecrement, - O_PreIncrement, - O_PreDecrement, - - O_PreIncrementAndPop, - O_PreDecrementAndPop, - - O_IncGlobal, - O_DecGlobal, - O_IncLocal, - O_DecLocal, - - O_Assign, - O_AssignAndPop, - O_AssignToGlobalAndPop, - O_AssignToLocalAndPop, - O_AssignToArrayAndPop, - - O_SubtractAssign, - O_AddAssign, - O_ModAssign, - O_MultiplyAssign, - O_DivideAssign, - O_ORAssign, - O_ANDAssign, - O_XORAssign, - O_RightShiftAssign, - O_LeftShiftAssign, - - O_SubtractAssignAndPop, - O_AddAssignAndPop, - O_ModAssignAndPop, - O_MultiplyAssignAndPop, - O_DivideAssignAndPop, - O_ORAssignAndPop, - O_ANDAssignAndPop, - O_XORAssignAndPop, - O_RightShiftAssignAndPop, - O_LeftShiftAssignAndPop, - - O_LogicalNot, //X - O_Negate, - - O_LiteralInt8, - O_LiteralInt16, - - O_IndexLiteral8, - O_IndexLiteral16, - - O_IndexLocalLiteral8, - O_IndexGlobalLiteral8, - O_IndexLocalLiteral16, - O_IndexGlobalLiteral16, - - O_BinaryAdditionAndStoreGlobal, - O_BinarySubtractionAndStoreGlobal, - O_BinaryMultiplicationAndStoreGlobal, - O_BinaryDivisionAndStoreGlobal, - - O_BinaryAdditionAndStoreLocal, - O_BinarySubtractionAndStoreLocal, - O_BinaryMultiplicationAndStoreLocal, - O_BinaryDivisionAndStoreLocal, - - O_CompareBEQ, - O_CompareBNE, - O_CompareBGE, - O_CompareBLE, - O_CompareBGT, - O_CompareBLT, - - O_CompareBEQ8, - O_CompareBNE8, - O_CompareBGE8, - O_CompareBLE8, - O_CompareBGT8, - O_CompareBLT8, - - O_BLA, - O_BLA8, - O_BLO, - O_BLO8, - - O_LiteralInt8ToGlobal, - O_LiteralInt16ToGlobal, - O_LiteralInt32ToLocal, - O_LiteralInt8ToLocal, - O_LiteralInt16ToLocal, - O_LiteralFloatToGlobal, - O_LiteralFloatToLocal, - O_LiteralInt32ToGlobal, - - O_GGBinaryMultiplication, - O_GLBinaryMultiplication, - O_LLBinaryMultiplication, - - O_GGBinaryAddition, - O_GLBinaryAddition, - O_LLBinaryAddition, - - O_GGBinarySubtraction, - O_GLBinarySubtraction, - O_LGBinarySubtraction, - O_LLBinarySubtraction, - - O_GGBinaryDivision, - O_GLBinaryDivision, - O_LGBinaryDivision, - O_LLBinaryDivision, - - O_GC_Command, - - O_GPushIterator, - O_LPushIterator, - O_GGNextKeyValueOrJump, - O_GLNextKeyValueOrJump, - O_LGNextKeyValueOrJump, - O_LLNextKeyValueOrJump, - O_GNextValueOrJump, - O_LNextValueOrJump, - - O_Switch, - O_SwitchLinear, - - O_GlobalStop, - - O_ToInt, - O_ToFloat, - - O_LoadLibConstant, - O_InitArray, - - O_DebugInfo, - - // non-interpreted opcodes - O_HASH_PLACEHOLDER, - O_FUNCTION_CALL_PLACEHOLDER, - - O_LAST, -}; - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ -#ifndef _STR_H -#define _STR_H -/* ------------------------------------------------------------------------- */ - -#ifndef WRENCH_WITHOUT_COMPILER - -#include -#include -#include -#include -#include -#include - -#ifdef STR_FILE_OPERATIONS -#include -#endif - -const unsigned int c_sizeofBaseString = 15; // this lib tries not to use dynamic RAM unless it has to -const int c_cstrFormatBufferSize = 1024; // when formatting, this much stack space is reserved during the call - -//----------------------------------------------------------------------------- -class WRstr -{ -public: - WRstr() { m_len = 0; m_smallbuf[0] = 0; m_str = m_smallbuf; m_buflen = c_sizeofBaseString; } - WRstr( const WRstr& str) { m_len = 0; m_str = m_smallbuf; m_buflen = c_sizeofBaseString; set(str, str.size()); } - WRstr( const WRstr* str ) { m_len = 0; m_str = m_smallbuf; m_buflen = c_sizeofBaseString; if ( str ) { set(*str, str->size()); } } - WRstr( const char* s, const unsigned int len ) { m_len = 0; m_str = m_smallbuf; m_buflen = c_sizeofBaseString; set(s, len); } - WRstr( const char* s ) { m_len = 0; m_str = m_smallbuf; m_buflen = c_sizeofBaseString; set(s, (unsigned int)strlen(s)); } - WRstr( const char c) { m_len = 1; m_str = m_smallbuf; m_smallbuf[0] = c; m_smallbuf[1] = 0; m_buflen = c_sizeofBaseString; } - - ~WRstr() { if ( m_str != m_smallbuf ) delete[] m_str; } - - WRstr& clear() { m_len = 0; m_str[0] = 0; return *this; } - - WRstr& format( const char* format, ... ) { va_list arg; va_start( arg, format ); clear(); appendFormatVA( format, arg ); va_end( arg ); return *this; } - WRstr& formatVA( const char* format, va_list arg ) { clear(); return appendFormatVA(format, arg); } - WRstr& appendFormat( const char* format, ... ) { va_list arg; va_start( arg, format ); appendFormatVA( format, arg ); va_end( arg ); return *this; } - WRstr& appendFormatVA( const char* format, va_list arg ) - { - char buf[ c_cstrFormatBufferSize + 1 ]; - int len = vsnprintf( buf, c_cstrFormatBufferSize, format, arg ); - if ( len > 0 ) insert( buf, (unsigned int)len, m_len ); - return *this; - } - - unsigned int release( char** toBuf ) - { - unsigned int retLen = m_len; - - if ( m_str == m_smallbuf ) - { - *toBuf = new char[m_len + 1]; - memcpy( *toBuf, m_str, (m_len+1) ); - } - else - { - *toBuf = m_str; - m_str = m_smallbuf; - m_buflen = c_sizeofBaseString; - } - - clear(); - return retLen; - } - - inline WRstr& trim(); - - inline WRstr& alloc( const unsigned int characters, const bool preserveContents =true ); - - unsigned int size() const { return m_len; } // see length - unsigned int bufferSize() const { return m_buflen; } // see length - unsigned int c_size() { for( m_len=0; m_len m_len) ? clear() : truncate(m_len - e); } // remove 'x' trailing characters - - inline bool isMatch( const char* buf ) const; - - inline WRstr& insert( const char* buf, const unsigned int len, const unsigned int startPos =0 ); - inline WRstr& insert( const WRstr& s, const unsigned int startPos =0 ) { return insert(s.m_str, s.m_len, startPos); } - - inline WRstr& append( const char* buf, const unsigned int len ) { return insert(buf, len, m_len); } - inline WRstr& append( const char c ); - inline WRstr& append( const WRstr& s ) { return insert(s.m_str, s.m_len, m_len); } - - // define the usual suspects: - - const char& operator[]( const int l ) const { return get((unsigned int)l); } - const char& operator[]( const unsigned int l ) const { return get(l); } - char& operator[]( const int l ) { return get((unsigned int)l); } - char& operator[]( const unsigned int l ) { return get(l); } - - char& get( const unsigned int l ) - { - assert( l < m_buflen ); - return m_str[l]; - } - - const char& get( const unsigned int l ) const - { - assert( l < m_buflen ); - return m_str[l]; - } - - WRstr& operator += ( const WRstr& str ) { return append(str.m_str, str.m_len); } - WRstr& operator += ( const char* s ) { return append(s, (unsigned int)strlen(s)); } - WRstr& operator += ( const char c ) { return append(c); } - - WRstr& operator = ( const WRstr& str ) { if ( &str != this ) set(str, str.size()); return *this; } - WRstr& operator = ( const WRstr* str ) { if ( !str ) { clear(); } else if ( this != str ) { set(*str, str->size()); } return *this; } - WRstr& operator = ( const char* c ) { set(c, (unsigned int)strlen(c)); return *this; } - WRstr& operator = ( const char c ) { set(&c, 1); return *this; } - - friend bool operator == ( const WRstr& s1, const WRstr& s2 ) { return s1.m_len == s2.m_len && (strncmp(s1.m_str, s2.m_str, s1.m_len) == 0); } - friend bool operator == ( const char* z, const WRstr& s ) { return s.isMatch( z ); } - friend bool operator == ( const WRstr& s, const char* z ) { return s.isMatch( z ); } - friend bool operator != ( const WRstr& s1, const WRstr& s2 ) { return s1.m_len != s2.m_len || (strncmp(s1.m_str, s2.m_str, s1.m_len) != 0); } - friend bool operator != ( const WRstr& s, const char* z ) { return !s.isMatch( z ); } - friend bool operator != ( const char* z, const WRstr& s ) { return !s.isMatch( z ); } - - friend WRstr operator + ( const WRstr& str, const char* s) { WRstr T(str); T += s; return T; } - friend WRstr operator + ( const WRstr& str, const char c) { WRstr T(str); T += c; return T; } - friend WRstr operator + ( const char* s, const WRstr& str ) { WRstr T(s, (unsigned int)strlen(s)); T += str; return T; } - friend WRstr operator + ( const char c, const WRstr& str ) { WRstr T(c); T += str; return T; } - friend WRstr operator + ( const WRstr& str1, const WRstr& str2 ) { WRstr T(str1); T += str2; return T; } - -protected: - - operator char*() const { return m_str; } // prevent accidental use - - char *m_str; // first element so if the class is cast as a C and de-referenced it always works - - unsigned int m_buflen; // how long the buffer itself is - unsigned int m_len; // how long the string is in the buffer - char m_smallbuf[ c_sizeofBaseString + 1 ]; // small temporary buffer so a new/delete is not imposed for small strings -}; - -//----------------------------------------------------------------------------- -WRstr& WRstr::trim() -{ - unsigned int start = 0; - - // find start - for( ; start= m_buflen ) // only need to alloc if more space is requested than we have - { - char* newStr = new char[ characters + 1 ]; // create the space - - if ( preserveContents ) - { - memcpy( newStr, m_str, m_buflen ); // preserve whatever we had - } - - if ( m_str != m_smallbuf ) - { - delete[] m_str; - } - - m_str = newStr; - m_buflen = characters; - } - - return *this; -} - -#ifdef STR_FILE_OPERATIONS -//----------------------------------------------------------------------------- -bool WRstr::fileToBuffer( const char* fileName, const bool appendToBuffer ) -{ - if ( !fileName ) - { - return false; - } - -#ifdef _WIN32 - struct _stat sbuf; - int ret = _stat( fileName, &sbuf ); -#else - struct stat sbuf; - int ret = stat( fileName, &sbuf ); -#endif - - if ( ret != 0 ) - { - return false; - } - - FILE *infil = fopen( fileName, "rb" ); - if ( !infil ) - { - return false; - } - - if ( appendToBuffer ) - { - alloc( sbuf.st_size + m_len, true ); - m_str[ sbuf.st_size + m_len ] = 0; - ret = (int)fread( m_str + m_len, sbuf.st_size, 1, infil ); - m_len += sbuf.st_size; - } - else - { - alloc( sbuf.st_size, false ); - m_len = sbuf.st_size; - m_str[ m_len ] = 0; - ret = (int)fread( m_str, m_len, 1, infil ); - } - - fclose( infil ); - return ret == 1; -} - -//----------------------------------------------------------------------------- -bool WRstr::bufferToFile( const char* fileName, const bool append) const -{ - if ( !fileName ) - { - return false; - } - - FILE *outfil = append ? fopen( fileName, "a+b" ) : fopen( fileName, "wb" ); - if ( !outfil ) - { - return false; - } - - int ret = (int)fwrite( m_str, m_len, 1, outfil ); - fclose( outfil ); - - return (m_len == 0) || (ret == 1); -} -#endif - -//----------------------------------------------------------------------------- -WRstr& WRstr::truncate( const unsigned int newLen ) -{ - if ( newLen >= m_len ) - { - return *this; - } - - if ( newLen < c_sizeofBaseString ) - { - if ( m_str != m_smallbuf ) - { - m_buflen = c_sizeofBaseString; - memcpy( m_smallbuf, m_str, newLen ); - delete[] m_str; - m_str = m_smallbuf; - } - } - - m_str[ newLen ] = 0; - m_len = newLen; - - return *this; -} - -//----------------------------------------------------------------------------- -bool WRstr::isMatch( const char* buf ) const -{ - return strcmp( buf, m_str ) == 0; -} - -//----------------------------------------------------------------------------- -WRstr& WRstr::insert( const char* buf, const unsigned int len, const unsigned int startPos /*=0*/ ) -{ - if ( len == 0 ) // insert 0? done - { - return *this; - } - - alloc( m_len + len, true ); // make sure there is enough room for the new string - if ( startPos >= m_len ) - { - if ( buf ) - { - memcpy( m_str + m_len, buf, len ); - } - } - else - { - if ( startPos != m_len ) - { - memmove( m_str + len + startPos, m_str + startPos, m_len ); - } - - if ( buf ) - { - memcpy( m_str + startPos, buf, len ); - } - } - - m_len += len; - m_str[m_len] = 0; - - return *this; -} - -//----------------------------------------------------------------------------- -WRstr& WRstr::append( const char c ) -{ - if ( (m_len+1) >= m_buflen ) - { - alloc( ((m_len * 3) / 2) + 1, true ); // single-character, expect a lot more are coming so alloc some buffer space - } - m_str[ m_len++ ] = c; - m_str[ m_len ] = 0; - - return *this; -} - -const char* wr_asciiDump( const void* d, unsigned int len, WRstr& str, int markByte =-1 ); - -#endif -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ -#ifndef _OPCODE_STREAM_H -#define _OPCODE_STREAM_H -#ifndef WRENCH_WITHOUT_COMPILER -/*------------------------------------------------------------------------------*/ - -//----------------------------------------------------------------------------- -class WROpcodeStream -{ -public: - WROpcodeStream() { m_buf = 0; clear(); } - ~WROpcodeStream() { clear(); } - WROpcodeStream& clear() - { - m_len = 0; - delete[] m_buf; - m_buf = 0; - m_bufLen = 0; - return *this; - } - - unsigned int size() const - { - return m_len; - } - - WROpcodeStream (const WROpcodeStream &other ) { m_buf = 0; *this = other; } - WROpcodeStream& operator = ( const WROpcodeStream& str ) - { - clear(); - if ( str.m_len ) - { - *this += str; - } - return *this; - } - - WROpcodeStream& operator += ( const WROpcodeStream& stream ) { return append(stream.m_buf, stream.m_len); } - WROpcodeStream& operator += ( const unsigned char data ) { return append(&data, 1); } - WROpcodeStream& append( const unsigned char* data, const int size ) - { - if ( (size + m_len) >= m_bufLen ) - { - unsigned char* buf = m_buf; - m_bufLen = size + m_len + 16; - m_buf = new unsigned char[ m_bufLen ]; - if ( m_len ) - { - memcpy( m_buf, buf, m_len ); - delete[] buf; - } - } - - memcpy( m_buf + m_len, data, size ); - m_len += size; - return *this; - - } - - unsigned char* p_str( int offset =0 ) { return m_buf + offset; } - operator const unsigned char*() const { return m_buf; } - unsigned char& operator[]( const int l ) { return *p_str(l); } - - WROpcodeStream& shave( const unsigned int e ) - { - m_len -= e; - return *this; - } - - unsigned int release( unsigned char** toBuf ) - { - unsigned int retLen = m_len; - *toBuf = m_buf; - m_buf = 0; - clear(); - return retLen; - } - -private: - unsigned char *m_buf; - unsigned int m_len; - unsigned int m_bufLen; -}; - -#endif - -#endif/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#ifndef _CC_H -#define _CC_H -/*------------------------------------------------------------------------------*/ - -#ifndef WRENCH_WITHOUT_COMPILER - -//------------------------------------------------------------------------------ -enum WROperationType -{ - WR_OPER_PRE, - WR_OPER_BINARY, - WR_OPER_BINARY_COMMUTE, // binary but operation order doesn't matter - WR_OPER_POST, -}; - -//------------------------------------------------------------------------------ -struct WROperation -{ - const char* token; - int precedence; // higher number if lower precedence - WROpcode opcode; - bool leftToRight; - WROperationType type; - WROpcode alt; -}; - -//------------------------------------------------------------------------------ -// reference: -// https://en.cppreference.com/w/cpp/language/operator_precedence -const WROperation c_operations[] = -{ -// precedence L2R type alt - - { "==", 10, O_CompareEQ, true, WR_OPER_BINARY_COMMUTE, O_LAST }, - { "!=", 10, O_CompareNE, true, WR_OPER_BINARY_COMMUTE, O_LAST }, - { ">=", 9, O_CompareGE, true, WR_OPER_BINARY, O_CompareLE }, - { "<=", 9, O_CompareLE, true, WR_OPER_BINARY, O_CompareGE }, - { ">", 9, O_CompareGT, true, WR_OPER_BINARY, O_CompareLT }, - { "<", 9, O_CompareLT, true, WR_OPER_BINARY, O_CompareGT }, - { "&&", 14, O_LogicalAnd, true, WR_OPER_BINARY_COMMUTE, O_LAST }, - { "||", 15, O_LogicalOr, true, WR_OPER_BINARY_COMMUTE, O_LAST }, - - { "++", 3, O_PreIncrement, true, WR_OPER_PRE, O_LAST }, - { "++", 2, O_PostIncrement, true, WR_OPER_POST, O_LAST }, - - { "--", 3, O_PreDecrement, true, WR_OPER_PRE, O_LAST }, - { "--", 2, O_PostDecrement, true, WR_OPER_POST, O_LAST }, - - { ".", 2, O_HASH_PLACEHOLDER, true, WR_OPER_BINARY, O_LAST }, - - { "!", 3, O_LogicalNot, false, WR_OPER_PRE, O_LAST }, - { "~", 3, O_BitwiseNOT, false, WR_OPER_PRE, O_LAST }, - { "-", 3, O_Negate, false, WR_OPER_PRE, O_LAST }, - - { "+", 6, O_BinaryAddition, true, WR_OPER_BINARY_COMMUTE, O_LAST }, - { "-", 6, O_BinarySubtraction, true, WR_OPER_BINARY, O_LAST }, - { "*", 5, O_BinaryMultiplication,true, WR_OPER_BINARY_COMMUTE, O_LAST }, - { "/", 5, O_BinaryDivision, true, WR_OPER_BINARY, O_LAST }, - { "%", 6, O_BinaryMod, true, WR_OPER_BINARY, O_LAST }, - - { "|", 13, O_BinaryOr, true, WR_OPER_BINARY_COMMUTE, O_LAST }, - { "&", 11, O_BinaryAnd, true, WR_OPER_BINARY_COMMUTE, O_LAST }, - { "^", 11, O_BinaryXOR, true, WR_OPER_BINARY_COMMUTE, O_LAST }, - - { ">>", 7, O_BinaryRightShift, true, WR_OPER_BINARY, O_LAST }, - { "<<", 7, O_BinaryLeftShift, true, WR_OPER_BINARY, O_LAST }, - - { "+=", 16, O_AddAssign, true, WR_OPER_BINARY, O_LAST }, - { "-=", 16, O_SubtractAssign, true, WR_OPER_BINARY, O_LAST }, - { "%=", 16, O_ModAssign, true, WR_OPER_BINARY, O_LAST }, - { "*=", 16, O_MultiplyAssign, true, WR_OPER_BINARY, O_LAST }, - { "/=", 16, O_DivideAssign, true, WR_OPER_BINARY, O_LAST }, - { "|=", 16, O_ORAssign, true, WR_OPER_BINARY, O_LAST }, - { "&=", 16, O_ANDAssign, true, WR_OPER_BINARY, O_LAST }, - { "^=", 16, O_XORAssign, true, WR_OPER_BINARY, O_LAST }, - { ">>=", 16, O_RightShiftAssign, false, WR_OPER_BINARY, O_LAST }, - { "<<=", 16, O_LeftShiftAssign, false, WR_OPER_BINARY, O_LAST }, - - { "=", 16, O_Assign, false, WR_OPER_BINARY, O_LAST }, - - { "@i", 3, O_ToInt, false, WR_OPER_PRE, O_LAST }, - { "@f", 3, O_ToFloat, false, WR_OPER_PRE, O_LAST }, - - { "@[]", 2, O_Index, true, WR_OPER_POST, O_LAST }, - { "@init", 2, O_InitArray, true, WR_OPER_POST, O_LAST }, - - { "@macroBegin", 0, O_LAST, true, WR_OPER_POST, O_LAST }, - - { "._count", 2, O_CountOf, true, WR_OPER_POST, O_LAST }, - { "._hash", 2, O_HashOf, true, WR_OPER_POST, O_LAST }, - { "._remove", 2, O_Remove, true, WR_OPER_POST, O_LAST }, - { "._exists", 2, O_HashEntryExists, true, WR_OPER_POST, O_LAST }, - - { 0, 0, O_LAST, false, WR_OPER_PRE, O_LAST }, -}; -const int c_highestPrecedence = 17; // one higher than the highest entry above, things that happen absolutely LAST - -//------------------------------------------------------------------------------ -enum WRExpressionType -{ - EXTYPE_NONE =0, - EXTYPE_LITERAL, - EXTYPE_LIB_CONSTANT, - EXTYPE_LABEL, - EXTYPE_OPERATION, - EXTYPE_RESOLVED, - EXTYPE_BYTECODE_RESULT, -}; - -//------------------------------------------------------------------------------ -struct WRNamespaceLookup -{ - uint32_t hash; // hash of symbol - WRarray references; // where this symbol is referenced (loaded) in the bytecode - WRstr label; - - WRNamespaceLookup() { reset(0); } - void reset( uint32_t h ) - { - hash = h; - references.clear(); - } -}; - -//------------------------------------------------------------------------------ -struct BytecodeJumpOffset -{ - int offset; - WRarray references; - uint32_t gotoHash; - - BytecodeJumpOffset() : offset(0), gotoHash(0) {} -}; - -//------------------------------------------------------------------------------ -struct GotoSource -{ - uint32_t hash; - int offset; -}; - -//------------------------------------------------------------------------------ -struct WRBytecode -{ - WROpcodeStream all; - WROpcodeStream opcodes; - - bool isStructSpace; - - WRarray localSpace; - WRarray functionSpace; - WRarray unitObjectSpace; - - void invalidateOpcodeCache() { opcodes.clear(); } - - WRarray jumpOffsetTargets; - WRarray gotoSource; - - void clear() - { - all.clear(); - opcodes.clear(); - isStructSpace = false; - localSpace.clear(); - - functionSpace.clear(); - unitObjectSpace.clear(); - - jumpOffsetTargets.clear(); - gotoSource.clear(); - - isStructSpace = false; - } -}; - -//------------------------------------------------------------------------------ -struct WRExpressionContext -{ - WRExpressionType type; - - bool spaceBefore; - bool spaceAfter; - bool global; - WRstr prefix; - WRstr token; - WRValue value; - WRstr literalString; - const WROperation* operation; - - int stackPosition; - - WRBytecode bytecode; - - WRExpressionContext() { reset(); } - - void setLocalSpace( WRarray& localSpace, bool isStructSpace ) - { - bytecode.localSpace.clear(); - bytecode.isStructSpace = isStructSpace; - for( unsigned int l=0; l context; - - WRBytecode bytecode; - bool lValue; - - //------------------------------------------------------------------------------ - void pushToStack( int index ) - { - int highest = 0; - for( unsigned int i=0; i highest ? context[i].stackPosition : highest; - } - } - - context[index].stackPosition = 0; - - // now compact the stack - for( int h=0; h h ) - { - --context[j].stackPosition; - } - } - --highest; - --h; - } - } - } - - //------------------------------------------------------------------------------ - void popFrom( int index ) - { - context[index].stackPosition = -1; - - for( unsigned int i=0; i& localSpace, bool isStructSpace ) - { - reset(); - bytecode.isStructSpace = isStructSpace; - for( unsigned int l=0; l constantValues; - - int16_t offsetOfLocalHashMap; - - // the code that runs when it loads - // the locals it has - WRBytecode bytecode; - - int parentUnitIndex; - - WRUnitContext() { reset(); } - void reset() - { - parentUnitIndex = 0; - hash = 0; - arguments = 0; - offsetInBytecode = 0; - offsetOfLocalHashMap = 0; - } -}; - -//------------------------------------------------------------------------------ -struct WRCompilationContext -{ -public: - WRError compile( const char* data, - const int size, - unsigned char** out, - int* outLen, - char* erroMsg, - const unsigned int compilerOptionFlags ); - - void createListing( const char* bytecode, WRstr& listing ); - -private: - - bool isReserved( const char* token ); - bool isValidLabel( WRstr& token, bool& isGlobal, WRstr& prefix, bool& isLibConstant ); - - bool getToken( WRExpressionContext& ex, const char* expect =0 ); - - static bool CheckSkipLoad( WROpcode opcode, WRBytecode& bytecode, int a, int o ); - static bool CheckFastLoad( WROpcode opcode, WRBytecode& bytecode, int a, int o ); - static bool IsLiteralLoadOpcode( unsigned char opcode ); - static bool CheckCompareReplace( WROpcode LS, WROpcode GS, WROpcode ILS, WROpcode IGS, WRBytecode& bytecode, unsigned int a, unsigned int o ); - - friend class WRExpression; - static void pushOpcode( WRBytecode& bytecode, WROpcode opcode ); - static void pushData( WRBytecode& bytecode, const unsigned char* data, const int len ) { bytecode.all.append( data, len ); } - static void pushData( WRBytecode& bytecode, const char* data, const int len ) { bytecode.all.append( (unsigned char*)data, len ); } - - int getBytecodePosition( WRBytecode& bytecode ) { return bytecode.all.size(); } - - bool m_addDebugLineNumbers; - bool m_embedGlobalSymbols; - bool m_embedSourceCode; - - int m_lastLineNumber; - uint16_t m_lastCode; - void pushDebug( uint16_t code, WRBytecode& bytecode,int param =-1 ); - void getSourcePosition( int& onLine, int& onChar, WRstr* line =0 ); - - int addRelativeJumpTarget( WRBytecode& bytecode ); - void setRelativeJumpTarget( WRBytecode& bytecode, int relativeJumpTarget ); - void addRelativeJumpSourceEx( WRBytecode& bytecode, WROpcode opcode, int relativeJumpTarget, const unsigned char* data, const int dataSize ); - void addRelativeJumpSource( WRBytecode& bytecode, WROpcode opcode, int relativeJumpTarget ); - void resolveRelativeJumps( WRBytecode& bytecode ); - - void appendBytecode( WRBytecode& bytecode, WRBytecode& addMe ); - - void pushLiteral( WRBytecode& bytecode, WRExpressionContext& context ); - void pushLibConstant( WRBytecode& bytecode, WRExpressionContext& context ); - int addLocalSpaceLoad( WRBytecode& bytecode, WRstr& token, bool addOnly =false ); - int addGlobalSpaceLoad( WRBytecode& bytecode, WRstr& token, bool addOnly =false ); - void addFunctionToHashSpace( WRBytecode& result, WRstr& token ); - void loadExpressionContext( WRExpression& expression, int depth, int operation ); - void resolveExpression( WRExpression& expression ); - unsigned int resolveExpressionEx( WRExpression& expression, int o, int p ); - - bool operatorFound( WRstr& token, WRarray& context, int depth ); - bool parseCallFunction( WRExpression& expression, WRstr functionName, int depth, bool parseArguments ); - bool pushObjectTable( WRExpressionContext& context, WRarray& localSpace, uint32_t hash ); - char parseExpression( WRExpression& expression); - bool parseUnit( bool isStruct, int parentUnitIndex ); - bool parseWhile( bool& returnCalled, WROpcode opcodeToReturn ); - bool parseDoWhile( bool& returnCalled, WROpcode opcodeToReturn ); - bool parseForLoop( bool& returnCalled, WROpcode opcodeToReturn ); - bool lookupConstantValue( WRstr& prefix, WRValue* value =0 ); - bool parseEnum( int unitIndex ); - uint32_t getSingleValueHash( const char* end ); - bool parseSwitch( bool& returnCalled, WROpcode opcodeToReturn ); - bool parseIf( bool& returnCalled, WROpcode opcodeToReturn ); - bool parseStatement( int unitIndex, char end, bool& returnCalled, WROpcode opcodeToReturn ); - - void createLocalHashMap( WRUnitContext& unit, unsigned char** buf, int* size ); - void link( unsigned char** out, int* outLen, const unsigned int compilerOptionFlags ); - - const char* m_source; - int m_sourceLen; - int m_pos; - - bool getChar( char &c ) { c = m_source[m_pos++]; return m_pos < m_sourceLen; } - bool checkAsComment( char lead ); - bool readCurlyBlock( WRstr& block ); - struct TokenBlock - { - WRstr data; - TokenBlock* next; - }; - - WRstr m_loadedToken; - WRValue m_loadedValue; - bool m_loadedQuoted; - - WRError m_err; - bool m_EOF; - bool m_LastParsedLabel; - bool m_parsingFor; - bool m_quoted; - - int m_unitTop; - WRarray m_units; - - WRarray m_continueTargets; - WRarray m_breakTargets; - - int m_foreachHash; - -/* - -jumpOffsetTarget-> code - code - code -fill 1 jump to jumpOffset - code - code -fill 2 jump to jumpOffset - - jumpOffset is a list of fills - - bytecode has a list of jump offsets it added so it can increment them - when appended - -*/ - -}; - -//------------------------------------------------------------------------------ -enum ScopeContextType -{ - Unit, - Switch, -}; - -//------------------------------------------------------------------------------ -struct ScopeContext -{ - int type; -}; - -#endif // WRENCH_WITHOUT_COMPILER - -#endif -/******************************************************************************* -Copyright (c) 2023 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ -#ifndef _WRENCH_DEBUG_H -#define _WRENCH_DEBUG_H -/*------------------------------------------------------------------------------*/ - -//------------------------------------------------------------------------------ -enum WrenchDebugComm -{ - Run, - Load, - - RequestStatus, - RequestSymbolBlock, - RequestSourceBlock, - RequestSourceHash, - - ReplyStatus, - ReplySymbolBlock, - ReplySource, - ReplySourceHash, - - ReplyUnavailable, - Err, - Halted, - -}; - -//------------------------------------------------------------------------------ -struct WrenchPacketGenericPayload -{ - int32_t hash; - - void xlate() - { - hash = wr_x32(hash); - } -}; - -//------------------------------------------------------------------------------ -struct WrenchPacket -{ - int32_t payloadSize; - char type; - char* payload; - - WrenchPacket() { memset(this, 0, sizeof(*this)); } - ~WrenchPacket() { clear(); } - - WrenchPacket( WrenchPacket& other ) - { - *this = other; - other.payload = 0; - } - - WrenchPacket& operator=( WrenchPacket& other ) - { - if ( this != &other ) - { - *this = other; - other.payload = 0; - } - - return *this; - } - - - void clear() { free(payload); payload = 0; } - - void xlate() { payloadSize = wr_x32(payloadSize); } - - char* allocate( int size ) - { - if ( payload ) - { - free( payload ); - } - return (payload = (char*)malloc(size)); - } - - WrenchPacketGenericPayload* genericPayload() - { - return (WrenchPacketGenericPayload *)allocate( sizeof(WrenchPacketGenericPayload) ); - } - -private: - WrenchPacket( const WrenchPacket& other ); - WrenchPacket& operator=( const WrenchPacket& other ); -}; - -//------------------------------------------------------------------------------ -enum WrenchDebugInfoType -{ - TypeMask = 0xC000, - PayloadMask = 0x3FFF, - - FunctionCall = 0x0000, // size: 1 the next opcode is a function call, step into or over? - LineNumber = 0x4000, // 14-bit line number - Returned = 0x8000, // function has returned - Reserved2 = 0xC000, -}; - -//------------------------------------------------------------------------------ -struct WrenchSymbol -{ - char label[64]; -}; - -//------------------------------------------------------------------------------ -struct WrenchFunction -{ - char label[64]; - SimpleLL locals; -}; - -#endif - -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#ifndef _STD_IO_DEFS_H -#define _STD_IO_DEFS_H -/*------------------------------------------------------------------------------*/ - -struct WRValue; -struct WRContext; -struct WRState; - -void wr_read_file( WRValue* stackTop, const int argn, WRContext* c ); -void wr_write_file( WRValue* stackTop, const int argn, WRContext* c ); - -void wr_getline( WRValue* stackTop, const int argn, WRContext* c ); -void wr_clock( WRValue* stackTop, const int argn, WRContext* c ); -void wr_milliseconds(WRValue* stackTop, const int argn, WRContext* c ); - -void wr_ioOpen( WRValue* stackTop, const int argn, WRContext* c ); -void wr_ioClose( WRValue* stackTop, const int argn, WRContext* c ); -void wr_ioRead( WRValue* stackTop, const int argn, WRContext* c ); -void wr_ioWrite( WRValue* stackTop, const int argn, WRContext* c ); -void wr_ioSeek( WRValue* stackTop, const int argn, WRContext* c ); - -void wr_ioPushConstants( WRState* w ); - -#endif -/******************************************************************************* -Copyright (c) 2024 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" -#ifndef WRENCH_WITHOUT_COMPILER -#include - -#define WR_COMPILER_LITERAL_STRING 0x10 -#define KEYHOLE_OPTIMIZER - -//------------------------------------------------------------------------------ -const char* c_reserved[] = -{ - "enum", - "break", - "case", - "continue", - "default", - "do", - "else", - "false", - "float", - "for", - "if", - "int", - "return", - "switch", - "true", - "function", - "while", - "new", - "null", - "struct", - "goto", - "gc_pause", - "" -}; - -//------------------------------------------------------------------------------ -WRError WRCompilationContext::compile( const char* source, - const int size, - unsigned char** out, - int* outLen, - char* errorMsg, - const unsigned int compilerOptionFlags ) -{ - m_source = source; - m_sourceLen = size; - - *outLen = 0; - *out = 0; - - m_lastLineNumber = 0; - m_lastCode = 0xFFFF; - - m_pos = 0; - m_err = WR_ERR_None; - m_EOF = false; - m_parsingFor = false; - m_unitTop = 0; - m_foreachHash = 0; - - m_units.setCount(1); - - bool returnCalled = false; - - m_loadedValue.p2 = INIT_AS_REF; - - m_addDebugLineNumbers = compilerOptionFlags & WR_EMBED_DEBUG_CODE; - m_embedSourceCode = compilerOptionFlags & WR_EMBED_SOURCE_CODE; - m_embedGlobalSymbols = compilerOptionFlags != 0; // if any options are set, embed them - - do - { - WRExpressionContext ex; - WRstr& token = ex.token; - WRValue& value = ex.value; - if ( !getToken(ex) ) - { - break; - } - - m_loadedToken = token; - m_loadedValue = value; - m_loadedQuoted = m_quoted; - - parseStatement( 0, ';', returnCalled, O_GlobalStop ); - - } while ( !m_EOF && (m_err == WR_ERR_None) ); - - WRstr msg; - - if ( m_err != WR_ERR_None ) - { - int onChar; - int onLine; - WRstr line; - - getSourcePosition( onLine, onChar, &line ); - - msg.format( "line:%d\n", onLine ); - msg.appendFormat( "err:%d\n", m_err ); - msg.appendFormat( "%-5d %s\n", onLine, line.c_str() ); - - for( int i=0; i 2) && (token[1] == ':') ) - { - isGlobal = true; - } - else - { - return false; - } - } - - for( unsigned int i=0; c_reserved[i][0]; ++i ) - { - if ( token == c_reserved[i] ) - { - return false; - } - } - - bool foundColon = false; - for( unsigned int i=0; i= m_sourceLen ) - { - m_EOF = true; - return false; - } - - } while( isspace(m_source[m_pos++]) ); - - token = m_source[m_pos - 1]; - - unsigned int t=0; - for( ; c_operations[t].token && strncmp( c_operations[t].token, "@macroBegin", 11); ++t ); - - int offset = m_pos - 1; - - for( ; c_operations[t].token; ++t ) - { - int len = (int)strnlen( c_operations[t].token, 20 ); - if ( ((offset + len) < m_sourceLen) - && !strncmp(m_source + offset, c_operations[t].token, len) ) - { - if ( isalnum(m_source[offset+len]) ) - { - continue; - } - - m_pos += len - 1; - token = c_operations[t].token; - goto foundMacroToken; - } - } - - for( t = 0; t' ) - { - if ( (m_pos < m_sourceLen) && m_source[m_pos] == '=' ) - { - token += '='; - ++m_pos; - } - else if ( (m_pos < m_sourceLen) && m_source[m_pos] == '>' ) - { - token += '>'; - ++m_pos; - - if ( (m_pos < m_sourceLen) && m_source[m_pos] == '=' ) - { - token += '='; - ++m_pos; - } - } - } - else if ( token[0] == '&' ) - { - if ( (m_pos < m_sourceLen) && m_source[m_pos] == '&' ) - { - token += '&'; - ++m_pos; - } - else if ( (m_pos < m_sourceLen) && m_source[m_pos] == '=' ) - { - token += '='; - ++m_pos; - } - } - else if ( token[0] == '^' ) - { - if ( (m_pos < m_sourceLen) && m_source[m_pos] == '=' ) - { - token += '='; - ++m_pos; - } - } - else if ( token[0] == '|' ) - { - if ( (m_pos < m_sourceLen) && m_source[m_pos] == '|' ) - { - token += '|'; - ++m_pos; - } - else if ( (m_pos < m_sourceLen) && m_source[m_pos] == '=' ) - { - token += '='; - ++m_pos; - } - } - else if ( token[0] == '+' ) - { - if ( m_pos < m_sourceLen ) - { - if ( m_source[m_pos] == '+' ) - { - token += '+'; - ++m_pos; - } - else if ( m_source[m_pos] == '=' ) - { - token += '='; - ++m_pos; - } - } - } - else if ( token[0] == '\"' || token[0] == '\'' ) - { - bool single = token[0] == '\''; - token.clear(); - m_quoted = true; - - do - { - if (m_pos >= m_sourceLen) - { - m_err = WR_ERR_unterminated_string_literal; - m_EOF = true; - return false; - } - - char c = m_source[m_pos]; - if (c == '\"' && !single ) // terminating character - { - if ( single ) - { - m_err = WR_ERR_bad_expression; - return false; - } - ++m_pos; - break; - } - else if ( c == '\'' && single ) - { - if ( !single || (token.size() > 1) ) - { - m_err = WR_ERR_bad_expression; - return false; - } - ++m_pos; - break; - } - else if (c == '\n') - { - m_err = WR_ERR_newline_in_string_literal; - return false; - } - else if (c == '\\') - { - c = m_source[++m_pos]; - if (m_pos >= m_sourceLen) - { - m_err = WR_ERR_unterminated_string_literal; - m_EOF = true; - return false; - } - - if (c == '\\') // escaped slash - { - token += '\\'; - } - else if (c == '0') - { - token += atoi(m_source + m_pos); - for (; (m_pos < m_sourceLen) && isdigit(m_source[m_pos]); ++m_pos); - --m_pos; - } - else if (c == '\"') - { - token += '\"'; - } - else if (c == '\'') - { - token += '\''; - } - else if (c == 'n') - { - token += '\n'; - } - else if (c == 'r') - { - token += '\r'; - } - else if (c == 't') - { - token += '\t'; - } - else - { - m_err = WR_ERR_bad_string_escape_sequence; - return false; - } - } - else - { - token += c; - } - - } while( ++m_pos < m_sourceLen ); - - value.p2 = INIT_AS_INT; - if ( single ) - { - value.ui = token.size() == 1 ? token[0] : 0; - token.clear(); - } - else - { - value.p2 = INIT_AS_INT; - value.type = (WRValueType)WR_COMPILER_LITERAL_STRING; - value.p = &token; - } - } - else if ( token[0] == '/' ) // might be a comment - { - if ( m_pos < m_sourceLen ) - { - if ( !isspace(m_source[m_pos]) ) - { - if ( m_source[m_pos] == '/' ) - { - for( ; m_pos= m_sourceLen ) - { - return false; - } - -parseAsNumber: - - m_LastParsedLabel = true; - - if ( token[0] == '0' && m_source[m_pos] == 'x' ) // interpret as hex - { - token.clear(); - m_pos++; - - for(;;) - { - if ( m_pos >= m_sourceLen ) - { - m_err = WR_ERR_unexpected_EOF; - return false; - } - - if ( !isxdigit(m_source[m_pos]) ) - { - break; - } - - token += m_source[m_pos++]; - } - - value.p2 = INIT_AS_INT; - value.ui = strtoul( token, 0, 16 ); - } - else if (token[0] == '0' && m_source[m_pos] == 'b' ) - { - token.clear(); - m_pos++; - - for(;;) - { - if ( m_pos >= m_sourceLen ) - { - m_err = WR_ERR_unexpected_EOF; - return false; - } - - if ( !isxdigit(m_source[m_pos]) ) - { - break; - } - - token += m_source[m_pos++]; - } - - value.p2 = INIT_AS_INT; - value.i = strtol( token, 0, 2 ); - } - else if (token[0] == '0' && isdigit(m_source[m_pos]) ) // octal - { - token.clear(); - - for(;;) - { - if ( m_pos >= m_sourceLen ) - { - m_err = WR_ERR_unexpected_EOF; - return false; - } - - if ( !isdigit(m_source[m_pos]) ) - { - break; - } - - token += m_source[m_pos++]; - } - - value.p2 = INIT_AS_INT; - value.i = strtol( token, 0, 8 ); - } - else - { - bool decimal = token[0] == '.'; - for(;;) - { - if ( m_pos >= m_sourceLen ) - { - m_err = WR_ERR_unexpected_EOF; - return false; - } - - if ( m_source[m_pos] == '.' ) - { - if ( decimal ) - { - return false; - } - - decimal = true; - } - else if ( !isdigit(m_source[m_pos]) ) - { - if ( m_source[m_pos] == 'f' || m_source[m_pos] == 'F') - { - decimal = true; - m_pos++; - } - - break; - } - - token += m_source[m_pos++]; - } - - if ( decimal ) - { - value.p2 = INIT_AS_FLOAT; - value.f = (float)atof( token ); - } - else - { - value.p2 = INIT_AS_INT; - value.ui = (uint32_t)strtoul( token, 0, 10 ); - } - } - } - else if ( token[0] == ':' && isspace(m_source[m_pos]) ) - { - - } - else if ( isalpha(token[0]) || token[0] == '_' || token[0] == ':' ) // must be a label - { - if ( token[0] != ':' || m_source[m_pos] == ':' ) - { - m_LastParsedLabel = true; - if ( m_pos < m_sourceLen - && token[0] == ':' - && m_source[m_pos] == ':' ) - { - token += ':'; - ++m_pos; - } - - for (; m_pos < m_sourceLen; ++m_pos) - { - if ( m_source[m_pos] == ':' && m_source[m_pos + 1] == ':' && token.size() > 0 ) - { - token += "::"; - m_pos ++; - continue; - } - - if (!isalnum(m_source[m_pos]) && m_source[m_pos] != '_' ) - { - break; - } - - token += m_source[m_pos]; - } - - if (token == "true") - { - value.p2 = INIT_AS_INT; - value.i = 1; - token = "1"; - } - else if (token == "false" || token == "null" ) - { - value.p2 = INIT_AS_INT; - value.i = 0; - token = "0"; - } - } - } - } - -foundMacroToken: - - ex.spaceAfter = (m_pos < m_sourceLen) && isspace(m_source[m_pos]); - } - - m_loadedToken.clear(); - m_loadedQuoted = m_quoted; - m_loadedValue.p2 = INIT_AS_REF; - - if ( expect && (token != expect) ) - { - return false; - } - - return true; -} - -//------------------------------------------------------------------------------ -bool WRCompilationContext::CheckSkipLoad( WROpcode opcode, WRBytecode& bytecode, int a, int o ) -{ - if ( bytecode.opcodes[o] == O_LoadFromLocal - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[a-3] = O_LLValues; - bytecode.all[a-1] = bytecode.all[a]; - bytecode.all[a] = opcode; - bytecode.opcodes.shave(2); - bytecode.opcodes += opcode; - return true; - } - else if ( bytecode.opcodes[o] == O_LoadFromGlobal - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[a-3] = O_LGValues; - bytecode.all[a-1] = bytecode.all[a]; - bytecode.all[a] = opcode; - bytecode.opcodes.shave(2); - bytecode.opcodes += opcode; - return true; - } - else if ( bytecode.opcodes[o] == O_LoadFromLocal - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[a-3] = O_GLValues; - bytecode.all[a-1] = bytecode.all[a]; - bytecode.all[a] = opcode; - bytecode.opcodes.shave(2); - bytecode.opcodes += opcode; - return true; - } - else if ( bytecode.opcodes[o] == O_LoadFromGlobal - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[a-3] = O_GGValues; - bytecode.all[a-1] = bytecode.all[a]; - bytecode.all[a] = opcode; - bytecode.opcodes.shave(2); - bytecode.opcodes += opcode; - return true; - } - - return false; -} - -//------------------------------------------------------------------------------ -bool WRCompilationContext::CheckFastLoad( WROpcode opcode, WRBytecode& bytecode, int a, int o ) -{ - if ( a <= 2 || o == 0 ) - { - return false; - } - - if ( (opcode == O_Index && CheckSkipLoad(O_IndexSkipLoad, bytecode, a, o)) - || (opcode == O_BinaryMod && CheckSkipLoad(O_BinaryModSkipLoad, bytecode, a, o)) - || (opcode == O_BinaryRightShift && CheckSkipLoad(O_BinaryRightShiftSkipLoad, bytecode, a, o)) - || (opcode == O_BinaryLeftShift && CheckSkipLoad(O_BinaryLeftShiftSkipLoad, bytecode, a, o)) - || (opcode == O_BinaryAnd && CheckSkipLoad(O_BinaryAndSkipLoad, bytecode, a, o)) - || (opcode == O_BinaryOr && CheckSkipLoad(O_BinaryOrSkipLoad, bytecode, a, o)) - || (opcode == O_BinaryXOR && CheckSkipLoad(O_BinaryXORSkipLoad, bytecode, a, o)) ) - { - return true; - } - - return false; -} - -//------------------------------------------------------------------------------ -bool WRCompilationContext::IsLiteralLoadOpcode( unsigned char opcode ) -{ - return opcode == O_LiteralInt32 - || opcode == O_LiteralZero - || opcode == O_LiteralFloat - || opcode == O_LiteralInt8 - || opcode == O_LiteralInt16; -} - -//------------------------------------------------------------------------------ -bool WRCompilationContext::CheckCompareReplace( WROpcode LS, WROpcode GS, WROpcode ILS, WROpcode IGS, WRBytecode& bytecode, unsigned int a, unsigned int o ) -{ - if ( IsLiteralLoadOpcode(bytecode.opcodes[o-1]) ) - { - if ( bytecode.opcodes[o] == O_LoadFromGlobal ) - { - bytecode.all[ a - 1 ] = GS; - bytecode.opcodes[o] = GS; - return true; - } - else if ( bytecode.opcodes[o] == O_LoadFromLocal ) - { - bytecode.all[ a - 1 ] = LS; - bytecode.opcodes[o] = LS; - return true; - } - } - else if ( IsLiteralLoadOpcode(bytecode.opcodes[o]) ) - { - if ( (bytecode.opcodes[o] == O_LiteralInt32 || bytecode.opcodes[o] == O_LiteralFloat) - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 6 ] = bytecode.all[ a ]; // store i3 - bytecode.all[ a ] = bytecode.all[ a - 5 ]; // move index - - bytecode.all[ a - 5 ] = bytecode.all[ a - 3 ]; - bytecode.all[ a - 4 ] = bytecode.all[ a - 2 ]; - bytecode.all[ a - 3 ] = bytecode.all[ a - 1 ]; - bytecode.all[ a - 2 ] = bytecode.all[ a - 6 ]; - bytecode.all[ a - 6 ] = bytecode.opcodes[o]; - bytecode.all[ a - 1 ] = IGS; - - bytecode.opcodes[o] = IGS; // reverse the logic - - return true; - } - else if ( bytecode.opcodes[o] == O_LiteralZero - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 2 ] = O_LiteralZero; - bytecode.all[ a ] = bytecode.all[ a - 1]; - bytecode.all[ a - 1 ] = IGS; - bytecode.opcodes[o] = IGS; // reverse the logic - return true; - } - else if ( bytecode.opcodes[o] == O_LiteralZero - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 2 ] = O_LiteralZero; - bytecode.all[ a ] = bytecode.all[ a - 1]; - bytecode.all[ a - 1 ] = IGS; - bytecode.opcodes[o] = IGS; // reverse the logic - return true; - } - else if ( bytecode.opcodes[o] == O_LiteralInt8 - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 1 ] = bytecode.all[ a - 2 ]; - bytecode.all[ a - 2 ] = bytecode.all[ a ]; - bytecode.all[ a ] = bytecode.all[ a - 1 ]; - bytecode.all[ a - 1 ] = IGS; - bytecode.all[ a - 3 ] = O_LiteralInt8; - - bytecode.opcodes[o] = IGS; // reverse the logic - return true; - } - else if ( bytecode.opcodes[o] == O_LiteralInt16 - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 2 ] = bytecode.all[ a ]; - - bytecode.all[ a - 4 ] = bytecode.all[ a - 3 ]; - bytecode.all[ a - 3 ] = bytecode.all[ a - 1 ]; - bytecode.all[ a ] = bytecode.all[ a - 4 ]; - bytecode.all[ a - 1 ] = IGS; - bytecode.all[ a - 4 ] = O_LiteralInt16; - - bytecode.opcodes[o] = IGS; // reverse the logic - return true; - } - if ( (bytecode.opcodes[o] == O_LiteralInt32 || bytecode.opcodes[o] == O_LiteralFloat) - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 6 ] = bytecode.all[ a ]; // store i3 - bytecode.all[ a ] = bytecode.all[ a - 5 ]; // move index - - bytecode.all[ a - 5 ] = bytecode.all[ a - 3 ]; - bytecode.all[ a - 4 ] = bytecode.all[ a - 2 ]; - bytecode.all[ a - 3 ] = bytecode.all[ a - 1 ]; - bytecode.all[ a - 2 ] = bytecode.all[ a - 6 ]; - bytecode.all[ a - 6 ] = bytecode.opcodes[o]; - bytecode.all[ a - 1 ] = ILS; - - bytecode.opcodes[o] = ILS; // reverse the logic - - return true; - } - else if ( bytecode.opcodes[o] == O_LiteralZero - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 2 ] = O_LiteralZero; - bytecode.all[ a ] = bytecode.all[ a - 1]; - bytecode.all[ a - 1 ] = ILS; - bytecode.opcodes[o] = ILS; // reverse the logic - return true; - } - else if ( bytecode.opcodes[o] == O_LiteralZero - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 2 ] = O_LiteralZero; - bytecode.all[ a ] = bytecode.all[ a - 1]; - bytecode.all[ a - 1 ] = ILS; - bytecode.opcodes[o] = ILS; // reverse the logic - return true; - } - else if ( bytecode.opcodes[o] == O_LiteralInt8 - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 1 ] = bytecode.all[ a - 2 ]; - bytecode.all[ a - 2 ] = bytecode.all[ a ]; - bytecode.all[ a ] = bytecode.all[ a - 1 ]; - bytecode.all[ a - 1 ] = ILS; - bytecode.all[ a - 3 ] = O_LiteralInt8; - - bytecode.opcodes[o] = ILS; // reverse the logic - return true; - } - else if ( bytecode.opcodes[o] == O_LiteralInt16 - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 2 ] = bytecode.all[ a ]; - - bytecode.all[ a - 4 ] = bytecode.all[ a - 3 ]; - bytecode.all[ a - 3 ] = bytecode.all[ a - 1 ]; - bytecode.all[ a ] = bytecode.all[ a - 4 ]; - bytecode.all[ a - 1 ] = ILS; - bytecode.all[ a - 4 ] = O_LiteralInt16; - - bytecode.opcodes[o] = ILS; // reverse the logic - return true; - } - } - - return false; -} - -//------------------------------------------------------------------------------ -void WRCompilationContext::pushOpcode( WRBytecode& bytecode, WROpcode opcode ) -{ -#ifdef KEYHOLE_OPTIMIZER - unsigned int o = bytecode.opcodes.size(); - if ( o ) - { - // keyhole optimizations - - --o; - unsigned int a = bytecode.all.size() - 1; - - if ( opcode == O_Return - && bytecode.opcodes[o] == O_LiteralZero ) - { - bytecode.all[a] = O_ReturnZero; - bytecode.opcodes[o] = O_ReturnZero; - return; - } - else if ( opcode == O_CompareEQ && o>0 && bytecode.opcodes[o] == O_LoadFromLocal && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_LLCompareEQ; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave( 1 ); - bytecode.opcodes.clear(); - bytecode.opcodes += O_LLCompareEQ; - return; - } - else if ( opcode == O_CompareNE && o>0 && bytecode.opcodes[o] == O_LoadFromLocal && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_LLCompareNE; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave( 1 ); - bytecode.opcodes.clear(); - bytecode.opcodes += O_LLCompareNE; - return; - } - else if ( opcode == O_CompareGT && o>0 && bytecode.opcodes[o] == O_LoadFromLocal && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_LLCompareGT; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave( 1 ); - bytecode.opcodes.clear(); - bytecode.opcodes += O_LLCompareGT; - return; - } - else if ( opcode == O_CompareLT && o>0 && bytecode.opcodes[o] == O_LoadFromLocal && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_LLCompareLT; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave( 1 ); - bytecode.opcodes.clear(); - bytecode.opcodes += O_LLCompareLT; - return; - } - else if ( opcode == O_CompareGE && o>0 && bytecode.opcodes[o] == O_LoadFromLocal && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_LLCompareGE; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave( 1 ); - bytecode.opcodes.clear(); - bytecode.opcodes += O_LLCompareGE; - return; - } - else if ( opcode == O_CompareLE && o>0 && bytecode.opcodes[o] == O_LoadFromLocal && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_LLCompareLE; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave( 1 ); - bytecode.opcodes.clear(); - bytecode.opcodes += O_LLCompareLE; - return; - } - else if ( opcode == O_CompareEQ && o>0 && bytecode.opcodes[o] == O_LoadFromGlobal && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_GGCompareEQ; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave( 1 ); - bytecode.opcodes.clear(); - bytecode.opcodes += O_GGCompareEQ; - return; - } - else if ( opcode == O_CompareNE && o>0 && bytecode.opcodes[o] == O_LoadFromGlobal && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_GGCompareNE; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave( 1 ); - bytecode.opcodes.clear(); - bytecode.opcodes += O_GGCompareNE; - return; - } - else if ( opcode == O_CompareGT && o>0 && bytecode.opcodes[o] == O_LoadFromGlobal && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_GGCompareGT; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave( 1 ); - bytecode.opcodes.clear(); - bytecode.opcodes += O_GGCompareGT; - return; - } - else if ( opcode == O_CompareLT && o>0 && bytecode.opcodes[o] == O_LoadFromGlobal && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_GGCompareLT; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave( 1 ); - bytecode.opcodes.clear(); - bytecode.opcodes += O_GGCompareLT; - return; - } - else if ( opcode == O_CompareGE && o>0 && bytecode.opcodes[o] == O_LoadFromGlobal && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_GGCompareGE; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave( 1 ); - bytecode.opcodes.clear(); - bytecode.opcodes += O_GGCompareGE; - return; - } - else if ( opcode == O_CompareLE && o>0 && bytecode.opcodes[o] == O_LoadFromGlobal && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_GGCompareLE; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave( 1 ); - bytecode.opcodes.clear(); - bytecode.opcodes += O_GGCompareLE; - return; - } - else if ( (opcode == O_CompareEQ && (o>0)) - && CheckCompareReplace(O_LSCompareEQ, O_GSCompareEQ, O_LSCompareEQ, O_GSCompareEQ, bytecode, a, o) ) - { - return; - } - else if ( (opcode == O_CompareNE && (o>0)) - && CheckCompareReplace(O_LSCompareNE, O_GSCompareNE, O_LSCompareNE, O_GSCompareNE, bytecode, a, o) ) - { - return; - } - else if ( (opcode == O_CompareGE && (o>0)) - && CheckCompareReplace(O_LSCompareGE, O_GSCompareGE, O_LSCompareLE, O_GSCompareLE, bytecode, a, o) ) - { - return; - } - else if ( (opcode == O_CompareLE && (o>0)) - && CheckCompareReplace(O_LSCompareLE, O_GSCompareLE, O_LSCompareGE, O_GSCompareGE, bytecode, a, o) ) - { - return; - } - else if ( (opcode == O_CompareGT && (o>0)) - && CheckCompareReplace(O_LSCompareGT, O_GSCompareGT, O_LSCompareLT, O_GSCompareLT, bytecode, a, o) ) - { - return; - } - else if ( (opcode == O_CompareLT && (o>0)) - && CheckCompareReplace(O_LSCompareLT, O_GSCompareLT, O_LSCompareGT, O_GSCompareGT, bytecode, a, o) ) - { - return; - } - else if ( CheckFastLoad(opcode, bytecode, a, o) ) - { - return; - } - else if ( opcode == O_BinaryMultiplication && (a>2) ) - { - if ( o>1 ) - { - if ( bytecode.opcodes[o] == O_LoadFromGlobal - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - // LoadFromGlobal a - 3 - // [index] a - 2 - // LoadFromGlobal a - 1 - // [index] a - - bytecode.all[ a - 3 ] = O_GGBinaryMultiplication; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - return; - } - else if ( bytecode.opcodes[o] == O_LoadFromLocal - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_GLBinaryMultiplication; - bytecode.all[ a - 1 ] = bytecode.all[ a - 2 ]; - bytecode.all[ a - 2 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - return; - } - else if ( bytecode.opcodes[o] == O_LoadFromGlobal - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_GLBinaryMultiplication; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - return; - } - else if ( bytecode.opcodes[o] == O_LoadFromLocal - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_LLBinaryMultiplication; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - return; - } - } - - bytecode.all += opcode; - bytecode.opcodes += opcode; - return; - } - else if ( opcode == O_BinaryAddition && (a>2) ) - { - if ( bytecode.opcodes[o] == O_LoadFromGlobal - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_GGBinaryAddition; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( bytecode.opcodes[o] == O_LoadFromLocal - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_GLBinaryAddition; - bytecode.all[ a - 1 ] = bytecode.all[ a - 2 ]; - bytecode.all[ a - 2 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( bytecode.opcodes[o] == O_LoadFromGlobal - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_GLBinaryAddition; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( bytecode.opcodes[o] == O_LoadFromLocal - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_LLBinaryAddition; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else - { - bytecode.all += opcode; - bytecode.opcodes += opcode; - } - - return; - } - else if ( opcode == O_BinarySubtraction && (a>2) ) - { - if ( bytecode.opcodes[o] == O_LoadFromGlobal - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_GGBinarySubtraction; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( bytecode.opcodes[o] == O_LoadFromLocal - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_LGBinarySubtraction; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( bytecode.opcodes[o] == O_LoadFromGlobal - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_GLBinarySubtraction; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( bytecode.opcodes[o] == O_LoadFromLocal - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_LLBinarySubtraction; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else - { - bytecode.all += opcode; - bytecode.opcodes += opcode; - } - - return; - } - else if ( opcode == O_BinaryDivision && (a>2) ) - { - if ( bytecode.opcodes[o] == O_LoadFromGlobal - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_GGBinaryDivision; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( bytecode.opcodes[o] == O_LoadFromLocal - && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 3 ] = O_LGBinaryDivision; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( bytecode.opcodes[o] == O_LoadFromGlobal - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_GLBinaryDivision; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( bytecode.opcodes[o] == O_LoadFromLocal - && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 3 ] = O_LLBinaryDivision; - bytecode.all[ a - 1 ] = bytecode.all[ a ]; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else - { - bytecode.all += opcode; - bytecode.opcodes += opcode; - } - - return; - } - else if ( opcode == O_Index ) - { - if ( bytecode.opcodes[o] == O_LiteralInt16 ) - { - bytecode.all[a-2] = O_IndexLiteral16; - bytecode.opcodes[o] = O_IndexLiteral16; - return; - } - else if ( bytecode.opcodes[o] == O_LiteralInt8 ) - { - bytecode.all[a-1] = O_IndexLiteral8; - bytecode.opcodes[o] = O_IndexLiteral8; - return; - } - } - else if ( opcode == O_BZ ) - { - if ( bytecode.opcodes[o] == O_LLCompareLT ) - { - bytecode.opcodes[o] = O_LLCompareLTBZ; - bytecode.all[ a - 2 ] = O_LLCompareLTBZ; - return; - } - else if ( bytecode.opcodes[o] == O_LLCompareGT ) - { - bytecode.opcodes[o] = O_LLCompareGTBZ; - bytecode.all[ a - 2 ] = O_LLCompareGTBZ; - return; - } - else if ( bytecode.opcodes[o] == O_LLCompareGE ) - { - bytecode.opcodes[o] = O_LLCompareGEBZ; - bytecode.all[ a - 2 ] = O_LLCompareGEBZ; - return; - } - else if ( bytecode.opcodes[o] == O_LLCompareLE ) - { - bytecode.opcodes[o] = O_LLCompareLEBZ; - bytecode.all[ a - 2 ] = O_LLCompareLEBZ; - return; - } - else if ( bytecode.opcodes[o] == O_LLCompareEQ ) - { - bytecode.opcodes[o] = O_LLCompareEQBZ; - bytecode.all[ a - 2 ] = O_LLCompareEQBZ; - return; - } - else if ( bytecode.opcodes[o] == O_LLCompareNE ) - { - bytecode.opcodes[o] = O_LLCompareNEBZ; - bytecode.all[ a - 2 ] = O_LLCompareNEBZ; - return; - } - else if ( bytecode.opcodes[o] == O_GGCompareLT ) - { - bytecode.opcodes[o] = O_GGCompareLTBZ; - bytecode.all[ a - 2 ] = O_GGCompareLTBZ; - return; - } - else if ( bytecode.opcodes[o] == O_GGCompareGT ) - { - bytecode.opcodes[o] = O_GGCompareGTBZ; - bytecode.all[ a - 2 ] = O_GGCompareGTBZ; - return; - } - else if ( bytecode.opcodes[o] == O_GGCompareLE ) - { - bytecode.opcodes[o] = O_GGCompareLEBZ; - bytecode.all[ a - 2 ] = O_GGCompareLEBZ; - return; - } - else if ( bytecode.opcodes[o] == O_GGCompareGE ) - { - bytecode.opcodes[o] = O_GGCompareGEBZ; - bytecode.all[ a - 2 ] = O_GGCompareGEBZ; - return; - } - else if ( bytecode.opcodes[o] == O_GGCompareEQ ) - { - bytecode.opcodes[o] = O_GGCompareEQBZ; - bytecode.all[ a - 2 ] = O_GGCompareEQBZ; - return; - } - else if ( bytecode.opcodes[o] == O_GGCompareNE ) - { - bytecode.opcodes[o] = O_GGCompareNEBZ; - bytecode.all[ a - 2 ] = O_GGCompareNEBZ; - return; - } - else if ( bytecode.opcodes[o] == O_GSCompareEQ ) - { - bytecode.opcodes[o] = O_GSCompareEQBZ; - bytecode.all[ a - 1 ] = O_GSCompareEQBZ; - return; - } - else if ( bytecode.opcodes[o] == O_LSCompareEQ ) - { - bytecode.opcodes[o] = O_LSCompareEQBZ; - bytecode.all[ a - 1 ] = O_LSCompareEQBZ; - return; - } - else if ( bytecode.opcodes[o] == O_GSCompareNE ) - { - bytecode.opcodes[o] = O_GSCompareNEBZ; - bytecode.all[ a - 1 ] = O_GSCompareNEBZ; - return; - } - else if ( bytecode.opcodes[o] == O_LSCompareNE ) - { - bytecode.opcodes[o] = O_LSCompareNEBZ; - bytecode.all[ a - 1 ] = O_LSCompareNEBZ; - return; - } - else if ( bytecode.opcodes[o] == O_GSCompareGE ) - { - bytecode.opcodes[o] = O_GSCompareGEBZ; - bytecode.all[ a - 1 ] = O_GSCompareGEBZ; - return; - } - else if ( bytecode.opcodes[o] == O_LSCompareGE ) - { - bytecode.opcodes[o] = O_LSCompareGEBZ; - bytecode.all[ a - 1 ] = O_LSCompareGEBZ; - return; - } - else if ( bytecode.opcodes[o] == O_GSCompareLE ) - { - bytecode.opcodes[o] = O_GSCompareLEBZ; - bytecode.all[ a - 1 ] = O_GSCompareLEBZ; - return; - } - else if ( bytecode.opcodes[o] == O_LSCompareLE ) - { - bytecode.opcodes[o] = O_LSCompareLEBZ; - bytecode.all[ a - 1 ] = O_LSCompareLEBZ; - return; - } - else if ( bytecode.opcodes[o] == O_GSCompareGT ) - { - bytecode.opcodes[o] = O_GSCompareGTBZ; - bytecode.all[ a - 1 ] = O_GSCompareGTBZ; - return; - } - else if ( bytecode.opcodes[o] == O_LSCompareGT ) - { - bytecode.opcodes[o] = O_LSCompareGTBZ; - bytecode.all[ a - 1 ] = O_LSCompareGTBZ; - return; - } - else if ( bytecode.opcodes[o] == O_GSCompareLT ) - { - bytecode.opcodes[o] = O_GSCompareLTBZ; - bytecode.all[ a - 1 ] = O_GSCompareLTBZ; - return; - } - else if ( bytecode.opcodes[o] == O_LSCompareLT ) - { - bytecode.opcodes[o] = O_LSCompareLTBZ; - bytecode.all[ a - 1 ] = O_LSCompareLTBZ; - return; - } - else if ( bytecode.opcodes[o] == O_CompareEQ ) // assign+pop is very common - { - if ( (o>1) && (bytecode.opcodes[o-1] == O_LoadFromLocal) && (bytecode.opcodes[o-2] == O_LoadFromLocal) ) - { - bytecode.all[a-4] = O_LLCompareEQBZ; - bytecode.all[a-2] = bytecode.all[a-1]; - bytecode.opcodes.clear(); - bytecode.opcodes[o-2] = O_LLCompareEQBZ; - bytecode.all.shave(2); - } - else if ( (o>1) && (bytecode.opcodes[o-1] == O_LoadFromGlobal) && (bytecode.opcodes[o-2] == O_LoadFromGlobal) ) - { - bytecode.all[a-4] = O_GGCompareEQBZ; - bytecode.all[a-2] = bytecode.all[a-1]; - bytecode.opcodes.clear(); - bytecode.opcodes[o-2] = O_GGCompareEQBZ; - bytecode.all.shave(2); - } - else - { - bytecode.all[a] = O_CompareBEQ; - bytecode.opcodes[o] = O_CompareBEQ; - } - return; - } - else if ( bytecode.opcodes[o] == O_CompareLT ) - { - if ( (o>1) && (bytecode.opcodes[o-1] == O_LoadFromLocal) && (bytecode.opcodes[o-2] == O_LoadFromLocal) ) - { - bytecode.all[a-4] = O_LLCompareLTBZ; - bytecode.all[a-2] = bytecode.all[a-1]; - bytecode.opcodes.clear(); - bytecode.opcodes[o-2] = O_LLCompareLTBZ; - bytecode.all.shave(2); - } - else if ( (o>1) && (bytecode.opcodes[o-1] == O_LoadFromGlobal) && (bytecode.opcodes[o-2] == O_LoadFromGlobal) ) - { - bytecode.all[a-4] = O_GGCompareLTBZ; - bytecode.all[a-2] = bytecode.all[a-1]; - bytecode.opcodes.clear(); - bytecode.opcodes[o-2] = O_GGCompareLTBZ; - bytecode.all.shave(2); - } - else - { - bytecode.all[a] = O_CompareBLT; - bytecode.opcodes[o] = O_CompareBLT; - } - return; - } - else if ( bytecode.opcodes[o] == O_CompareGT ) - { - if ( (o>1) && (bytecode.opcodes[o-1] == O_LoadFromLocal) && (bytecode.opcodes[o-2] == O_LoadFromLocal) ) - { - bytecode.all[a-4] = O_LLCompareGTBZ; - bytecode.all[a-2] = bytecode.all[a-1]; - bytecode.opcodes.clear(); - bytecode.opcodes[o-2] = O_LLCompareGTBZ; - bytecode.all.shave(2); - } - else if ( (o>1) && (bytecode.opcodes[o-1] == O_LoadFromGlobal) && (bytecode.opcodes[o-2] == O_LoadFromGlobal) ) - { - bytecode.all[a-4] = O_GGCompareGTBZ; - bytecode.all[a-2] = bytecode.all[a-1]; - bytecode.opcodes.clear(); - bytecode.opcodes[o-2] = O_GGCompareGTBZ; - bytecode.all.shave(2); - } - else - { - bytecode.all[a] = O_CompareBGT; - bytecode.opcodes[o] = O_CompareBGT; - } - return; - } - else if ( bytecode.opcodes[o] == O_CompareGE ) - { - if ( (o>1) && (bytecode.opcodes[o-1] == O_LoadFromLocal) && (bytecode.opcodes[o-2] == O_LoadFromLocal) ) - { - bytecode.all[a-4] = O_LLCompareGEBZ; - bytecode.all[a-2] = bytecode.all[a-3]; - bytecode.all[a-3] = bytecode.all[a-1]; - bytecode.opcodes.clear(); - bytecode.opcodes[o-2] = O_LLCompareLTBZ; - bytecode.all.shave(2); - } - else if ( (o>1) && (bytecode.opcodes[o-1] == O_LoadFromGlobal) && (bytecode.opcodes[o-2] == O_LoadFromGlobal) ) - { - bytecode.all[a-4] = O_GGCompareGEBZ; - bytecode.all[a-2] = bytecode.all[a-3]; - bytecode.all[a-3] = bytecode.all[a-1]; - bytecode.opcodes.clear(); - bytecode.opcodes[o-2] = O_GGCompareLTBZ; - bytecode.all.shave(2); - } - else - { - bytecode.all[a] = O_CompareBGE; - bytecode.opcodes[o] = O_CompareBGE; - } - return; - } - else if ( bytecode.opcodes[o] == O_CompareLE ) - { - if ( (o>1) && (bytecode.opcodes[o-1] == O_LoadFromLocal) && (bytecode.opcodes[o-2] == O_LoadFromLocal) ) - { - bytecode.all[a-4] = O_LLCompareLEBZ; - bytecode.all[a-2] = bytecode.all[a-3]; - bytecode.all[a-3] = bytecode.all[a-1]; - bytecode.opcodes.clear(); - bytecode.opcodes[o-2] = O_LLCompareGTBZ; - bytecode.all.shave(2); - } - else if ( (o>1) && (bytecode.opcodes[o-1] == O_LoadFromGlobal) && (bytecode.opcodes[o-2] == O_LoadFromGlobal) ) - { - bytecode.all[a-4] = O_GGCompareLEBZ; - bytecode.all[a-2] = bytecode.all[a-3]; - bytecode.all[a-3] = bytecode.all[a-1]; - bytecode.opcodes.clear(); - bytecode.opcodes[o-2] = O_GGCompareGTBZ; - bytecode.all.shave(2); - } - else - { - bytecode.all[a] = O_CompareBLE; - bytecode.opcodes[o] = O_CompareBLE; - } - - return; - } - else if ( bytecode.opcodes[o] == O_CompareNE ) // assign+pop is very common - { - if ( (o>1) && (bytecode.opcodes[o-1] == O_LoadFromLocal) && (bytecode.opcodes[o-2] == O_LoadFromLocal) ) - { - bytecode.all[a-4] = O_LLCompareNEBZ; - bytecode.all[a-2] = bytecode.all[a-1]; - bytecode.opcodes.clear(); - bytecode.opcodes[o-2] = O_LLCompareNEBZ; - bytecode.all.shave(2); - } - else if ( (o>1) && (bytecode.opcodes[o-1] == O_LoadFromGlobal) && (bytecode.opcodes[o-2] == O_LoadFromGlobal) ) - { - bytecode.all[a-4] = O_GGCompareNEBZ; - bytecode.all[a-2] = bytecode.all[a-1]; - bytecode.opcodes.clear(); - bytecode.opcodes[o-2] = O_GGCompareNEBZ; - bytecode.all.shave(2); - } - else - { - bytecode.all[a] = O_CompareBNE; - bytecode.opcodes[o] = O_CompareBNE; - } - return; - } - else if ( bytecode.opcodes[o] == O_LogicalOr ) // assign+pop is very common - { - bytecode.all[a] = O_BLO; - bytecode.opcodes[o] = O_BLO; - return; - } - else if ( bytecode.opcodes[o] == O_LogicalAnd ) // assign+pop is very common - { - bytecode.all[a] = O_BLA; - bytecode.opcodes[o] = O_BLA; - return; - } - } - else if ( opcode == O_PopOne ) - { - if ( bytecode.opcodes[o] == O_LoadFromLocal || bytecode.opcodes[o] == O_LoadFromGlobal ) - { - bytecode.all.shave(2); - bytecode.opcodes.clear(); - return; - } - else if ( bytecode.opcodes[o] == O_CallLibFunction && (a > 4) ) - { - bytecode.all[a-5] = O_CallLibFunctionAndPop; - bytecode.opcodes[o] = O_CallLibFunctionAndPop; - return; - } - else if ( bytecode.opcodes[o] == O_FUNCTION_CALL_PLACEHOLDER ) - { - bytecode.all[ a ] = O_PopOne; - return; - } - else if ( bytecode.opcodes[o] == O_PreIncrement || bytecode.opcodes[o] == O_PostIncrement ) - { - if ( (o > 0) && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 2 ] = O_IncGlobal; - - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( (o > 0) && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 2 ] = O_IncLocal; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else - { - bytecode.all[a] = O_PreIncrementAndPop; - bytecode.opcodes[o] = O_PreIncrementAndPop; - } - - return; - } - else if ( bytecode.opcodes[o] == O_PreDecrement || bytecode.opcodes[o] == O_PostDecrement ) - { - if ( (o > 0) && bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - bytecode.all[ a - 2 ] = O_DecGlobal; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( (o > 0) && bytecode.opcodes[o-1] == O_LoadFromLocal ) - { - bytecode.all[ a - 2 ] = O_DecLocal; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else - { - bytecode.all[a] = O_PreDecrementAndPop; - bytecode.opcodes[o] = O_PreDecrementAndPop; - } - - return; - } - else if ( bytecode.opcodes[o] == O_Assign ) // assign+pop is very common - { - if ( o > 0 ) - { - if ( bytecode.opcodes[o-1] == O_LoadFromGlobal ) - { - if ( o > 1 && bytecode.opcodes[o-2] == O_LiteralInt8 ) - { - // a - 4: O_literalInt8 - // a - 3: val - // a - 2: load from global - // a - 1: index - // a -- assign - - bytecode.all[ a - 4 ] = O_LiteralInt8ToGlobal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 3 ]; - bytecode.all[ a - 3 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( o > 1 && bytecode.opcodes[o-2] == O_LiteralInt16 ) - { - bytecode.all[ a - 5 ] = O_LiteralInt16ToGlobal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 3 ]; - bytecode.all[ a - 3 ] = bytecode.all[ a - 4 ]; - bytecode.all[ a - 4 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( o > 1 && bytecode.opcodes[o-2] == O_LiteralInt32 ) - { - bytecode.all[ a - 7 ] = O_LiteralInt32ToGlobal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 3 ]; - bytecode.all[ a - 3 ] = bytecode.all[ a - 4 ]; - bytecode.all[ a - 4 ] = bytecode.all[ a - 5 ]; - bytecode.all[ a - 5 ] = bytecode.all[ a - 6 ]; - bytecode.all[ a - 6 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( o > 1 && bytecode.opcodes[o-2] == O_LiteralFloat ) - { - bytecode.all[ a - 7 ] = O_LiteralFloatToGlobal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 3 ]; - bytecode.all[ a - 3 ] = bytecode.all[ a - 4 ]; - bytecode.all[ a - 4 ] = bytecode.all[ a - 5 ]; - bytecode.all[ a - 5 ] = bytecode.all[ a - 6 ]; - bytecode.all[ a - 6 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( (o > 1) && bytecode.opcodes[o-2] == O_LiteralZero ) - { - bytecode.all[ a - 3 ] = O_LiteralInt8ToGlobal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 1 ]; - bytecode.all[ a - 1 ] = 0; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( (o > 1) && bytecode.opcodes[o - 2] == O_BinaryDivision ) - { - bytecode.all[ a - 3 ] = O_BinaryDivisionAndStoreGlobal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( (o > 1) && bytecode.opcodes[o - 2] == O_BinaryAddition ) - { - bytecode.all[ a - 3 ] = O_BinaryAdditionAndStoreGlobal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( (o > 1) && bytecode.opcodes[o - 2] == O_BinaryMultiplication ) - { - bytecode.all[ a - 3 ] = O_BinaryMultiplicationAndStoreGlobal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( (o > 1) && bytecode.opcodes[o - 2] == O_BinarySubtraction ) - { - bytecode.all[ a - 3 ] = O_BinarySubtractionAndStoreGlobal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( (o > 1) && bytecode.opcodes[o - 2] == O_FUNCTION_CALL_PLACEHOLDER ) - { - //bytecode.all[a] = bytecode.all[a-1]; - bytecode.all[a-2] = O_AssignToGlobalAndPop; - bytecode.all.shave(1); - bytecode.opcodes[o] = O_AssignToGlobalAndPop; - } - else - { - bytecode.all[ a - 2 ] = O_AssignToGlobalAndPop; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - - return; - } - else if ( bytecode.opcodes[ o - 1 ] == O_LoadFromLocal ) - { - if ( o > 1 && bytecode.opcodes[o-2] == O_LiteralInt8 ) - { - bytecode.all[ a - 4 ] = O_LiteralInt8ToLocal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 3 ]; - bytecode.all[ a - 3 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( o > 1 && bytecode.opcodes[o-2] == O_LiteralInt16 ) - { - bytecode.all[ a - 5 ] = O_LiteralInt16ToLocal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 3 ]; - bytecode.all[ a - 3 ] = bytecode.all[ a - 4 ]; - bytecode.all[ a - 4 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( o > 1 && bytecode.opcodes[o-2] == O_LiteralInt32 ) - { - bytecode.all[ a - 7 ] = O_LiteralInt32ToLocal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 3 ]; - bytecode.all[ a - 3 ] = bytecode.all[ a - 4 ]; - bytecode.all[ a - 4 ] = bytecode.all[ a - 5 ]; - bytecode.all[ a - 5 ] = bytecode.all[ a - 6 ]; - bytecode.all[ a - 6 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( o > 1 && bytecode.opcodes[o-2] == O_LiteralFloat ) - { - bytecode.all[ a - 7 ] = O_LiteralFloatToLocal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 3 ]; - bytecode.all[ a - 3 ] = bytecode.all[ a - 4 ]; - bytecode.all[ a - 4 ] = bytecode.all[ a - 5 ]; - bytecode.all[ a - 5 ] = bytecode.all[ a - 6 ]; - bytecode.all[ a - 6 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( o > 1 && bytecode.opcodes[o-2] == O_LiteralZero ) - { - bytecode.all[ a - 3 ] = O_LiteralInt8ToLocal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 1 ]; - bytecode.all[ a - 1 ] = 0; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - else if ( o > 1 && bytecode.opcodes[o - 2] == O_BinaryDivision ) - { - bytecode.all[ a - 3 ] = O_BinaryDivisionAndStoreLocal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( o > 1 && bytecode.opcodes[o - 2] == O_BinaryAddition ) - { - bytecode.all[ a - 3 ] = O_BinaryAdditionAndStoreLocal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( o > 1 && bytecode.opcodes[o - 2] == O_BinaryMultiplication ) - { - bytecode.all[ a - 3 ] = O_BinaryMultiplicationAndStoreLocal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else if ( o > 1 && bytecode.opcodes[o - 2] == O_BinarySubtraction ) - { - bytecode.all[ a - 3 ] = O_BinarySubtractionAndStoreLocal; - bytecode.all[ a - 2 ] = bytecode.all[ a - 1 ]; - bytecode.all.shave(2); - bytecode.opcodes.clear(); - } - else - { - bytecode.all[ a - 2 ] = O_AssignToLocalAndPop; - bytecode.all.shave(1); - bytecode.opcodes.clear(); - } - return; - } - } - - bytecode.all[a] = O_AssignAndPop; - bytecode.opcodes[o] = O_AssignAndPop; - return; - } - else if ( bytecode.opcodes[o] == O_LiteralZero ) // put a zero on just to pop it off.. - { - bytecode.opcodes.clear(); - bytecode.all.shave(1); - return; - } - else if ( bytecode.opcodes[o] == O_SubtractAssign ) - { - bytecode.all[a] = O_SubtractAssignAndPop; - bytecode.opcodes[o] = O_SubtractAssignAndPop; - return; - } - else if ( bytecode.opcodes[o] == O_AddAssign ) - { - bytecode.all[a] = O_AddAssignAndPop; - bytecode.opcodes[o] = O_AddAssignAndPop; - return; - } - else if ( bytecode.opcodes[o] == O_ModAssign ) - { - bytecode.all[a] = O_ModAssignAndPop; - bytecode.opcodes[o] = O_ModAssignAndPop; - return; - } - else if ( bytecode.opcodes[o] == O_MultiplyAssign ) - { - bytecode.all[a] = O_MultiplyAssignAndPop; - bytecode.opcodes[o] = O_MultiplyAssignAndPop; - return; - } - else if ( bytecode.opcodes[o] == O_DivideAssign ) - { - bytecode.all[a] = O_DivideAssignAndPop; - bytecode.opcodes[o] = O_DivideAssignAndPop; - return; - } - else if ( bytecode.opcodes[o] == O_ORAssign ) - { - bytecode.all[a] = O_ORAssignAndPop; - bytecode.opcodes[o] = O_ORAssignAndPop; - return; - } - else if ( bytecode.opcodes[o] == O_ANDAssign ) - { - bytecode.all[a] = O_ANDAssignAndPop; - bytecode.opcodes[o] = O_ANDAssignAndPop; - return; - } - else if ( bytecode.opcodes[o] == O_XORAssign ) - { - bytecode.all[a] = O_XORAssignAndPop; - bytecode.opcodes[o] = O_XORAssignAndPop; - return; - } - else if ( bytecode.opcodes[o] == O_RightShiftAssign ) - { - bytecode.all[a] = O_RightShiftAssignAndPop; - bytecode.opcodes[o] = O_RightShiftAssignAndPop; - return; - } - else if ( bytecode.opcodes[o] == O_LeftShiftAssign ) - { - bytecode.all[a] = O_LeftShiftAssignAndPop; - bytecode.opcodes[o] = O_LeftShiftAssignAndPop; - return; - } - } - } -#endif - bytecode.all += opcode; - bytecode.opcodes += opcode; -} - -//------------------------------------------------------------------------------ -void WRCompilationContext::pushDebug( uint16_t code, WRBytecode& bytecode, int param ) -{ -#ifdef WRENCH_INCLUDE_DEBUG_CODE - if ( !m_addDebugLineNumbers ) - { - return; - } - - if ( param == -1 ) - { - int onChar; - getSourcePosition( param, onChar ); - - if ( param == m_lastLineNumber && code == m_lastCode ) - { - return; - } - - m_lastLineNumber = param; - m_lastCode = code; - } - - pushOpcode( bytecode, O_DebugInfo ); - -// uint16_t codeword = code | ((uint16_t)param & PayloadMask); -// unsigned char data[2]; -// pushData( bytecode, wr_pack16(codeword, data), 2 ); -#endif - -} - -//------------------------------------------------------------------------------ -void WRCompilationContext::getSourcePosition( int& onLine, int& onChar, WRstr* line ) -{ - onChar = 0; - onLine = 1; - - for( int p = 0; line && pclear(); - for( int p = i+1; p -129) && !no8version ) - { - switch( o ) - { - case O_RelativeJump: *bytecode.all.p_str(offset - 1) = O_RelativeJump8; break; - case O_BZ: *bytecode.all.p_str(offset - 1) = O_BZ8; break; - - case O_CompareBEQ: *bytecode.all.p_str(offset - 1) = O_CompareBEQ8; break; - case O_CompareBNE: *bytecode.all.p_str(offset - 1) = O_CompareBNE8; break; - case O_CompareBGE: *bytecode.all.p_str(offset - 1) = O_CompareBGE8; break; - case O_CompareBLE: *bytecode.all.p_str(offset - 1) = O_CompareBLE8; break; - case O_CompareBGT: *bytecode.all.p_str(offset - 1) = O_CompareBGT8; break; - case O_CompareBLT: *bytecode.all.p_str(offset - 1) = O_CompareBLT8; break; - - case O_GSCompareEQBZ: *bytecode.all.p_str(offset - 1) = O_GSCompareEQBZ8; ++offset; break; - case O_LSCompareEQBZ: *bytecode.all.p_str(offset - 1) = O_LSCompareEQBZ8; ++offset; break; - case O_GSCompareNEBZ: *bytecode.all.p_str(offset - 1) = O_GSCompareNEBZ8; ++offset; break; - case O_LSCompareNEBZ: *bytecode.all.p_str(offset - 1) = O_LSCompareNEBZ8; ++offset; break; - case O_GSCompareGEBZ: *bytecode.all.p_str(offset - 1) = O_GSCompareGEBZ8; ++offset; break; - case O_LSCompareGEBZ: *bytecode.all.p_str(offset - 1) = O_LSCompareGEBZ8; ++offset; break; - case O_GSCompareLEBZ: *bytecode.all.p_str(offset - 1) = O_GSCompareLEBZ8; ++offset; break; - case O_LSCompareLEBZ: *bytecode.all.p_str(offset - 1) = O_LSCompareLEBZ8; ++offset; break; - case O_GSCompareGTBZ: *bytecode.all.p_str(offset - 1) = O_GSCompareGTBZ8; ++offset; break; - case O_LSCompareGTBZ: *bytecode.all.p_str(offset - 1) = O_LSCompareGTBZ8; ++offset; break; - case O_GSCompareLTBZ: *bytecode.all.p_str(offset - 1) = O_GSCompareLTBZ8; ++offset; break; - case O_LSCompareLTBZ: *bytecode.all.p_str(offset - 1) = O_LSCompareLTBZ8; ++offset; break; - - case O_LLCompareLTBZ: *bytecode.all.p_str(offset - 1) = O_LLCompareLTBZ8; offset += 2; break; - case O_LLCompareGTBZ: *bytecode.all.p_str(offset - 1) = O_LLCompareGTBZ8; offset += 2; break; - case O_LLCompareLEBZ: *bytecode.all.p_str(offset - 1) = O_LLCompareLEBZ8; offset += 2; break; - case O_LLCompareGEBZ: *bytecode.all.p_str(offset - 1) = O_LLCompareGEBZ8; offset += 2; break; - case O_LLCompareEQBZ: *bytecode.all.p_str(offset - 1) = O_LLCompareEQBZ8; offset += 2; break; - case O_LLCompareNEBZ: *bytecode.all.p_str(offset - 1) = O_LLCompareNEBZ8; offset += 2; break; - case O_GGCompareLTBZ: *bytecode.all.p_str(offset - 1) = O_GGCompareLTBZ8; offset += 2; break; - case O_GGCompareGTBZ: *bytecode.all.p_str(offset - 1) = O_GGCompareGTBZ8; offset += 2; break; - case O_GGCompareLEBZ: *bytecode.all.p_str(offset - 1) = O_GGCompareLEBZ8; offset += 2; break; - case O_GGCompareGEBZ: *bytecode.all.p_str(offset - 1) = O_GGCompareGEBZ8; offset += 2; break; - case O_GGCompareEQBZ: *bytecode.all.p_str(offset - 1) = O_GGCompareEQBZ8; offset += 2; break; - case O_GGCompareNEBZ: *bytecode.all.p_str(offset - 1) = O_GGCompareNEBZ8; offset += 2; break; - - case O_BLA: *bytecode.all.p_str(offset - 1) = O_BLA8; break; - case O_BLO: *bytecode.all.p_str(offset - 1) = O_BLO8; break; - - // no work to be done - case O_RelativeJump8: - case O_BZ8: - case O_BLA8: - case O_BLO8: - case O_CompareBLE8: - case O_CompareBGE8: - case O_CompareBGT8: - case O_CompareBLT8: - case O_CompareBEQ8: - case O_CompareBNE8: - break; - - case O_GSCompareEQBZ8: - case O_LSCompareEQBZ8: - case O_GSCompareNEBZ8: - case O_LSCompareNEBZ8: - case O_GSCompareGEBZ8: - case O_LSCompareGEBZ8: - case O_GSCompareLEBZ8: - case O_LSCompareLEBZ8: - case O_GSCompareGTBZ8: - case O_LSCompareGTBZ8: - case O_GSCompareLTBZ8: - case O_LSCompareLTBZ8: - ++offset; - break; - - case O_LLCompareLTBZ8: - case O_LLCompareGTBZ8: - case O_LLCompareLEBZ8: - case O_LLCompareGEBZ8: - case O_LLCompareEQBZ8: - case O_LLCompareNEBZ8: - case O_GGCompareLTBZ8: - case O_GGCompareGTBZ8: - case O_GGCompareLEBZ8: - case O_GGCompareGEBZ8: - case O_GGCompareEQBZ8: - case O_GGCompareNEBZ8: - { - offset += 2; - break; - } - - default: - m_err = WR_ERR_compiler_panic; - return; - } - - *bytecode.all.p_str(offset) = (int8_t)diff; - } - else - { - switch( o ) - { - // check to see if any were pushed into 16-bit land - // that were previously optimized - case O_RelativeJump8: *bytecode.all.p_str(offset - 1) = O_RelativeJump; break; - case O_BZ8: *bytecode.all.p_str(offset - 1) = O_BZ; break; - case O_CompareBEQ8: *bytecode.all.p_str(offset - 1) = O_CompareBEQ; break; - case O_CompareBNE8: *bytecode.all.p_str(offset - 1) = O_CompareBNE; break; - case O_CompareBGE8: *bytecode.all.p_str(offset - 1) = O_CompareBGE; break; - case O_CompareBLE8: *bytecode.all.p_str(offset - 1) = O_CompareBLE; break; - case O_CompareBGT8: *bytecode.all.p_str(offset - 1) = O_CompareBGT; break; - case O_CompareBLT8: *bytecode.all.p_str(offset - 1) = O_CompareBLT; break; - case O_GSCompareEQBZ8: *bytecode.all.p_str(offset - 1) = O_GSCompareEQBZ; ++offset; break; - case O_LSCompareEQBZ8: *bytecode.all.p_str(offset - 1) = O_LSCompareEQBZ; ++offset; break; - case O_GSCompareNEBZ8: *bytecode.all.p_str(offset - 1) = O_GSCompareNEBZ; ++offset; break; - case O_LSCompareNEBZ8: *bytecode.all.p_str(offset - 1) = O_LSCompareNEBZ; ++offset; break; - case O_GSCompareGEBZ8: *bytecode.all.p_str(offset - 1) = O_GSCompareGEBZ; ++offset; break; - case O_LSCompareGEBZ8: *bytecode.all.p_str(offset - 1) = O_LSCompareGEBZ; ++offset; break; - case O_GSCompareLEBZ8: *bytecode.all.p_str(offset - 1) = O_GSCompareLEBZ; ++offset; break; - case O_LSCompareLEBZ8: *bytecode.all.p_str(offset - 1) = O_LSCompareLEBZ; ++offset; break; - case O_GSCompareGTBZ8: *bytecode.all.p_str(offset - 1) = O_GSCompareGTBZ; ++offset; break; - case O_LSCompareGTBZ8: *bytecode.all.p_str(offset - 1) = O_LSCompareGTBZ; ++offset; break; - case O_GSCompareLTBZ8: *bytecode.all.p_str(offset - 1) = O_GSCompareLTBZ; ++offset; break; - case O_LSCompareLTBZ8: *bytecode.all.p_str(offset - 1) = O_LSCompareLTBZ; ++offset; break; - - case O_LLCompareLTBZ8: *bytecode.all.p_str(offset - 1) = O_LLCompareLTBZ; offset += 2; break; - case O_LLCompareGTBZ8: *bytecode.all.p_str(offset - 1) = O_LLCompareGTBZ; offset += 2; break; - case O_LLCompareLEBZ8: *bytecode.all.p_str(offset - 1) = O_LLCompareLEBZ; offset += 2; break; - case O_LLCompareGEBZ8: *bytecode.all.p_str(offset - 1) = O_LLCompareGEBZ; offset += 2; break; - case O_LLCompareEQBZ8: *bytecode.all.p_str(offset - 1) = O_LLCompareEQBZ; offset += 2; break; - case O_LLCompareNEBZ8: *bytecode.all.p_str(offset - 1) = O_LLCompareNEBZ; offset += 2; break; - case O_GGCompareLTBZ8: *bytecode.all.p_str(offset - 1) = O_GGCompareLTBZ; offset += 2; break; - case O_GGCompareGTBZ8: *bytecode.all.p_str(offset - 1) = O_GGCompareGTBZ; offset += 2; break; - case O_GGCompareLEBZ8: *bytecode.all.p_str(offset - 1) = O_GGCompareLEBZ; offset += 2; break; - case O_GGCompareGEBZ8: *bytecode.all.p_str(offset - 1) = O_GGCompareGEBZ; offset += 2; break; - case O_GGCompareEQBZ8: *bytecode.all.p_str(offset - 1) = O_GGCompareEQBZ; offset += 2; break; - case O_GGCompareNEBZ8: *bytecode.all.p_str(offset - 1) = O_GGCompareNEBZ; offset += 2; break; - - case O_GGNextKeyValueOrJump: - case O_GLNextKeyValueOrJump: - case O_LGNextKeyValueOrJump: - case O_LLNextKeyValueOrJump: - { - offset += 3; - break; - } - - case O_GNextValueOrJump: - case O_LNextValueOrJump: - { - offset += 2; - break; - } - - case O_BLA8: *bytecode.all.p_str(offset - 1) = O_BLA; break; - case O_BLO8: *bytecode.all.p_str(offset - 1) = O_BLO; break; - - // no work to be done, already visited - case O_RelativeJump: - case O_BZ: - case O_BLA: - case O_BLO: - case O_CompareBLE: - case O_CompareBGE: - case O_CompareBGT: - case O_CompareBLT: - case O_CompareBEQ: - case O_CompareBNE: - break; - - case O_GSCompareEQBZ: - case O_LSCompareEQBZ: - case O_GSCompareNEBZ: - case O_LSCompareNEBZ: - case O_GSCompareGEBZ: - case O_LSCompareGEBZ: - case O_GSCompareLEBZ: - case O_LSCompareLEBZ: - case O_GSCompareGTBZ: - case O_LSCompareGTBZ: - case O_GSCompareLTBZ: - case O_LSCompareLTBZ: - ++offset; - break; - - case O_LLCompareLTBZ: - case O_LLCompareGTBZ: - case O_LLCompareLEBZ: - case O_LLCompareGEBZ: - case O_LLCompareEQBZ: - case O_LLCompareNEBZ: - case O_GGCompareLTBZ: - case O_GGCompareGTBZ: - case O_GGCompareLEBZ: - case O_GGCompareGEBZ: - case O_GGCompareEQBZ: - case O_GGCompareNEBZ: - { - offset += 2; - break; - } - - default: - { - m_err = WR_ERR_compiler_panic; - return; - } - } - - wr_pack16( diff, bytecode.all.p_str(offset) ); - } - } - } -} - -//------------------------------------------------------------------------------ -void WRCompilationContext::appendBytecode( WRBytecode& bytecode, WRBytecode& addMe ) -{ - for (unsigned int n = 0; n < addMe.jumpOffsetTargets.count(); ++n) - { - if (addMe.jumpOffsetTargets[n].gotoHash) - { - int index = addRelativeJumpTarget(bytecode); - bytecode.jumpOffsetTargets[index].gotoHash = addMe.jumpOffsetTargets[n].gotoHash; - bytecode.jumpOffsetTargets[index].offset = addMe.jumpOffsetTargets[n].offset + bytecode.all.size(); - } - } - - // add the namespace, making sure to offset it into the new block properly - for (unsigned int n = 0; n < addMe.localSpace.count(); ++n) - { - unsigned int m = 0; - for (m = 0; m < bytecode.localSpace.count(); ++m) - { - if (bytecode.localSpace[m].hash == addMe.localSpace[n].hash) - { - for (unsigned int s = 0; s < addMe.localSpace[n].references.count(); ++s) - { - bytecode.localSpace[m].references.append() = addMe.localSpace[n].references[s] + bytecode.all.size(); - } - - break; - } - } - - if (m >= bytecode.localSpace.count()) - { - WRNamespaceLookup* space = &bytecode.localSpace.append(); - *space = addMe.localSpace[n]; - for (unsigned int s = 0; s < space->references.count(); ++s) - { - space->references[s] += bytecode.all.size(); - } - } - } - - // add the function space, making sure to offset it into the new block properly - for (unsigned int n = 0; n < addMe.functionSpace.count(); ++n) - { - WRNamespaceLookup* space = &bytecode.functionSpace.append(); - *space = addMe.functionSpace[n]; - for (unsigned int s = 0; s < space->references.count(); ++s) - { - space->references[s] += bytecode.all.size(); - } - } - - // add the function space, making sure to offset it into the new block properly - for (unsigned int u = 0; u < addMe.unitObjectSpace.count(); ++u) - { - WRNamespaceLookup* space = &bytecode.unitObjectSpace.append(); - *space = addMe.unitObjectSpace[u]; - for (unsigned int s = 0; s < space->references.count(); ++s) - { - space->references[s] += bytecode.all.size(); - } - } - - // add the goto targets, making sure to offset it into the new block properly - for (unsigned int u = 0; u < addMe.gotoSource.count(); ++u) - { - GotoSource* G = &bytecode.gotoSource.append(); - G->hash = addMe.gotoSource[u].hash; - G->offset = addMe.gotoSource[u].offset + bytecode.all.size(); - } - - if ( bytecode.all.size() > 1 - && bytecode.opcodes.size() > 0 - && addMe.opcodes.size() == 1 - && addMe.all.size() > 2 - && addMe.gotoSource.count() == 0 - && addMe.opcodes[0] == O_IndexLiteral16 - && bytecode.opcodes[bytecode.opcodes.size() - 1] == O_LoadFromLocal ) - { - bytecode.all[bytecode.all.size()-2] = O_IndexLocalLiteral16; - for( unsigned int i=1; i 1 - && bytecode.opcodes.size() > 0 - && addMe.opcodes.size() == 1 - && addMe.all.size() > 2 - && addMe.gotoSource.count() == 0 - && addMe.opcodes[0] == O_IndexLiteral16 - && bytecode.opcodes[bytecode.opcodes.size() - 1] == O_LoadFromGlobal ) - { - bytecode.all[bytecode.all.size()-2] = O_IndexGlobalLiteral16; - for( unsigned int i=1; i 1 - && bytecode.opcodes.size() > 0 - && addMe.opcodes.size() == 1 - && addMe.all.size() > 1 - && addMe.gotoSource.count() == 0 - && addMe.opcodes[0] == O_IndexLiteral8 - && bytecode.opcodes[bytecode.opcodes.size() - 1] == O_LoadFromLocal ) - { - bytecode.all[bytecode.all.size()-2] = O_IndexLocalLiteral8; - for( unsigned int i=1; i 1 - && bytecode.opcodes.size() > 0 - && addMe.opcodes.size() == 1 - && addMe.all.size() > 1 - && addMe.gotoSource.count() == 0 - && addMe.opcodes[0] == O_IndexLiteral8 - && bytecode.opcodes[bytecode.opcodes.size() - 1] == O_LoadFromGlobal ) - { - bytecode.all[bytecode.all.size()-2] = O_IndexGlobalLiteral8; - for( unsigned int i=1; i 0 - && bytecode.opcodes.size() > 0 - && addMe.opcodes.size() == 2 - && addMe.gotoSource.count() == 0 - && addMe.all.size() == 3 - && addMe.opcodes[1] == O_Index ) - { - if ( bytecode.opcodes[bytecode.opcodes.size() - 1] == O_LoadFromLocal - && addMe.opcodes[0] == O_LoadFromLocal ) - { - int a = bytecode.all.size() - 1; - - addMe.all[0] = bytecode.all[a]; - bytecode.all[a] = addMe.all[1]; - bytecode.all[a-1] = O_LLValues; - bytecode.all += addMe.all[0]; - bytecode.all += O_IndexSkipLoad; - - bytecode.opcodes += O_LoadFromLocal; - bytecode.opcodes += O_IndexSkipLoad; - return; - } - else if ( bytecode.opcodes[bytecode.opcodes.size() - 1] == O_LoadFromGlobal - && addMe.opcodes[0] == O_LoadFromGlobal ) - { - int a = bytecode.all.size() - 1; - - addMe.all[0] = bytecode.all[a]; - bytecode.all[a] = addMe.all[1]; - bytecode.all[a-1] = O_GGValues; - bytecode.all += addMe.all[0]; - bytecode.all += O_IndexSkipLoad; - - bytecode.opcodes += O_LoadFromLocal; - bytecode.opcodes += O_IndexSkipLoad; - return; - } - else if ( bytecode.opcodes[bytecode.opcodes.size() - 1] == O_LoadFromLocal - && addMe.opcodes[0] == O_LoadFromGlobal ) - { - int a = bytecode.all.size() - 1; - - addMe.all[0] = bytecode.all[a]; - bytecode.all[a] = addMe.all[1]; - bytecode.all[a-1] = O_GLValues; - bytecode.all += addMe.all[0]; - bytecode.all += O_IndexSkipLoad; - - bytecode.opcodes += O_LoadFromLocal; - bytecode.opcodes += O_IndexSkipLoad; - return; - } - else if ( bytecode.opcodes[bytecode.opcodes.size() - 1] == O_LoadFromGlobal - && addMe.opcodes[0] == O_LoadFromLocal ) - { - int a = bytecode.all.size() - 1; - - addMe.all[0] = bytecode.all[a]; - bytecode.all[a] = addMe.all[1]; - bytecode.all[a-1] = O_LGValues; - bytecode.all += addMe.all[0]; - bytecode.all += O_IndexSkipLoad; - - bytecode.opcodes += O_LoadFromLocal; - bytecode.opcodes += O_IndexSkipLoad; - return; - } - } - else if ( addMe.all.size() == 1 ) - { - if ( addMe.opcodes[0] == O_HASH_PLACEHOLDER ) - { - if ( bytecode.opcodes.size() > 1 - && bytecode.opcodes[ bytecode.opcodes.size() - 2 ] == O_LiteralInt32 - && bytecode.opcodes[ bytecode.opcodes.size() - 1 ] == O_LoadFromLocal ) - { - int o = bytecode.all.size() - 7; - - bytecode.all[o] = O_LocalIndexHash; - bytecode.all[o + 5] = bytecode.all[o + 4]; - bytecode.all[o + 4] = bytecode.all[o + 3]; - bytecode.all[o + 3] = bytecode.all[o + 2]; - bytecode.all[o + 2] = bytecode.all[o + 1]; - bytecode.all[o + 1] = bytecode.all[o + 6]; - - bytecode.opcodes.clear(); - bytecode.all.shave(1); - } - else if (bytecode.opcodes.size() > 1 - && bytecode.opcodes[ bytecode.opcodes.size() - 2 ] == O_LiteralInt32 - && bytecode.opcodes[ bytecode.opcodes.size() - 1 ] == O_LoadFromGlobal ) - { - int o = bytecode.all.size() - 7; - - bytecode.all[o] = O_GlobalIndexHash; - bytecode.all[o + 5] = bytecode.all[o + 4]; - bytecode.all[o + 4] = bytecode.all[o + 3]; - bytecode.all[o + 3] = bytecode.all[o + 2]; - bytecode.all[o + 2] = bytecode.all[o + 1]; - bytecode.all[o + 1] = bytecode.all[o + 6]; - - bytecode.opcodes.clear(); - bytecode.all.shave(1); - } - else if (bytecode.opcodes.size() > 1 - && bytecode.opcodes[ bytecode.opcodes.size() - 2 ] == O_LiteralInt32 - && bytecode.opcodes[ bytecode.opcodes.size() - 1 ] == O_StackSwap ) - { - int a = bytecode.all.size() - 7; - - bytecode.all[a] = O_StackIndexHash; - - bytecode.opcodes.clear(); - bytecode.all.shave(2); - - } - else - { - m_err = WR_ERR_compiler_panic; - } - - return; - } - - pushOpcode( bytecode, (WROpcode)addMe.opcodes[0] ); - return; - } - - resolveRelativeJumps( addMe ); - - bytecode.all += addMe.all; - bytecode.opcodes += addMe.opcodes; -} - -//------------------------------------------------------------------------------ -void WRCompilationContext::pushLiteral( WRBytecode& bytecode, WRExpressionContext& context ) -{ - WRValue& value = context.value; - unsigned char data[4]; - - if ( value.type == WR_INT && value.i == 0 ) - { - pushOpcode( bytecode, O_LiteralZero ); - } - else if ( value.type == WR_INT ) - { - if ( (value.i <= 127) && (value.i >= -128) ) - { - pushOpcode( bytecode, O_LiteralInt8 ); - unsigned char be = (char)value.i; - pushData( bytecode, &be, 1 ); - } - else if ( (value.i <= 32767) && (value.i >= -32768) ) - { - pushOpcode( bytecode, O_LiteralInt16 ); - int16_t be = value.i; - pushData( bytecode, wr_pack16(be, data), 2 ); - } - else - { - pushOpcode( bytecode, O_LiteralInt32 ); - unsigned char data[4]; - pushData( bytecode, wr_pack32(value.i, data), 4 ); - } - } - else if ( value.type == WR_COMPILER_LITERAL_STRING ) - { - pushOpcode( bytecode, O_LiteralString ); - int16_t be = context.literalString.size(); - pushData( bytecode, wr_pack16(be, data), 2 ); - for( unsigned int i=0; i= bytecode.localSpace.count() && !addOnly ) - { - WRstr t2; - if (token[0] == ':' && token[1] == ':') - { - t2 = token; - } - else - { - t2.format("::%s", token.c_str()); - } - - uint32_t ghash = wr_hashStr(t2); - - // was NOT found locally which is possible for a "global" if - // the argument list names it, now check global with the global - // hash - if ( !bytecode.isStructSpace ) // structs are immune to this - { - for (unsigned int j = 0; j < m_units[0].bytecode.localSpace.count(); ++j) - { - if (m_units[0].bytecode.localSpace[j].hash == ghash) - { - pushOpcode(bytecode, O_LoadFromGlobal); - unsigned char c = j; - pushData(bytecode, &c, 1); - return j; - } - } - } - } - - bytecode.localSpace[i].hash = hash; - bytecode.localSpace[i].label = token; - - if ( !addOnly ) - { - pushOpcode( bytecode, O_LoadFromLocal ); - unsigned char c = i; - pushData( bytecode, &c, 1 ); - } - - return i; -} - -//------------------------------------------------------------------------------ -int WRCompilationContext::addGlobalSpaceLoad( WRBytecode& bytecode, WRstr& token, bool addOnly ) -{ - uint32_t hash; - WRstr t2; - if ( token[0] == ':' && token[1] == ':' ) - { - t2 = token; - } - else - { - t2.format( "::%s", token.c_str() ); - } - hash = wr_hash( t2, t2.size() ); - - unsigned int i=0; - - for( ; i 0 - && expression.context[operation].operation - && expression.context[operation].operation->opcode == O_HASH_PLACEHOLDER - && depth > operation ) - { - unsigned char buf[4]; - wr_pack32( wr_hash( expression.context[depth].token, - expression.context[depth].token.size()), - buf ); - pushOpcode( expression.bytecode, O_LiteralInt32 ); - pushData( expression.bytecode, buf, 4 ); - } - else - { - switch( expression.context[depth].type ) - { - case EXTYPE_LIB_CONSTANT: - { - pushLibConstant( expression.bytecode, expression.context[depth] ); - break; - } - - case EXTYPE_LITERAL: - { - pushLiteral( expression.bytecode, expression.context[depth] ); - break; - } - - case EXTYPE_LABEL: - { - if ( expression.context[depth].global ) - { - addGlobalSpaceLoad( expression.bytecode, - expression.context[depth].token ); - } - else - { - addLocalSpaceLoad( expression.bytecode, - expression.context[depth].token ); - } - - break; - } - - case EXTYPE_BYTECODE_RESULT: - { - appendBytecode( expression.bytecode, - expression.context[depth].bytecode ); - break; - } - - case EXTYPE_RESOLVED: - case EXTYPE_OPERATION: - default: - { - break; - } - } - } - - expression.pushToStack( depth ); // make sure stack knows something got loaded on top of it - - expression.context[depth].type = EXTYPE_RESOLVED; // this slot is now a resolved value sitting on the stack -} - -//------------------------------------------------------------------------------ -void WRExpression::swapWithTop( int stackPosition, bool addOpcodes ) -{ - if ( stackPosition == 0 ) - { - return; - } - - unsigned int currentTop = -1; - unsigned int swapWith = -1; - for( unsigned int i=0; i 1; ++p ) - { - // left to right operations - for( int o=0; (unsigned int)o < expression.context.count(); ++o ) - { - if ( (expression.context[o].type != EXTYPE_OPERATION) - || expression.context[o].operation->precedence > p - || !expression.context[o].operation->leftToRight ) - { - continue; - } - - resolves += resolveExpressionEx( expression, o, p ); - if (m_err) - { - return; - } - o = (unsigned int)-1; - } - - // right to left operations - for( int o = expression.context.count() - 1; o >= 0; --o ) - { - if ( (expression.context[o].type != EXTYPE_OPERATION) - || expression.context[o].operation->precedence > p - || expression.context[o].operation->leftToRight ) - { - continue; - } - - resolves += resolveExpressionEx( expression, o, p ); - if (m_err) - { - return; - } - o = expression.context.count(); - } - } - - if ( startedWith && (resolves != startedWith) ) - { - m_err = WR_ERR_bad_expression; - return; - } -} - -//------------------------------------------------------------------------------ -unsigned int WRCompilationContext::resolveExpressionEx( WRExpression& expression, int o, int p ) -{ - unsigned int ret = 0; - switch( expression.context[o].operation->type ) - { - case WR_OPER_PRE: - { - ret = 1; - - if ( expression.context[o + 1].stackPosition == -1 ) - { - loadExpressionContext( expression, o + 1, 0 ); // load argument - } - else if ( expression.context[o + 1].stackPosition != 0 ) - { - expression.swapWithTop( expression.context[o + 1].stackPosition ); - } - - appendBytecode( expression.bytecode, expression.context[o].bytecode ); // apply operator - expression.context.remove( o, 1 ); // knock off operator - expression.pushToStack(o); - break; - } - - case WR_OPER_BINARY_COMMUTE: - { - // for operations like + and * the operands can be in any - // order, so don't go to any great lengths to shift the stack - // around for them - - ret = 2; - - if( o == 0 ) - { - m_err = WR_ERR_bad_expression; - return 0; - } - - int second = o + 1; // push first - int first = o - 1; // push second - // so - // 1 - first - // [oper] - // 0 - second - - if ( expression.context[second].stackPosition == -1 ) - { - if ( expression.context[first].stackPosition == -1 ) - { - loadExpressionContext( expression, first, o ); - } - else if ( expression.context[first].stackPosition != 0 ) - { - // otherwise swap first to the top and load the second - expression.swapWithTop( expression.context[first].stackPosition ); - } - - loadExpressionContext( expression, second, o ); - } - else if ( expression.context[first].stackPosition == -1 ) - { - if ( expression.context[second].stackPosition != 0 ) - { - expression.swapWithTop( expression.context[second].stackPosition ); - } - - // just load the second to top - loadExpressionContext( expression, first, o ); - } - else if ( expression.context[second].stackPosition == 1 ) - { - expression.swapWithTop( expression.context[first].stackPosition ); - } - else if ( expression.context[first].stackPosition == 1 ) - { - expression.swapWithTop( expression.context[second].stackPosition ); - } - else - { - // first and second are both loaded but neither - // is in the correct position - - WRCompilationContext::pushOpcode( expression.bytecode, O_SwapTwoToTop ); - unsigned char pos = expression.context[first].stackPosition + 1; - WRCompilationContext::pushData( expression.bytecode, &pos, 1 ); - pos = expression.context[second].stackPosition + 1; - WRCompilationContext::pushData( expression.bytecode, &pos, 1 ); - - expression.swapWithTop( expression.context[second].stackPosition, false ); - expression.swapWithTop( 1, false ); - expression.swapWithTop( expression.context[first].stackPosition, false ); - } - - appendBytecode( expression.bytecode, expression.context[o].bytecode ); // apply operator - - expression.context.remove( o - 1, 2 ); // knock off operator and arg - expression.pushToStack(o - 1); - - break; - } - - case WR_OPER_BINARY: - { - ret = 2; - - if( o == 0 ) - { - m_err = WR_ERR_bad_expression; - return 0; - } - - int second = o + 1; // push first - int first = o - 1; // push second - bool useAlt = false; - // so - // 1 - first - // [oper] - // 0 - second - - if ( expression.context[second].stackPosition == -1 ) - { - if ( expression.context[first].stackPosition == -1 ) - { - loadExpressionContext( expression, second, o ); // nope, grab 'em - loadExpressionContext( expression, first, o ); - } - else - { - // first is in the stack somewhere, we need - // to swap it to the top, then load, then - // swap top values - expression.swapWithTop( expression.context[first].stackPosition ); - - loadExpressionContext( expression, second, o ); - - if ( expression.context[o].operation->alt == O_LAST ) - { - expression.swapWithTop( 1 ); - } - else // otherwise top shuffle is NOT required because we have an equal-but-opposite operation - { - useAlt = true; - } - } - } - else if ( expression.context[first].stackPosition == -1 ) - { - // perfect, swap up the second, then load the - // first - - expression.swapWithTop( expression.context[second].stackPosition ); - - loadExpressionContext( expression, first, o ); - } - else if ( expression.context[second].stackPosition == 1 ) - { - // second is already where its supposed to be, - // swap first to top if it's not there already - if ( expression.context[first].stackPosition != 0 ) - { - expression.swapWithTop( expression.context[first].stackPosition ); - } - } - else if ( expression.context[second].stackPosition == 0 ) - { - // second is on top of the stack, swap with - // next level down then swap first up - - WRCompilationContext::pushOpcode( expression.bytecode, O_SwapTwoToTop ); - unsigned char pos = expression.context[first].stackPosition + 1; - WRCompilationContext::pushData( expression.bytecode, &pos, 1 ); - pos = 2; - WRCompilationContext::pushData( expression.bytecode, &pos, 1 ); - - expression.swapWithTop( 1, false ); - expression.swapWithTop( expression.context[first].stackPosition, false ); - } - else - { - // first and second are both loaded but neither is in the correct position - - WRCompilationContext::pushOpcode( expression.bytecode, O_SwapTwoToTop ); - unsigned char pos = expression.context[first].stackPosition + 1; - WRCompilationContext::pushData( expression.bytecode, &pos, 1 ); - pos = expression.context[second].stackPosition + 1; - WRCompilationContext::pushData( expression.bytecode, &pos, 1 ); - - expression.swapWithTop( expression.context[second].stackPosition, false ); - expression.swapWithTop( 1, false ); - expression.swapWithTop( expression.context[first].stackPosition, false ); - } - - if ( useAlt ) - { - pushOpcode( expression.bytecode, expression.context[o].operation->alt ); - } - else - { - appendBytecode( expression.bytecode, expression.context[o].bytecode ); // apply operator - } - - expression.context.remove( o - 1, 2 ); // knock off operator and arg - expression.pushToStack(o - 1); - - break; - } - - case WR_OPER_POST: - { - ret = 1; - - if( o == 0 ) - { - m_err = WR_ERR_bad_expression; - return 0; - } - - if ( expression.context[o - 1].stackPosition == -1 ) - { - loadExpressionContext( expression, o - 1, o ); // load argument - } - else if ( expression.context[o - 1].stackPosition != 0 ) - { - expression.swapWithTop( expression.context[o - 1].stackPosition ); - } - - appendBytecode( expression.bytecode, expression.context[o].bytecode ); - expression.context.remove( o, 1 ); // knock off operator - expression.pushToStack( o - 1 ); - - break; - } - } - - return ret; -} - -//------------------------------------------------------------------------------ -bool WRCompilationContext::operatorFound( WRstr& token, WRarray& context, int depth ) -{ - for( int i=0; c_operations[i].token; i++ ) - { - if ( token == c_operations[i].token ) - { - if ( c_operations[i].type == WR_OPER_PRE - && depth > 0 - && (context[depth-1].type != EXTYPE_OPERATION - || (context[depth - 1].type == EXTYPE_OPERATION && - (context[depth - 1].operation->type != WR_OPER_BINARY && context[depth - 1].operation->type != WR_OPER_BINARY_COMMUTE))) ) - { - continue; - } - - context[depth].operation = c_operations + i; - context[depth].type = EXTYPE_OPERATION; - - pushOpcode( context[depth].bytecode, c_operations[i].opcode ); - - return true; - } - } - - return false; -} - -//------------------------------------------------------------------------------ -bool WRCompilationContext::parseCallFunction( WRExpression& expression, WRstr functionName, int depth, bool parseArguments ) -{ - WRstr prefix = expression.context[depth].prefix; - - expression.context[depth].type = EXTYPE_BYTECODE_RESULT; - - unsigned char argsPushed = 0; - - if ( parseArguments ) - { - WRstr& token2 = expression.context[depth].token; - WRValue& value2 = expression.context[depth].value; - - for(;;) - { - if ( !getToken(expression.context[depth]) ) - { - m_err = WR_ERR_unexpected_EOF; - return 0; - } - - if ( !m_quoted && token2 == ")" ) - { - break; - } - - ++argsPushed; - - WRExpression nex( expression.bytecode.localSpace, expression.bytecode.isStructSpace ); - nex.context[0].token = token2; - nex.context[0].value = value2; - m_loadedToken = token2; - m_loadedValue = value2; - m_loadedQuoted = m_quoted; - - char end = parseExpression( nex ); - - if ( nex.bytecode.opcodes.size() > 0 ) - { - char op = nex.bytecode.opcodes[nex.bytecode.opcodes.size() - 1]; - if ( op == O_Index - || op == O_IndexSkipLoad - || op == O_IndexLiteral8 - || op == O_IndexLiteral16 - || op == O_IndexLocalLiteral8 - || op == O_IndexGlobalLiteral8 - || op == O_IndexLocalLiteral16 - || op == O_IndexGlobalLiteral16 ) - { - nex.bytecode.opcodes += O_Dereference; - nex.bytecode.all += O_Dereference; - } - } - - appendBytecode( expression.context[depth].bytecode, nex.bytecode ); - - if ( end == ')' ) - { - break; - } - else if ( end != ',' ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - } - } - - pushDebug( FunctionCall, expression.context[depth].bytecode, wr_hashStr(prefix) ); - - if ( prefix.size() ) - { - prefix += "::"; - prefix += functionName; - - unsigned char buf[4]; - wr_pack32( wr_hashStr(prefix), buf ); - - pushOpcode( expression.context[depth].bytecode, O_CallLibFunction ); - pushData( expression.context[depth].bytecode, &argsPushed, 1 ); // arg # - pushData( expression.context[depth].bytecode, buf, 4 ); // hash id - - // normal lib will copy down the result, otherwise - // ignore it, it will be AFTER args - } - else - { - // push the number of args - uint32_t hash = wr_hashStr( functionName ); - - unsigned int i=0; - for( ; i& localSpace, - uint32_t hash ) -{ - WRstr& token2 = context.token; - WRValue& value2 = context.value; - - unsigned int i=0; - for( ; i 0) - && ((expression.context[depth - 1].type == EXTYPE_LABEL) - || (expression.context[depth - 1].type == EXTYPE_LITERAL)) ) - { - // two labels/literals cannot follow each other - m_err = WR_ERR_bad_expression; - return 0; - } - - // it's a literal - expression.context[depth].type = EXTYPE_LITERAL; - if ( value.type == WR_COMPILER_LITERAL_STRING ) - { - expression.context[depth].literalString = *(WRstr*)value.p; - } - - ++depth; - continue; - } - - if ( token == ";" || token == ")" || token == "}" || token == "," || token == "]" || token == ":" ) - { - end = token[0]; - break; - } - - if ( token == "{" ) - { - if ( depth == 0 ) - { - if ( parseExpression(expression) != '}' ) - { - m_err = WR_ERR_bad_expression; - return 0; - } - - return ';'; - } - else - { - // it's an initializer - - if ( depth < 2 ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - if ( (expression.context[depth - 1].operation - && expression.context[ depth - 1 ].operation->opcode != O_Assign) ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - - if ( expression.context[depth - 2].operation - && !((expression.context[ depth - 2 ].operation->opcode == O_InitArray) - || (expression.context[ depth - 2 ].operation->opcode == O_Index)) - ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - - - if ( depth == 3 - && expression.context[0].type == EXTYPE_LABEL - && expression.context[1].type == EXTYPE_OPERATION - && expression.context[1].operation->opcode - && expression.context[1].operation->opcode == O_Index - && expression.context[1].bytecode.opcodes.size() > 0 ) - { - unsigned int i = expression.context[1].bytecode.opcodes.size() - 1; - unsigned int a = expression.context[1].bytecode.all.size() - 1; - WROpcode o = (WROpcode)(expression.context[1].bytecode.opcodes[ i ]); - - switch( o ) - { - case O_Index: - { - expression.context[1].bytecode.all[a] = O_InitArray; - break; - } - - case O_IndexLiteral16: - { - if (a > 1) - { - expression.context[1].bytecode.all[a - 2] = O_LiteralInt16; - pushOpcode(expression.context[1].bytecode, O_InitArray); - } - break; - } - - case O_IndexLiteral8: - { - if (a > 0) - { - expression.context[1].bytecode.all[a - 1] = O_LiteralInt8; - pushOpcode(expression.context[1].bytecode, O_InitArray); - } - break; - } - - default: break; - } - } - - - expression.context.remove( depth - 1, 1 ); // knock off the equate - depth--; - - WRstr t("@init"); - for( int o=0; c_operations[o].token; o++ ) - { - if ( t == c_operations[o].token ) - { - expression.context[depth].operation = c_operations + o; - expression.context[depth].type = EXTYPE_OPERATION; - break; - } - } - - - WRstr& token2 = expression.context[depth].token; - WRValue& value2 = expression.context[depth].value; - - uint16_t initializer = -1; - - for(;;) - { - if ( !getToken(expression.context[depth]) ) - { - m_err = WR_ERR_unexpected_EOF; - return 0; - } - - if ( !m_quoted && token2 == "}" ) - { - break; - } - - ++initializer; - - WRExpression nex( expression.bytecode.localSpace, expression.bytecode.isStructSpace ); - nex.context[0].token = token2; - nex.context[0].value = value2; - m_loadedToken = token2; - m_loadedValue = value2; - m_loadedQuoted = m_quoted; - - char end = parseExpression( nex ); - - unsigned char data[2]; - wr_pack16( initializer, data ); - - if ( end == '}' ) - { - appendBytecode( expression.context[depth].bytecode, nex.bytecode ); - pushOpcode( expression.context[depth].bytecode, O_AssignToArrayAndPop ); - pushData( expression.context[depth].bytecode, data, 2 ); - break; - } - else if ( end == ',' ) - { - appendBytecode( expression.context[depth].bytecode, nex.bytecode ); - pushOpcode( expression.context[depth].bytecode, O_AssignToArrayAndPop ); - pushData( expression.context[depth].bytecode, data, 2 ); - } - else if ( end == ':' ) - { - if ( nex.bytecode.all.size() == 0 ) - { - pushOpcode( expression.context[depth].bytecode, O_LiteralZero ); - } - else - { - appendBytecode( expression.context[depth].bytecode, nex.bytecode ); - } - - WRExpression nex2( expression.bytecode.localSpace, expression.bytecode.isStructSpace ); - nex2.context[0].token = token2; - nex2.context[0].value = value2; - char end = parseExpression( nex2 ); - - if ( nex2.bytecode.all.size() == 0 ) - { - pushOpcode( expression.context[depth].bytecode, O_LiteralZero ); - } - else - { - appendBytecode( expression.context[depth].bytecode, nex2.bytecode ); - } - - pushOpcode( expression.context[depth].bytecode, O_AssignToHashTableAndPop ); - - if ( end == '}' ) - { - break; - } - else if ( end != ',' ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - } - else - { - m_err = WR_ERR_unexpected_token; - return 0; - } - } - - ++initializer; - - if ( !getToken(expression.context[depth]) || token2 != ";" ) - { - m_err = WR_ERR_unexpected_EOF; - return 0; - } - - if ( depth == 2 ) - { - if ( expression.context[1].bytecode.all.size() == 3 - && expression.context[1].bytecode.all[0] == O_LiteralInt8 - && expression.context[1].bytecode.all[1] == 0 - && expression.context[1].bytecode.all[2] == O_InitArray ) - { - if (initializer < 255) - { - *expression.context[1].bytecode.all.p_str(1) = (unsigned char)initializer; - } - else - { - expression.context[1].bytecode.all.clear(); - expression.context[1].bytecode.opcodes.clear(); - pushOpcode( expression.context[1].bytecode, O_LiteralInt16 ); - unsigned char data[2]; - expression.context[1].bytecode.all.append(wr_pack16((uint16_t)initializer, data), 2); - pushOpcode( expression.context[1].bytecode, O_InitArray ); - } - } - - // pop the element off and reload the actual table that was created - pushOpcode(expression.context[1].bytecode, O_PopOne); - if (expression.context[0].global) - { - addGlobalSpaceLoad( expression.context[1].bytecode, - expression.context[0].token); - } - else - { - addLocalSpaceLoad( expression.context[1].bytecode, - expression.context[0].token); - } - } - - - m_loadedToken = token2; - m_loadedValue = value2; - m_loadedQuoted = m_quoted; - - ++depth; - - continue; - } - } - - if ( operatorFound(token, expression.context, depth) ) - { - ++depth; - continue; - } - - if ( !m_quoted && token == "new" ) - { - if ( (depth < 2) || - (expression.context[depth - 1].operation - && expression.context[ depth - 1 ].operation->opcode != O_Assign) ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - - if ( (depth < 2) - || (expression.context[depth - 2].operation - && expression.context[ depth - 2 ].operation->opcode != O_Index) ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - - WRstr& token2 = expression.context[depth].token; - WRValue& value2 = expression.context[depth].value; - - bool isGlobal; - WRstr prefix; - bool isLibConstant; - - if ( !getToken(expression.context[depth]) ) - { - m_err = WR_ERR_bad_expression; - return 0; - } - - if ( !isValidLabel(token2, isGlobal, prefix, isLibConstant) || isLibConstant ) - { - m_err = WR_ERR_bad_expression; - return 0; - } - - WRstr functionName = token2; - uint32_t hash = wr_hashStr( functionName ); - - if ( !getToken(expression.context[depth]) ) - { - m_err = WR_ERR_bad_expression; - return 0; - } - - if ( !m_quoted && token2 == ";" ) - { - if ( !parseCallFunction(expression, functionName, depth, false) ) - { - return 0; - } - - m_loadedToken = ";"; - m_loadedValue.p2 = INIT_AS_REF; - m_loadedQuoted = m_quoted; - } - else if ( !m_quoted && token2 == "(" ) - { - if ( !parseCallFunction(expression, functionName, depth, true) ) - { - return 0; - } - - if ( !getToken(expression.context[depth]) ) - { - m_err = WR_ERR_unexpected_EOF; - return 0; - } - - if ( token2 != "{" ) - { - m_loadedToken = token2; - m_loadedValue = value2; - m_loadedQuoted = m_quoted; - } - } - else if (!m_quoted && token2 == "{") - { - if ( !parseCallFunction(expression, functionName, depth, false) ) - { - return 0; - } - token2 = "{"; - } - else if ( !m_quoted && token2 == "[" ) - { - // must be "array" directive - if ( !getToken(expression.context[depth], "]") ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - - expression.context.remove( depth - 1, 1 ); // knock off the equate - depth--; - - WRstr& token3 = expression.context[depth].token; - WRValue& value3 = expression.context[depth].value; - - // we know we're about to create at least X new - // commands in a row, unless the function calls - // allocate memory (a distinct possibility) no GC will - // be required, so pause it by the number of - // allocations (TBD) - pushOpcode( expression.context[depth].bytecode, O_GC_Command ); - unsigned int gcOffset = expression.context[depth].bytecode.all.size(); - pushData( expression.context[depth].bytecode, "\0\0", 2 ); - - if ( !getToken(expression.context[depth], "{") ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - - uint16_t initializer = 0; - - if ( !m_quoted && token3 == "{" ) - { - for(;;) - { - if ( !getToken(expression.context[depth]) ) - { - m_err = WR_ERR_unexpected_EOF; - return 0; - } - - if ( !m_quoted && token3 == "}" ) - { - break; - } - - if ( !parseCallFunction(expression, functionName, depth, false) ) - { - return 0; - } - - if (!m_quoted && token3 != "{") - { - m_err = WR_ERR_unexpected_token; - return 0; - } - - if ( !pushObjectTable(expression.context[depth], expression.bytecode.localSpace, hash) ) - { - m_err = WR_ERR_bad_expression; - return 0; - } - - pushOpcode( expression.context[depth].bytecode, O_AssignToArrayAndPop ); - - unsigned char data[2]; - wr_pack16( initializer, data ); - ++initializer; - - pushData( expression.context[depth].bytecode, data, 2 ); - - if ( !getToken(expression.context[depth]) ) - { - m_err = WR_ERR_unexpected_EOF; - return 0; - } - - if ( !m_quoted && token3 == "," ) - { - continue; - } - else if ( token3 != "}" ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - else - { - break; - } - } - - if ( !getToken(expression.context[depth], ";") ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - - WRstr t("@init"); - for( int o=0; c_operations[o].token; o++ ) - { - if ( t == c_operations[o].token ) - { - expression.context[depth].type = EXTYPE_OPERATION; - expression.context[depth].operation = c_operations + o; - break; - } - } - - m_loadedToken = token3; - m_loadedValue = value3; - m_loadedQuoted = m_quoted; - } - else if ( token2 != ";" ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - - wr_pack16( initializer, expression.context[depth].bytecode.all.p_str(gcOffset) ); - - ++depth; - continue; - } - else if ( token2 != "{" ) - { - m_err = WR_ERR_bad_expression; - return 0; - } - - if ( !pushObjectTable(expression.context[depth], expression.bytecode.localSpace, hash) ) - { - m_err = WR_ERR_bad_expression; - return 0; - } - - ++depth; - continue; - } - - if ( !m_quoted && token == "(" ) - { - // might be cast, call or sub-expression - - if ( (depth > 0) && - (expression.context[depth - 1].type == EXTYPE_LABEL - || expression.context[depth - 1].type == EXTYPE_LIB_CONSTANT) ) - { - // always only a call - expression.context[depth - 1].type = EXTYPE_LABEL; - - --depth; - if ( !parseCallFunction(expression, expression.context[depth].token, depth, true) ) - { - return 0; - } - } - else if ( !getToken(expression.context[depth]) ) - { - m_err = WR_ERR_unexpected_EOF; - return 0; - } - else if ( token == "int" ) - { - if ( !getToken(expression.context[depth], ")") ) - { - m_err = WR_ERR_bad_expression; - return 0; - } - - WRstr t( "@i" ); - operatorFound( t, expression.context, depth ); - } - else if ( token == "float" ) - { - if ( !getToken(expression.context[depth], ")") ) - { - m_err = WR_ERR_bad_expression; - return 0; - } - - WRstr t( "@f" ); - operatorFound( t, expression.context, depth ); - } - else if (depth < 0) - { - m_err = WR_ERR_bad_expression; - return 0; - } - else - { - WRExpression nex( expression.bytecode.localSpace, expression.bytecode.isStructSpace ); - nex.context[0].token = token; - nex.context[0].value = value; - m_loadedToken = token; - m_loadedValue = value; - m_loadedQuoted = m_quoted; - if ( parseExpression(nex) != ')' ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - - - if ( depth > 0 && expression.context[depth - 1].operation - && expression.context[depth - 1].operation->opcode == O_Remove ) - { - --depth; - WRstr t( "._remove" ); - expression.context[depth].bytecode = nex.bytecode; - operatorFound( t, expression.context, depth ); - } - else if ( depth > 0 && expression.context[depth - 1].operation - && expression.context[depth - 1].operation->opcode == O_HashEntryExists ) - { - --depth; - WRstr t( "._exists" ); - expression.context[depth].bytecode = nex.bytecode; - operatorFound( t, expression.context, depth ); - } - else - { - expression.context[depth].type = EXTYPE_BYTECODE_RESULT; - expression.context[depth].bytecode = nex.bytecode; - } - } - - ++depth; - continue; - } - - if ( !m_quoted && token == "[" ) - { - WRExpression nex( expression.bytecode.localSpace, expression.bytecode.isStructSpace ); - nex.context[0].token = token; - nex.context[0].value = value; - - if ( parseExpression(nex) != ']' ) - { - m_err = WR_ERR_unexpected_EOF; - return 0; - } - - WRstr t( "@[]" ); - - if ( nex.bytecode.all.size() == 0 ) - { - operatorFound( t, expression.context, depth ); - expression.context[depth].bytecode.all.shave(1); - expression.context[depth].bytecode.opcodes.shave(1); - pushOpcode( expression.context[depth].bytecode, O_LiteralInt8 ); - unsigned char c = 0; - pushData( expression.context[depth].bytecode, &c, 1 ); - pushOpcode( expression.context[depth].bytecode, O_InitArray ); - } - else - { - expression.context[depth].bytecode = nex.bytecode; - operatorFound( t, expression.context, depth ); - } - - ++depth; - continue; - } - - bool isGlobal; - WRstr prefix; - bool isLibConstant; - - if ( isValidLabel(token, isGlobal, prefix, isLibConstant) ) - { - if ( (depth > 0) - && ((expression.context[depth - 1].type == EXTYPE_LABEL) - || (expression.context[depth - 1].type == EXTYPE_LITERAL) - || (expression.context[depth - 1].type == EXTYPE_LIB_CONSTANT)) ) - { - // two labels/literals cannot follow each other - m_err = WR_ERR_bad_expression; - return 0; - } - - WRstr label = token; - if ( depth == 0 && !m_parsingFor ) - { - if ( !getToken(expression.context[depth]) ) - { - m_err = WR_ERR_unexpected_EOF; - return 0; - } - - if ( !m_quoted && token == ":" ) - { - uint32_t hash = wr_hashStr( label ); - for( unsigned int i=0; iopcode == O_Index - && expression.context[1].bytecode.opcodes.size() > 0 ) - { - unsigned int i = expression.context[1].bytecode.opcodes.size() - 1; - unsigned int a = expression.context[1].bytecode.all.size() - 1; - WROpcode o = (WROpcode)(expression.context[1].bytecode.opcodes[ i ]); - - switch( o ) - { - case O_Index: - { - expression.context[1].bytecode.all[a] = O_InitArray; - break; - } - - case O_IndexLiteral16: - { - if (a > 1) - { - expression.context[1].bytecode.all[a - 2] = O_LiteralInt16; - pushOpcode(expression.context[1].bytecode, O_InitArray); - } - break; - } - - case O_IndexLiteral8: - { - if (a > 0) - { - expression.context[1].bytecode.all[a - 1] = O_LiteralInt8; - pushOpcode(expression.context[1].bytecode, O_InitArray); - } - break; - } - - default: break; - } - } - - resolveExpression( expression ); - - return end; -} - -//------------------------------------------------------------------------------ -bool WRCompilationContext::parseUnit( bool isStruct, int parentUnitIndex ) -{ - int previousIndex = m_unitTop; - m_unitTop = m_units.count(); - - if ( parentUnitIndex && isStruct ) - { - m_err = WR_ERR_struct_in_struct; - return false; - } - - bool isGlobal; - - WRExpressionContext ex; - WRstr& token = ex.token; - WRstr prefix; - bool isLibConstant; - - // get the function name - if ( !getToken(ex) - || !isValidLabel(token, isGlobal, prefix, isLibConstant) - || isGlobal - || isLibConstant ) - { - m_err = WR_ERR_bad_label; - return false; - } - - m_units[m_unitTop].hash = wr_hash( token, token.size() ); - m_units[m_unitTop].bytecode.isStructSpace = isStruct; - - m_units[m_unitTop].parentUnitIndex = parentUnitIndex; // if non-zero, must be called from a new'ed structure! - - // get the function name - if ( getToken(ex, "(") ) - { - for(;;) - { - if ( !getToken(ex) ) - { - m_err = WR_ERR_unexpected_EOF; - return false; - } - - if ( !m_quoted && token == ")" ) - { - break; - } - - if ( !isValidLabel(token, isGlobal, prefix, isLibConstant) || isGlobal || isLibConstant ) - { - m_err = WR_ERR_bad_label; - return false; - } - - ++m_units[m_unitTop].arguments; - - // register the argument on the hash stack - addLocalSpaceLoad( m_units[m_unitTop].bytecode, token, true ); - - if ( !getToken(ex) ) - { - m_err = WR_ERR_unexpected_EOF; - return false; - } - - if ( !m_quoted && token == ")" ) - { - break; - } - - if ( token != "," ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - } - - if ( !getToken(ex, "{") ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - } - else if ( token != "{" ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - - bool returnCalled; - parseStatement( m_unitTop, '}', returnCalled, O_Return ); - - if ( !returnCalled ) - { - pushOpcode( m_units[m_unitTop].bytecode, O_LiteralZero ); - pushOpcode( m_units[m_unitTop].bytecode, O_Return ); - } - - m_unitTop = previousIndex; - - return true; -} - -//------------------------------------------------------------------------------ -bool WRCompilationContext::parseWhile( bool& returnCalled, WROpcode opcodeToReturn ) -{ - WRExpressionContext ex; - WRstr& token = ex.token; - WRValue& value = ex.value; - - if ( !getToken(ex, "(") ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - - if ( !getToken(ex) ) - { - m_err = WR_ERR_unexpected_EOF; - return false; - } - - WRExpression nex( m_units[m_unitTop].bytecode.localSpace, m_units[m_unitTop].bytecode.isStructSpace ); - nex.context[0].token = token; - nex.context[0].value = value; - m_loadedToken = token; - m_loadedValue = value; - m_loadedQuoted = m_quoted; - - if ( parseExpression(nex) != ')' ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - - *m_continueTargets.push() = addRelativeJumpTarget( m_units[m_unitTop].bytecode ); - setRelativeJumpTarget(m_units[m_unitTop].bytecode, *m_continueTargets.tail() ); - - appendBytecode( m_units[m_unitTop].bytecode, nex.bytecode ); - - *m_breakTargets.push() = addRelativeJumpTarget( m_units[m_unitTop].bytecode ); - - addRelativeJumpSource( m_units[m_unitTop].bytecode, O_BZ, *m_breakTargets.tail() ); - - if ( !parseStatement(m_unitTop, ';', returnCalled, opcodeToReturn) ) - { - return false; - } - - addRelativeJumpSource( m_units[m_unitTop].bytecode, O_RelativeJump, *m_continueTargets.tail() ); - setRelativeJumpTarget( m_units[m_unitTop].bytecode, *m_breakTargets.tail() ); - - m_continueTargets.pop(); - m_breakTargets.pop(); - - resolveRelativeJumps( m_units[m_unitTop].bytecode ); - - return true; -} - -//------------------------------------------------------------------------------ -bool WRCompilationContext::parseDoWhile( bool& returnCalled, WROpcode opcodeToReturn ) -{ - *m_continueTargets.push() = addRelativeJumpTarget( m_units[m_unitTop].bytecode ); - *m_breakTargets.push() = addRelativeJumpTarget( m_units[m_unitTop].bytecode ); - - int jumpToTop = addRelativeJumpTarget( m_units[m_unitTop].bytecode ); - setRelativeJumpTarget( m_units[m_unitTop].bytecode, jumpToTop ); - - if ( !parseStatement(m_unitTop, ';', returnCalled, opcodeToReturn) ) - { - return false; - } - - setRelativeJumpTarget(m_units[m_unitTop].bytecode, *m_continueTargets.tail() ); - - WRExpressionContext ex; - WRstr& token = ex.token; - WRValue& value = ex.value; - - if ( !getToken(ex) ) - { - m_err = WR_ERR_unexpected_EOF; - return false; - } - - if ( token != "while" ) - { - m_err = WR_ERR_expected_while; - return false; - } - - if ( !getToken(ex, "(") ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - - WRExpression nex( m_units[m_unitTop].bytecode.localSpace, m_units[m_unitTop].bytecode.isStructSpace ); - nex.context[0].token = token; - nex.context[0].value = value; - //m_loadedToken = token; - //m_loadedValue = value; - - if ( parseExpression(nex) != ')' ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - - if (!getToken(ex, ";")) - { - m_err = WR_ERR_unexpected_token; - return false; - } - setRelativeJumpTarget(m_units[m_unitTop].bytecode, *m_continueTargets.tail() ); - - appendBytecode( m_units[m_unitTop].bytecode, nex.bytecode ); - - addRelativeJumpSource( m_units[m_unitTop].bytecode, O_BZ, *m_breakTargets.tail() ); - addRelativeJumpSource( m_units[m_unitTop].bytecode, O_RelativeJump, jumpToTop ); - - setRelativeJumpTarget( m_units[m_unitTop].bytecode, *m_breakTargets.tail() ); - - m_continueTargets.pop(); - m_breakTargets.pop(); - - resolveRelativeJumps( m_units[m_unitTop].bytecode ); - - return true; -} - -//------------------------------------------------------------------------------ -bool WRCompilationContext::parseForLoop( bool& returnCalled, WROpcode opcodeToReturn ) -{ - WRExpressionContext ex; - WRstr& token = ex.token; - WRValue& value = ex.value; - - if ( !getToken(ex, "(") ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - - // create the continue and break reference points - *m_continueTargets.push() = addRelativeJumpTarget( m_units[m_unitTop].bytecode ); - *m_breakTargets.push() = addRelativeJumpTarget( m_units[m_unitTop].bytecode ); - - -/* - push iterator -B: - get next or jump A - . - . - . - goto B -A: -*/ - - -/* - - -[setup] -<- condition point -[condition] --> false jump break -[code] -<- continue point -[post code] --> always jump condition -<- break point - -*/ - - bool foreachPossible = true; - bool foreachKV = false; - bool foreachV = false; - int foreachLoadI = 0; - unsigned char foreachLoad[4]; - unsigned char g; - - m_parsingFor = true; - - // [setup] - for(;;) - { - if ( !getToken(ex) ) - { - m_err = WR_ERR_unexpected_EOF; - return false; - } - - if ( !m_quoted && token == ";" ) - { - break; - } - - WRExpression nex( m_units[m_unitTop].bytecode.localSpace, m_units[m_unitTop].bytecode.isStructSpace ); - nex.context[0].token = token; - nex.context[0].value = value; - m_loadedToken = token; - m_loadedValue = value; - m_loadedQuoted = m_quoted; - - char end = parseExpression( nex ); - - if ( foreachPossible ) - { - if ( foreachLoadI < 3 ) - { - if (nex.bytecode.opcodes.size() == 1 - && nex.bytecode.all.size() == 2 - && ((nex.bytecode.all[0] == O_LoadFromLocal) || (nex.bytecode.all[0] == O_LoadFromGlobal)) ) - { - foreachLoad[foreachLoadI++] = nex.bytecode.all[0]; - foreachLoad[foreachLoadI++] = nex.bytecode.all[1]; - } - else if ( nex.bytecode.opcodes.size() == 2 - && nex.bytecode.all.size() == 5 - && nex.bytecode.all[0] == O_DebugInfo - && ((nex.bytecode.all[3] == O_LoadFromLocal) || (nex.bytecode.all[3] == O_LoadFromGlobal)) ) - { - foreachLoad[foreachLoadI++] = nex.bytecode.all[3]; - foreachLoad[foreachLoadI++] = nex.bytecode.all[4]; - } - else - { - foreachPossible = false; - } - } - else - { - foreachPossible = false; - } - } - - pushOpcode( nex.bytecode, O_PopOne ); - - appendBytecode( m_units[m_unitTop].bytecode, nex.bytecode ); - - if ( end == ';' ) - { - foreachPossible = false; - break; - } - else if ( end == ':' ) - { - if ( foreachPossible ) - { - WRExpression nex( m_units[m_unitTop].bytecode.localSpace, m_units[m_unitTop].bytecode.isStructSpace ); - nex.context[0].token = token; - nex.context[0].value = value; - end = parseExpression( nex ); - if ( end == ')' - && nex.bytecode.opcodes.size() == 1 - && nex.bytecode.all.size() == 2 ) - { - - WRstr T; - T.format( "foreach:%d", m_foreachHash++ ); - g = (unsigned char)(addGlobalSpaceLoad( m_units[0].bytecode, T, true )); - - if ( nex.bytecode.opcodes[0] == O_LoadFromLocal ) - { - m_units[m_unitTop].bytecode.all += O_LPushIterator; - } - else - { - m_units[m_unitTop].bytecode.all += O_GPushIterator; - } - - m_units[m_unitTop].bytecode.all += nex.bytecode.all[1]; - pushData( m_units[m_unitTop].bytecode, &g, 1 ); - - if ( foreachLoadI == 4 ) - { - foreachKV = true; - } - else if ( foreachLoadI == 2 ) - { - foreachV = true; - } - else - { - m_err = WR_ERR_unexpected_token; - return 0; - } - - break; - } - else - { - m_err = WR_ERR_unexpected_token; - return 0; - } - } - else - { - m_err = WR_ERR_unexpected_token; - return 0; - } - } - else if ( end != ',' ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - } - - // <- condition point - int conditionPoint = addRelativeJumpTarget( m_units[m_unitTop].bytecode ); - - setRelativeJumpTarget( m_units[m_unitTop].bytecode, conditionPoint ); - - if ( foreachV || foreachKV ) - { - if ( foreachV ) - { - unsigned char load[2] = { foreachLoad[1], g }; - - if ( foreachLoad[0] == O_LoadFromLocal ) - { - addRelativeJumpSourceEx( m_units[m_unitTop].bytecode, O_LNextValueOrJump, *m_breakTargets.tail(), load, 2 ); - } - else - { - addRelativeJumpSourceEx( m_units[m_unitTop].bytecode, O_GNextValueOrJump, *m_breakTargets.tail(), load, 2 ); - } - } - else - { - unsigned char load[3] = { foreachLoad[1], foreachLoad[3], g }; - - if ( foreachLoad[0] == O_LoadFromLocal ) - { - if ( foreachLoad[2] == O_LoadFromLocal ) - { - addRelativeJumpSourceEx( m_units[m_unitTop].bytecode, O_LLNextKeyValueOrJump, *m_breakTargets.tail(), load, 3 ); - } - else - { - addRelativeJumpSourceEx( m_units[m_unitTop].bytecode, O_LGNextKeyValueOrJump, *m_breakTargets.tail(), load, 3 ); - } - } - else - { - if ( foreachLoad[2] == O_LoadFromLocal ) - { - addRelativeJumpSourceEx( m_units[m_unitTop].bytecode, O_GLNextKeyValueOrJump, *m_breakTargets.tail(), load, 3 ); - } - else - { - addRelativeJumpSourceEx( m_units[m_unitTop].bytecode, O_GGNextKeyValueOrJump, *m_breakTargets.tail(), load, 3 ); - } - } - } - - m_parsingFor = false; - - // [ code ] - if ( !parseStatement(m_unitTop, ';', returnCalled, opcodeToReturn) ) - { - return false; - } - } - else - { - if ( !getToken(ex) ) - { - m_err = WR_ERR_unexpected_EOF; - return false; - } - - // [ condition ] - if ( token != ";" ) - { - WRExpression nex( m_units[m_unitTop].bytecode.localSpace, m_units[m_unitTop].bytecode.isStructSpace ); - nex.context[0].token = token; - nex.context[0].value = value; - m_loadedToken = token; - m_loadedValue = value; - m_loadedQuoted = m_quoted; - - if ( parseExpression( nex ) != ';' ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - - appendBytecode( m_units[m_unitTop].bytecode, nex.bytecode ); - - // -> false jump break - addRelativeJumpSource( m_units[m_unitTop].bytecode, O_BZ, *m_breakTargets.tail() ); - } - - - WRExpression post( m_units[m_unitTop].bytecode.localSpace, m_units[m_unitTop].bytecode.isStructSpace ); - - // [ post code ] - for(;;) - { - if ( !getToken(ex) ) - { - m_err = WR_ERR_unexpected_EOF; - return false; - } - - if ( !m_quoted && token == ")" ) - { - break; - } - - WRExpression nex( m_units[m_unitTop].bytecode.localSpace, m_units[m_unitTop].bytecode.isStructSpace ); - nex.context[0].token = token; - nex.context[0].value = value; - m_loadedToken = token; - m_loadedValue = value; - m_loadedQuoted = m_quoted; - - char end = parseExpression( nex ); - pushOpcode( nex.bytecode, O_PopOne ); - - appendBytecode( post.bytecode, nex.bytecode ); - - if ( end == ')' ) - { - break; - } - else if ( end != ',' ) - { - m_err = WR_ERR_unexpected_token; - return 0; - } - } - - m_parsingFor = false; - - // [ code ] - if ( !parseStatement(m_unitTop, ';', returnCalled, opcodeToReturn) ) - { - return false; - } - - // <- continue point - setRelativeJumpTarget( m_units[m_unitTop].bytecode, *m_continueTargets.tail() ); - - // [post code] - appendBytecode( m_units[m_unitTop].bytecode, post.bytecode ); - } - - addRelativeJumpSource( m_units[m_unitTop].bytecode, O_RelativeJump, conditionPoint ); - setRelativeJumpTarget( m_units[m_unitTop].bytecode, *m_breakTargets.tail() ); - - m_continueTargets.pop(); - m_breakTargets.pop(); - - resolveRelativeJumps( m_units[m_unitTop].bytecode ); - - return true; -} - -//------------------------------------------------------------------------------ -bool WRCompilationContext::lookupConstantValue( WRstr& prefix, WRValue* value ) -{ - for( unsigned int v=0; v 16-bit default location - 16 bit case offset - 16 bit case offset - 16 bit case offset - cases... - [cases] - break target: - - -continue target: -switch ins -16-bit mod -16-bit default location -32 hash case mod 0 : 16 bit case offset -32 hash case mod 1 : 16 bit case offset -32 hash case mod 2 : 16 bit case offset -... -[cases] -break target: - -*/ - -//------------------------------------------------------------------------------ -struct WRSwitchCase -{ - uint32_t hash; // hash to get this case - bool occupied; // null hash is legal and common, must mark occupation of a node with extra flag - bool defaultCase; - int16_t jumpOffset; // where to go to get to this case -}; - -//------------------------------------------------------------------------------ -bool WRCompilationContext::parseSwitch( bool& returnCalled, WROpcode opcodeToReturn ) -{ - WRExpressionContext ex; - WRstr& token = ex.token; - WRValue& value = ex.value; - - if ( !getToken(ex, "(") ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - - WRExpression selectionCriteria( m_units[m_unitTop].bytecode.localSpace, m_units[m_unitTop].bytecode.isStructSpace ); - selectionCriteria.context[0].token = token; - selectionCriteria.context[0].value = value; - - if ( parseExpression(selectionCriteria) != ')' ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - - appendBytecode( m_units[m_unitTop].bytecode, selectionCriteria.bytecode ); - - - if ( !getToken(ex, "{") ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - - WRarray cases; - int16_t defaultOffset = -1; - WRSwitchCase* swCase = 0; // current case - - int defaultCaseJumpTarget = addRelativeJumpTarget( m_units[m_unitTop].bytecode ); - - *m_breakTargets.push() = addRelativeJumpTarget( m_units[m_unitTop].bytecode ); - - int selectionLogicPoint = addRelativeJumpTarget( m_units[m_unitTop].bytecode ); - addRelativeJumpSource( m_units[m_unitTop].bytecode, O_RelativeJump, selectionLogicPoint ); - - unsigned int startingBytecodeMarker = m_units[m_unitTop].bytecode.all.size(); - - for(;;) - { - if ( !getToken(ex) ) - { - m_err = WR_ERR_unexpected_EOF; - return false; - } - - if( !m_quoted && token == "}" ) - { - break; - } - - if ( !m_quoted && token == "case" ) - { - swCase = &cases.append(); - swCase->jumpOffset = m_units[m_unitTop].bytecode.all.size(); - swCase->hash = getSingleValueHash(":"); - if ( m_err ) - { - return false; - } - - swCase->occupied = true; - swCase->defaultCase = false; - - if ( cases.count() > 1 ) - { - for( unsigned int h = 0; h < cases.count() - 1 ; ++h ) - { - if ( cases[h].occupied && !cases[h].defaultCase && (swCase->hash == cases[h].hash) ) - { - m_err = WR_ERR_switch_duplicate_case; - return false; - } - } - } - } - else if ( !m_quoted && token == "default" ) - { - if ( defaultOffset != -1 ) - { - m_err = WR_ERR_switch_duplicate_case; - return false; - } - - if ( !getToken(ex, ":") ) - { - m_err = WR_ERR_unexpected_token; - return false; - } - - defaultOffset = m_units[m_unitTop].bytecode.all.size(); - setRelativeJumpTarget( m_units[m_unitTop].bytecode, defaultCaseJumpTarget ); - } - else - { - if ( swCase == 0 && defaultOffset == -1 ) - { - m_err = WR_ERR_switch_case_or_default_expected; - return false; - } - - m_loadedToken = token; - m_loadedValue = value; - m_loadedQuoted = m_quoted; - - if ( !parseStatement(m_unitTop, ';', returnCalled, opcodeToReturn) ) - { - return false; - } - } - } - - if ( startingBytecodeMarker == m_units[m_unitTop].bytecode.all.size() ) - { - // no code was added so this is one big null operation, go - // ahead and null it - m_units[m_unitTop].bytecode.all.shave(3); - m_units[m_unitTop].bytecode.opcodes.clear(); - - pushOpcode(m_units[m_unitTop].bytecode, O_PopOne); // pop off the selection criteria - - m_units[m_unitTop].bytecode.jumpOffsetTargets.pop(); - m_breakTargets.pop(); - return true; - } - - // make sure the last instruction is a break (jump) so the - // selection logic is skipped at the end of the last case/default - if ( !m_units[m_unitTop].bytecode.opcodes.size() - || (m_units[m_unitTop].bytecode.opcodes.size() && m_units[m_unitTop].bytecode.opcodes[m_units[m_unitTop].bytecode.opcodes.size() - 1] != O_RelativeJump) ) - { - addRelativeJumpSource( m_units[m_unitTop].bytecode, O_RelativeJump, *m_breakTargets.tail() ); - } - - // selection logic jumps HERE - setRelativeJumpTarget( m_units[m_unitTop].bytecode, selectionLogicPoint ); - - // find the highest hash value, and size an array to that - unsigned int size = 0; - for( unsigned int d=0; d size ) - { - size = cases[d].hash; - } - - if ( size >= 254 ) - { - break; - } - } - - // first try the easy way - - ++size; - - WRSwitchCase* table = 0; - unsigned char packbuf[4]; - - if ( size < 254 ) // cases are labeled 0-254, just use a linear jump table - { - pushOpcode( m_units[m_unitTop].bytecode, O_SwitchLinear ); - - packbuf[0] = size; - pushData( m_units[m_unitTop].bytecode, packbuf, 1 ); // size - - int currentPos = m_units[m_unitTop].bytecode.all.size(); - - if ( defaultOffset == -1 ) - { - defaultOffset = size*2 + 2; - } - else - { - defaultOffset -= currentPos; - } - - table = new WRSwitchCase[size]; - memset( table, 0, size*sizeof(WRSwitchCase) ); - - for( unsigned int i = 0; i= cases.count() ) - { - break; - } - else - { - delete[] table; - table = 0; - } - } - - if ( mod >= 0x7FFE ) - { - m_err = WR_ERR_switch_construction_error; - return false; - } - - if ( defaultOffset == -1 ) - { - defaultOffset = mod*6 + 4; - } - else - { - defaultOffset -= currentPos; - } - - pushData( m_units[m_unitTop].bytecode, wr_pack16(mod, packbuf), 2 ); // mod value - pushData( m_units[m_unitTop].bytecode, wr_pack16(defaultOffset, packbuf), 2 ); // default offset - - for( uint16_t m = 0; m offsets; - for( unsigned char i=unit.arguments; i 0 ) // for the non-zero unit fill location into the jump table - { - int16_t offset = code.size(); - wr_pack16( offset, code.p_str(m_units[u].offsetInBytecode) ); - } - - // fill in relative jumps for the gotos - for( unsigned int g=0; g -129) ) - { - *m_units[u].bytecode.all.p_str( m_units[u].bytecode.gotoSource[g].offset ) = (unsigned char)O_RelativeJump8; - *m_units[u].bytecode.all.p_str( m_units[u].bytecode.gotoSource[g].offset + 1 ) = diff; - } - else - { - *m_units[u].bytecode.all.p_str( m_units[u].bytecode.gotoSource[g].offset ) = (unsigned char)O_RelativeJump; - wr_pack16( diff, m_units[u].bytecode.all.p_str(m_units[u].bytecode.gotoSource[g].offset + 1) ); - } - - break; - } - } - - if ( j >= m_units[u].bytecode.jumpOffsetTargets.count() ) - { - m_err = WR_ERR_goto_target_not_found; - return; - } - } - - int base = code.size(); - -// wr_asciiDump( m_units[u].bytecode.all, m_units[u].bytecode.all.size(), str ); -// printf( "unit %d\n%d:\n%s\n", u, code.size(), str.c_str() ); - - code.append( m_units[u].bytecode.all, m_units[u].bytecode.all.size() ); - -// wr_asciiDump(code.p_str(), code.size(), str); -// printf("header:\n%d:\n%s\n", code.size(), str.c_str()); - - // load new's - for( unsigned int f=0; f= m_units.count() ) - { - if ( code[index+5] == O_PopOne ) - { - code[index] = O_CallFunctionByHashAndPop; - } - else - { - code[index] = O_CallFunctionByHash; - } - - wr_pack32( N.hash, code.p_str(index+2) ); - } - } - } - } - - -// [h][#globals][bytes] / [global data|global data crc] / [CRC] -// [h][#globals][bytes] |[[debug options + crc]|/ [global data|global data crc] / [CRC] - -#ifdef WRENCH_INCLUDE_DEBUG_CODE - if ( m_addDebugLineNumbers || m_embedSourceCode ) - { - WRstr symbolBlock; - if ( m_addDebugLineNumbers || m_embedSourceCode ) - { - symbolBlock.append( (char *)wr_pack16(globals, data), 2 ); // globals - symbolBlock.append( (char *)wr_pack16(units - 1, data), 2 ); // functions - - data[0] = 0; - for( unsigned int g=0; gxtype) && IS_EXARRAY_TYPE(s->r->xtype) ) - { - // we don't mark this type, but we might mark it's target - mark( s->r ); - return; - } - - if ( !IS_EXARRAY_TYPE(s->xtype) || s->va->m_skipGC || (s->va->m_size & 0x40000000) ) - { - return; - } - - assert( !IS_RAW_ARRAY(s->xtype) ); - - WRGCObject* sva = s->va; - - if ( sva->m_type == SV_VALUE ) - { - WRValue* top = sva->m_Vdata + sva->m_size; - - for( WRValue* V = sva->m_Vdata; Vm_type == SV_HASH_TABLE ) - { - for( uint32_t i=0; im_mod; ++i ) - { - if ( sva->m_hashTable[i] ) - { - uint32_t item = i<<1; - mark( sva->m_Vdata + item++ ); - mark( sva->m_Vdata + item ); - } - } - } - - sva->m_size |= 0x40000000; -} - -//------------------------------------------------------------------------------ -void WRContext::gc( WRValue* stackTop ) -{ - if ( !svAllocated ) - { - return; - } - - if ( gcPauseCount ) - { - --gcPauseCount; - return; - } - - // mark stack - for( WRValue* s=w->stack; sm_size & 0x40000000 ) - { - current->m_size &= ~0x40000000; - prev = current; - current = current->m_next; - } - // otherwise free it as unreferenced - else - { - current->clear(); - - if ( prev == 0 ) - { - svAllocated = current->m_next; - free( current ); - current = svAllocated; - } - else - { - prev->m_next = current->m_next; - free( current ); - current = prev->m_next; - } - } - } -} - -//------------------------------------------------------------------------------ -WRGCObject* WRContext::getSVA( int size, WRGCObjectType type, bool init ) -{ - WRGCObject* ret = (WRGCObject*)malloc( sizeof(WRGCObject) ); - ret->init( size, type ); - if ( init ) - { - memset( (char*)ret->m_Vdata, 0, sizeof(WRValue) * size); - } - - if ( type == SV_CHAR ) - { - ret->m_creatorContext = this; - } - - ret->m_next = svAllocated; - svAllocated = ret; - - return ret; -} - -//------------------------------------------------------------------------------ -inline bool wr_getNextValue( WRValue* iterator, WRValue* value, WRValue* key ) -{ - if ( !IS_ITERATOR(iterator->xtype) ) - { - return false; - } - - uint32_t element = DECODE_ARRAY_ELEMENT_FROM_P2( iterator->p2 ); - - if ( iterator->va->m_type == SV_HASH_TABLE ) - { - for( ; elementva->m_mod; ++element ) - { - if ( iterator->va->m_hashTable[element] ) - { - iterator->p2 = INIT_AS_ITERATOR | ENCODE_ARRAY_ELEMENT_TO_P2(element+1); - - element <<= 1; - - value->p2 = INIT_AS_REF; - value->r = iterator->va->m_Vdata + element; - if ( key ) - { - key->p2 = INIT_AS_REF; - key->r = iterator->va->m_Vdata + element + 1; - } - - return true; - } - } - - return false; - } - else - { - if ( element >= iterator->va->m_size ) - { - return false; - } - - if ( key ) - { - key->p2 = INIT_AS_INT; - key->i = element; - } - - value->p2 = INIT_AS_REF; - value->r = iterator->va->m_Vdata + element; - - iterator->p2 = INIT_AS_ITERATOR | ENCODE_ARRAY_ELEMENT_TO_P2(++element); - } - - return true; -} - -//#define D_OPCODE -#ifdef D_OPCODE - #define PER_INSTRUCTION printf( "S[%d] %d:%s\n", (int)(stackTop - w->stack), (int)READ_8_FROM_PC(pc), c_opcodeName[READ_8_FROM_PC(pc)]); -#else - #define PER_INSTRUCTION -#endif - -#ifdef WRENCH_JUMPTABLE_INTERPRETER - #define CONTINUE { PER_INSTRUCTION; goto *opcodeJumptable[READ_8_FROM_PC(pc++)]; } - #define CASE(LABEL) LABEL -#else - #define CONTINUE { PER_INSTRUCTION; continue; } - #define CASE(LABEL) case O_##LABEL -#endif - -#ifdef WRENCH_COMPACT - -static float divisionF( float a, float b ) { return a / b; } -static float addF( float a, float b ) { return a + b; } -static float subtractionF( float a, float b ) { return a - b; } -static float multiplicationF( float a, float b ) { return a * b; } - -static int divisionI( int a, int b ) { return a / b; } -int wr_addI( int a, int b ) { return a + b; } -static int subtractionI( int a, int b ) { return a - b; } -static int multiplicationI( int a, int b ) { return a * b; } - -static int rightShiftI( int a, int b ) { return a >> b; } -static int leftShiftI( int a, int b ) { return a << b; } -static int modI( int a, int b ) { return a % b; } -static int orI( int a, int b ) { return a | b; } -static int xorI( int a, int b ) { return a ^ b; } -static int andI( int a, int b ) { return a & b; } - -static bool CompareGTI( int a, int b ) { return a > b; } -static bool CompareLTI( int a, int b ) { return a < b; } -static bool CompareANDI( int a, int b ) { return a && b; } -static bool CompareORI( int a, int b ) { return a || b; } - -static bool CompareLTF( float a, float b ) { return a < b; } -static bool CompareGTF( float a, float b ) { return a > b; } - -bool CompareEQI( int a, int b ) { return a == b; } -bool CompareEQF( float a, float b ) { return a == b; } - -static bool CompareBlankF( float a, float b ) { return false; } -static float blankF( float a, float b ) { return 0; } - -int32_t READ_32_FROM_PC_func( const unsigned char* P ) -{ - return ( (((int32_t)*(P)) ) - | (((int32_t)*((P)+1)) << 8) - | (((int32_t)*((P)+2)) << 16) - | (((int32_t)*((P)+3)) << 24) ); -} - -int16_t READ_16_FROM_PC_func( const unsigned char* P ) -{ - return ( ((int16_t)*(P)) ) - | ((int16_t)*(P+1) << 8 ); -} - -#endif - -//------------------------------------------------------------------------------ -WRValue* wr_callFunction( WRContext* context, WRFunction* function, const WRValue* argv, const int argn ) -{ -#ifdef WRENCH_JUMPTABLE_INTERPRETER - const void* opcodeJumptable[] = - { - &&RegisterFunction, - - &&LiteralInt32, - &&LiteralZero, - &&LiteralFloat, - &&LiteralString, - - &&CallFunctionByHash, - &&CallFunctionByHashAndPop, - &&CallFunctionByIndex, - &&PushIndexFunctionReturnValue, - - &&CallLibFunction, - &&CallLibFunctionAndPop, - - &&NewObjectTable, - &&AssignToObjectTableByOffset, - - &&AssignToHashTableAndPop, - &&Remove, - &&HashEntryExists, - - &&PopOne, - &&ReturnZero, - &&Return, - &&Stop, - - &&Dereference, - &&Index, - &&IndexSkipLoad, - &&CountOf, - &&HashOf, - - &&StackIndexHash, - &&GlobalIndexHash, - &&LocalIndexHash, - - &&StackSwap, - &&SwapTwoToTop, - - &&LoadFromLocal, - &&LoadFromGlobal, - - &&LLValues, - &&LGValues, - &&GLValues, - &&GGValues, - - &&BinaryRightShiftSkipLoad, - &&BinaryLeftShiftSkipLoad, - &&BinaryAndSkipLoad, - &&BinaryOrSkipLoad, - &&BinaryXORSkipLoad, - &&BinaryModSkipLoad, - - &&BinaryMultiplication, - &&BinarySubtraction, - &&BinaryDivision, - &&BinaryRightShift, - &&BinaryLeftShift, - &&BinaryMod, - &&BinaryOr, - &&BinaryXOR, - &&BinaryAnd, - &&BinaryAddition, - - &&BitwiseNOT, - - &&RelativeJump, - &&RelativeJump8, - - &&BZ, - &&BZ8, - - &&LogicalAnd, - &&LogicalOr, - &&CompareLE, - &&CompareGE, - &&CompareGT, - &&CompareLT, - &&CompareEQ, - &&CompareNE, - - &&GGCompareGT, - &&GGCompareGE, - &&GGCompareLT, - &&GGCompareLE, - &&GGCompareEQ, - &&GGCompareNE, - - &&LLCompareGT, - &&LLCompareGE, - &&LLCompareLT, - &&LLCompareLE, - &&LLCompareEQ, - &&LLCompareNE, - - &&GSCompareEQ, - &&LSCompareEQ, - &&GSCompareNE, - &&LSCompareNE, - &&GSCompareGE, - &&LSCompareGE, - &&GSCompareLE, - &&LSCompareLE, - &&GSCompareGT, - &&LSCompareGT, - &&GSCompareLT, - &&LSCompareLT, - - &&GSCompareEQBZ, - &&LSCompareEQBZ, - &&GSCompareNEBZ, - &&LSCompareNEBZ, - &&GSCompareGEBZ, - &&LSCompareGEBZ, - &&GSCompareLEBZ, - &&LSCompareLEBZ, - &&GSCompareGTBZ, - &&LSCompareGTBZ, - &&GSCompareLTBZ, - &&LSCompareLTBZ, - - &&GSCompareEQBZ8, - &&LSCompareEQBZ8, - &&GSCompareNEBZ8, - &&LSCompareNEBZ8, - &&GSCompareGEBZ8, - &&LSCompareGEBZ8, - &&GSCompareLEBZ8, - &&LSCompareLEBZ8, - &&GSCompareGTBZ8, - &&LSCompareGTBZ8, - &&GSCompareLTBZ8, - &&LSCompareLTBZ8, - - &&LLCompareLTBZ, - &&LLCompareLEBZ, - &&LLCompareGTBZ, - &&LLCompareGEBZ, - &&LLCompareEQBZ, - &&LLCompareNEBZ, - - &&GGCompareLTBZ, - &&GGCompareLEBZ, - &&GGCompareGTBZ, - &&GGCompareGEBZ, - &&GGCompareEQBZ, - &&GGCompareNEBZ, - - &&LLCompareLTBZ8, - &&LLCompareLEBZ8, - &&LLCompareGTBZ8, - &&LLCompareGEBZ8, - &&LLCompareEQBZ8, - &&LLCompareNEBZ8, - - &&GGCompareLTBZ8, - &&GGCompareLEBZ8, - &&GGCompareGTBZ8, - &&GGCompareGEBZ8, - &&GGCompareEQBZ8, - &&GGCompareNEBZ8, - - &&PostIncrement, - &&PostDecrement, - &&PreIncrement, - &&PreDecrement, - - &&PreIncrementAndPop, - &&PreDecrementAndPop, - - &&IncGlobal, - &&DecGlobal, - &&IncLocal, - &&DecLocal, - - &&Assign, - &&AssignAndPop, - &&AssignToGlobalAndPop, - &&AssignToLocalAndPop, - &&AssignToArrayAndPop, - - &&SubtractAssign, - &&AddAssign, - &&ModAssign, - &&MultiplyAssign, - &&DivideAssign, - &&ORAssign, - &&ANDAssign, - &&XORAssign, - &&RightShiftAssign, - &&LeftShiftAssign, - - &&SubtractAssignAndPop, - &&AddAssignAndPop, - &&ModAssignAndPop, - &&MultiplyAssignAndPop, - &&DivideAssignAndPop, - &&ORAssignAndPop, - &&ANDAssignAndPop, - &&XORAssignAndPop, - &&RightShiftAssignAndPop, - &&LeftShiftAssignAndPop, - - &&LogicalNot, - &&Negate, - - &&LiteralInt8, - &&LiteralInt16, - - &&IndexLiteral8, - &&IndexLiteral16, - - &&IndexLocalLiteral8, - &&IndexGlobalLiteral8, - &&IndexLocalLiteral16, - &&IndexGlobalLiteral16, - - &&BinaryAdditionAndStoreGlobal, - &&BinarySubtractionAndStoreGlobal, - &&BinaryMultiplicationAndStoreGlobal, - &&BinaryDivisionAndStoreGlobal, - - &&BinaryAdditionAndStoreLocal, - &&BinarySubtractionAndStoreLocal, - &&BinaryMultiplicationAndStoreLocal, - &&BinaryDivisionAndStoreLocal, - - &&CompareBEQ, - &&CompareBNE, - &&CompareBGE, - &&CompareBLE, - &&CompareBGT, - &&CompareBLT, - - &&CompareBEQ8, - &&CompareBNE8, - &&CompareBGE8, - &&CompareBLE8, - &&CompareBGT8, - &&CompareBLT8, - - &&BLA, - &&BLA8, - &&BLO, - &&BLO8, - - &&LiteralInt8ToGlobal, - &&LiteralInt16ToGlobal, - &&LiteralInt32ToLocal, - &&LiteralInt8ToLocal, - &&LiteralInt16ToLocal, - &&LiteralFloatToGlobal, - &&LiteralFloatToLocal, - &&LiteralInt32ToGlobal, - - &&GGBinaryMultiplication, - &&GLBinaryMultiplication, - &&LLBinaryMultiplication, - - &&GGBinaryAddition, - &&GLBinaryAddition, - &&LLBinaryAddition, - - &&GGBinarySubtraction, - &&GLBinarySubtraction, - &&LGBinarySubtraction, - &&LLBinarySubtraction, - - &&GGBinaryDivision, - &&GLBinaryDivision, - &&LGBinaryDivision, - &&LLBinaryDivision, - - &&GC_Command, - - &&GPushIterator, - &&LPushIterator, - &&GGNextKeyValueOrJump, - &&GLNextKeyValueOrJump, - &&LGNextKeyValueOrJump, - &&LLNextKeyValueOrJump, - &&GNextValueOrJump, - &&LNextValueOrJump, - - &&Switch, - &&SwitchLinear, - - &&GlobalStop, - - &&ToInt, - &&ToFloat, - - &&LoadLibConstant, - &&InitArray, - - &&DebugInfo, - }; -#endif - - const unsigned char* pc; - - union - { - unsigned char findex; - WRValue* register0; - const unsigned char *hashLoc; - uint32_t hashLocInt; - }; - - WRValue* register1 = 0; - WRValue* frameBase = 0; - WRState* w = context->w; - const unsigned char* bottom = context->bottom; - WRValue* stackTop = w->stack; - WRValue* globalSpace = (WRValue *)(context + 1); - - union - { - // never used at the same time..save RAM! - WRValue* register2; - uint32_t sizeCheck; - int args; - uint32_t hash; - WRVoidFunc* voidFunc; - WRReturnFunc* returnFunc; - WRTargetFunc* targetFunc; - }; - -#ifdef WRENCH_COMPACT - union - { - WRFuncIntCall intCall; - WRCompareFuncIntCall boolIntCall; - }; - union - { - WRFuncFloatCall floatCall; - WRCompareFuncFloatCall boolFloatCall; - }; -#endif - - w->err = WR_ERR_None; - -#ifdef WRENCH_INCLUDE_DEBUG_CODE - if ( context->debugInterface && function ) - { - if ( !function->offset ) // impossible offset, this is a re-entry! - { - // pop state - pc = context->debugInterface->m_pc; - frameBase = context->debugInterface->m_frameBase; - stackTop = context->debugInterface->m_stackTop; - goto debugContinue; - } - } -#endif - - if ( function ) - { - stackTop->p = 0; - (stackTop++)->p2 = INIT_AS_INT; - - for( args = 0; args < argn; ++args ) - { - *stackTop++ = argv[args]; - } - - pc = context->stopLocation; - goto callFunction; - } - - pc = bottom + 2; - -#ifdef WRENCH_JUMPTABLE_INTERPRETER - - CONTINUE; - -#else - - for(;;) - { - switch( READ_8_FROM_PC(pc++) ) - { -#endif - CASE(RegisterFunction): - { - hash = (stackTop -= 3)->i; - - findex = hash; - WRFunction* localFunctions = context->localFunctions + findex; - - localFunctions->arguments = (unsigned char)(hash >> 8); - localFunctions->frameSpaceNeeded = (unsigned char)(hash >> 16); - - localFunctions->hash = (stackTop + 1)->i; - - localFunctions->offset = bottom + (stackTop + 2)->i; - - localFunctions->frameBaseAdjustment = 1 - + localFunctions->frameSpaceNeeded - + localFunctions->arguments; - - context->registry.getAsRawValueHashTable(localFunctions->hash)->wrf = localFunctions; - - CONTINUE; - } - - CASE(LiteralInt32): - { - register0 = stackTop++; - register0->p2 = INIT_AS_INT; - goto load32ToTemp; - } - - CASE(LiteralZero): - { -literalZero: - stackTop->p = 0; - (stackTop++)->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(LiteralFloat): - { - register0 = stackTop++; - register0->p2 = INIT_AS_FLOAT; - goto load32ToTemp; - } - - CASE(LiteralString): - { - hash = (uint16_t)READ_16_FROM_PC(pc); - pc += 2; - - context->gc( stackTop ); - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = context->getSVA( hash, SV_CHAR, false ); - - for ( char* to = (char *)(stackTop++)->va->m_data ; hash ; --hash ) - { - *to++ = READ_8_FROM_PC(pc++); - } - - CONTINUE; - } - - CASE(LoadLibConstant): - { - *stackTop = *(w->globalRegistry.getAsRawValueHashTable(READ_32_FROM_PC(pc))); - - if ( (stackTop->p2 & INIT_AS_LIB_CONST) != INIT_AS_LIB_CONST ) - { - w->err = WR_ERR_library_constant_not_loaded; - return 0; - } - - (stackTop++)->p2 &= ~INIT_AS_LIB_CONST; - pc += 4; - CONTINUE; - } - - CASE(InitArray): - { - register0 = &(--stackTop)->singleValue(); - register1 = &(stackTop - 1)->deref(); - register1->p2 = INIT_AS_ARRAY; - register1->va = context->getSVA( register0->ui, SV_VALUE, true ); - CONTINUE; - } - - CASE(DebugInfo): - { -#ifdef WRENCH_INCLUDE_DEBUG_CODE - if ( context->debugInterface ) - { - context->debugInterface->codewordEncountered( READ_16_FROM_PC(pc), stackTop ); - if ( context->debugInterface->m_brk ) - { - // push state - context->debugInterface->m_argv = argv; - context->debugInterface->m_argn = argn; - context->debugInterface->m_pc = pc; - context->debugInterface->m_frameBase = frameBase; - context->debugInterface->m_stackTop = stackTop; - context->debugInterface->m_brk = false; - - stackTop->p2 = INIT_AS_DEBUG_BREAK; - return stackTop; - } - } - -debugContinue: -#endif - pc += 2; - CONTINUE; - } - - CASE(CallFunctionByHash): - { - args = READ_8_FROM_PC(pc++); - - // initialize a return value of 'zero' - register0 = stackTop; - register0->p = 0; - register0->p2 = INIT_AS_INT; - - if ( ! ((register1 = w->globalRegistry.getAsRawValueHashTable(READ_32_FROM_PC(pc)))->ccb) ) - { -#ifdef WRENCH_INCLUDE_DEBUG_CODE - if ( context->debugInterface ) - { - - } -#endif - w->err = WR_ERR_function_hash_signature_not_found; - return 0; - } - - register1->ccb( context, stackTop - args, args, *stackTop, register1->usr ); - - // DO care about return value, which will be at the top - // of the stack - - if ( args ) - { - stackTop -= (args - 1); - *(stackTop - 1) = *register0; - } - else - { - ++stackTop; - } - pc += 4; - CONTINUE; - } - - CASE(CallFunctionByHashAndPop): - { - args = READ_8_FROM_PC(pc++); - - if ( !((register1 = w->globalRegistry.getAsRawValueHashTable(READ_32_FROM_PC(pc)))->ccb) ) - { -#ifdef WRENCH_INCLUDE_DEBUG_CODE - if ( context->debugInterface ) - { - - } -#endif - w->err = WR_ERR_function_hash_signature_not_found; - return 0; - } - - register1->ccb( context, stackTop - args, args, *stackTop, register1->usr ); - - stackTop -= args; - pc += 4; - CONTINUE; - } - - CASE(CallFunctionByIndex): - { - args = READ_8_FROM_PC(pc++); - - // function MUST exist or we wouldn't be here, we would - // be in the "call by hash" above - function = context->localFunctions + READ_8_FROM_PC(pc++); - pc += READ_8_FROM_PC(pc); -callFunction: - // rectify arg count? hopefully not lets get calling! - if ( args != function->arguments ) - { - if ( args > function->arguments ) - { - stackTop -= args - function->arguments; // extra arguments are *poofed* - } - else - { - // un-specified arguments are set to IntZero - for( ; args < function->arguments; ++args ) - { - stackTop->p = 0; - (stackTop++)->p2 = INIT_AS_INT; - } - } - } - - // for speed locals are not guaranteed to be initialized.. but we have to make - // sure they are not randomly selected to a - // "collectable" type or the gc will iterate them in some corner cases (ask me how I know) - // A simple offset add would be so fast.. - // TODO figure out how to use it! - -// stackTop += function->frameSpaceNeeded; - for( int l=0; lframeSpaceNeeded; ++l ) - { - (stackTop++)->p2 = INIT_AS_INT; - } - - // temp value contains return vector/frame base - register0 = stackTop++; // return vector - register0->frame = frameBase; - register0->returnOffset = pc - bottom; // can't use "pc" because it might set the "gc me" bit, this is guaranteed to be small enough to never do that - - pc = function->offset; - - // set the new frame base to the base arguments the function is expecting - frameBase = stackTop - function->frameBaseAdjustment; - - assert( stackTop < (w->stack + w->stackSize) ); - - CONTINUE; - } - - CASE(PushIndexFunctionReturnValue): - { - *(stackTop++) = (++register0)->deref(); - CONTINUE; - } - - CASE(CallLibFunction): - { - stackTop->p2 = INIT_AS_INT; - stackTop->p = 0; - - args = READ_8_FROM_PC(pc++); // which have already been pushed - - if ( ! ((register1 = w->globalRegistry.getAsRawValueHashTable(READ_32_FROM_PC(pc)))->lcb) ) - { -#ifdef WRENCH_INCLUDE_DEBUG_CODE - if ( context->debugInterface ) - { - - } -#endif - w->err = WR_ERR_function_hash_signature_not_found; - return 0; - } - - register1->lcb( stackTop, args, context ); - pc += 4; - -#ifdef WRENCH_COMPACT - // this "always works" but is not necessary if args is - // zero, just a simple stackTop increment is required - stackTop -= --args; - *(stackTop - 1) = *(stackTop + args); -#else - if ( args ) - { - stackTop -= --args; - *(stackTop - 1) = *(stackTop + args); - } - else - { - ++stackTop; - } -#endif - - CONTINUE; - } - - CASE(CallLibFunctionAndPop): - { - args = READ_8_FROM_PC(pc++); // which have already been pushed - - if ( ! ((register1 = w->globalRegistry.getAsRawValueHashTable(READ_32_FROM_PC(pc)))->lcb) ) - { -#ifdef WRENCH_INCLUDE_DEBUG_CODE - if ( context->debugInterface ) - { - - } -#endif - w->err = WR_ERR_function_hash_signature_not_found; - return 0; - } - - register1->lcb( stackTop, args, context ); - pc += 4; - - stackTop -= args; - - CONTINUE; - } - - CASE(NewObjectTable): - { - const unsigned char* table = bottom + READ_16_FROM_PC(pc); - pc += 2; - - if ( table > bottom ) - { - // if unit was called with no arguments from global - // level there are not "free" stack entries to - // gnab, so create it here, but preserve the - // first value - - // NOTE: we are guaranteed to have at least one - // value if table > bottom - - unsigned char count = READ_8_FROM_PC(table++); - - register1 = (stackTop + READ_8_FROM_PC(table))->r; - register2 = (stackTop + READ_8_FROM_PC(table))->r2; - - stackTop->p2 = INIT_AS_STRUCT; - - // table : members in local space - // table + 1 : arguments + 1 (+1 to save the calculation below) - // table +2/3 : m_mod - // table + 4: [static hash table ] - - stackTop->va = context->getSVA( count, SV_VALUE, false ); - - stackTop->va->m_ROMHashTable = table + 3; - stackTop->va->m_mod = READ_16_FROM_PC(table+1); - - register0 = stackTop->va->m_Vdata; - register0->r = register1; - (register0++)->r2 = register2; - - if ( --count > 0 ) - { - memcpy( (char*)register0, stackTop + READ_8_FROM_PC(table) + 1, count*sizeof(WRValue) ); - } - - context->gc(++stackTop); // dop this here to take care of any memory the 'new' allocated - } - else - { - goto literalZero; - } - - CONTINUE; - } - - CASE(AssignToObjectTableByOffset): - { - register1 = --stackTop; - register0 = (stackTop - 1); - sizeCheck = READ_8_FROM_PC(pc++); - if ( IS_EXARRAY_TYPE(register0->xtype) && (sizeCheck < register0->va->m_size) ) - { - register0 = register0->va->m_Vdata + sizeCheck; -#ifdef WRENCH_COMPACT - goto doAssignToLocalAndPop; -#else - wr_assign[(register0->type << 2) | register1->type](register0, register1); -#endif - } - - CONTINUE; - } - - CASE(AssignToHashTableAndPop): - { - register0 = --stackTop; // value - register1 = --stackTop; // index - wr_assignToHashTable( context, register1, register0, stackTop - 1 ); - CONTINUE; - } - - CASE(Remove): - { - hash = (--stackTop)->getHash(); - register0 = &((stackTop - 1)->deref()); - if ( register0->xtype == WR_EX_HASH_TABLE ) - { - register0->va->exists( hash, false, true ); - } - CONTINUE; - } - - CASE(HashEntryExists): - { - register0 = --stackTop; - register1 = (stackTop - 1); - register2 = &(register1->deref()); - register1->p2 = INIT_AS_INT; - register1->i = ((register2->xtype == WR_EX_HASH_TABLE) && register2->va->exists(register0->getHash(), false, false)) ? 1 : 0; - - CONTINUE; - } - - CASE(PopOne): - { - --stackTop; - CONTINUE; - } - - CASE(ReturnZero): - { - (stackTop++)->init(); - } - CASE(Return): - { - register0 = stackTop - 2; - - pc = bottom + register0->returnOffset; // grab return PC - - stackTop = frameBase; - frameBase = register0->frame; - CONTINUE; - } - - CASE(ToInt): - { - register0 = stackTop - 1; - register0->i = register0->asInt(); - register0->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(ToFloat): - { - register0 = stackTop - 1; - register0->f = register0->asFloat(); - register0->p2 = INIT_AS_FLOAT; - CONTINUE; - } - - CASE(GlobalStop): - { - register0 = w->stack - 1; - ++pc; - } - CASE(Stop): - { - *w->stack = *(register0 + 1); - context->stopLocation = pc - 1; - return &(w->stack)->deref(); - } - - CASE(Dereference): - { - register0 = (stackTop - 1); - *register0 = register0->deref(); - CONTINUE; - } - - CASE(Index): - { - register0 = --stackTop; - register1 = --stackTop; - } - - CASE(IndexSkipLoad): - { - wr_index[(register0->type<<2)|register1->type]( context, register0, register1, stackTop++ ); - CONTINUE; - } - - CASE(CountOf): - { - register0 = stackTop - 1; - wr_countOfArrayElement( register0, register0 ); - CONTINUE; - } - - CASE(HashOf): - { - register0 = stackTop - 1; - register0->ui = register0->getHash(); - register0->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(GlobalIndexHash): - { - register0 = &(globalSpace + READ_8_FROM_PC(pc++))->deref(); - register1 = stackTop++; - goto hashIndexJump; - } - - CASE(LocalIndexHash): - { - register0 = &(frameBase + READ_8_FROM_PC(pc++))->deref(); - register1 = stackTop++; - goto hashIndexJump; - } - - CASE(StackIndexHash): - { - register0 = &(stackTop - 1)->deref(); - register1 = register0; -hashIndexJump: - stackTop->ui = READ_32_FROM_PC(pc); - pc += 4; - if ( !EXPECTS_HASH_INDEX(register0->xtype) ) - { - register0->p2 = INIT_AS_HASH_TABLE; - register0->va = context->getSVA( 0, SV_HASH_TABLE, false ); - } - -#ifdef WRENCH_COMPACT - goto indexTempLiteralPostLoad; -#else - stackTop->p2 = INIT_AS_INT; - wr_index[(WR_INT << 2) | register0->type](context, stackTop, register0, stackTop - 1); - CONTINUE; -#endif - } - - CASE(StackSwap): - { - register0 = stackTop - 1; - register1 = stackTop - READ_8_FROM_PC(pc++); - register2 = register0->r2; - WRValue* r = register0->r; - - register0->r2 = register1->r2; - register0->r = register1->r; - - register1->r = r; - register1->r2 = register2; - - CONTINUE; - } - - CASE(SwapTwoToTop): // accomplish two (or three when optimized) swaps into one instruction - { - register0 = stackTop - READ_8_FROM_PC(pc++); - - uint32_t t = (stackTop - 1)->p2; - const void* p = (stackTop - 1)->p; - - (stackTop - 1)->p2 = register0->p2; - (stackTop - 1)->p = register0->p; - - register0->p = p; - register0->p2 = t; - - register0 = stackTop - READ_8_FROM_PC(pc++); - - t = (stackTop - 2)->p2; - p = (stackTop - 2)->p; - - (stackTop - 2)->p2 = register0->p2; - (stackTop - 2)->p = register0->p; - - register0->p = p; - register0->p2 = t; - - CONTINUE; - } - - CASE(LoadFromLocal): - { - stackTop->p = frameBase + READ_8_FROM_PC(pc++); - (stackTop++)->p2 = INIT_AS_REF; - CONTINUE; - } - - CASE(LoadFromGlobal): - { - stackTop->p = globalSpace + READ_8_FROM_PC(pc++); - (stackTop++)->p2 = INIT_AS_REF; - CONTINUE; - } - - CASE(LLValues): { register0 = frameBase + READ_8_FROM_PC(pc++); register1 = frameBase + READ_8_FROM_PC(pc++); CONTINUE; } - CASE(LGValues): { register0 = frameBase + READ_8_FROM_PC(pc++); register1 = globalSpace + READ_8_FROM_PC(pc++); CONTINUE; } - CASE(GLValues): { register0 = globalSpace + READ_8_FROM_PC(pc++); register1 = frameBase + READ_8_FROM_PC(pc++); CONTINUE; } - CASE(GGValues): { register0 = globalSpace + READ_8_FROM_PC(pc++); register1 = globalSpace + READ_8_FROM_PC(pc++); CONTINUE; } - - CASE(BitwiseNOT): - { - register0 = stackTop - 1; - register0->ui = wr_bitwiseNot[ register0->type ]( register0 ); - register0->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(RelativeJump): - { - pc += READ_16_FROM_PC(pc); - CONTINUE; - } - - CASE(RelativeJump8): - { - pc += (int8_t)READ_8_FROM_PC(pc); - CONTINUE; - } - - CASE(BZ): - { - register0 = --stackTop; - pc += wr_LogicalNot[register0->type](register0) ? READ_16_FROM_PC(pc) : 2; - CONTINUE; - } - - CASE(BZ8): - { - register0 = --stackTop; - pc += wr_LogicalNot[register0->type](register0) ? (int8_t)READ_8_FROM_PC(pc) : 2; - CONTINUE; - } - - CASE(LogicalNot): - { - register0 = stackTop - 1; - register0->i = wr_LogicalNot[ register0->type ]( register0 ); - register0->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(Negate): - { - register0 = stackTop - 1; - wr_negate[ register0->type ]( register0, register0 ); - CONTINUE; - } - - CASE(IndexLiteral16): - { - stackTop->i = READ_16_FROM_PC(pc); - pc += 2; - goto indexLiteral; - } - - CASE(IndexLiteral8): - { - stackTop->i = READ_8_FROM_PC(pc++); -indexLiteral: - stackTop->p2 = INIT_AS_INT; - register0 = stackTop - 1; - wr_index[(WR_INT<<2)|register0->type]( context, stackTop, register0, register0 ); - CONTINUE; - } - - CASE(IndexLocalLiteral16): - { - register0 = frameBase + READ_8_FROM_PC(pc++); - (++stackTop)->i = READ_16_FROM_PC(pc); - pc += 2; -#ifdef WRENCH_COMPACT - goto indexTempLiteralPostLoad; -#else - stackTop->p2 = INIT_AS_INT; - wr_index[(WR_INT << 2) | register0->type](context, stackTop, register0, stackTop - 1); - CONTINUE; -#endif - } - - CASE(IndexLocalLiteral8): - { - register0 = frameBase + READ_8_FROM_PC(pc++); -#ifdef WRENCH_COMPACT -indexTempLiteral: -#endif - (++stackTop)->i = READ_8_FROM_PC(pc++); -#ifdef WRENCH_COMPACT -indexTempLiteralPostLoad: -#endif - stackTop->p2 = INIT_AS_INT; - wr_index[(WR_INT<<2)|register0->type]( context, stackTop, register0, stackTop - 1 ); - CONTINUE; - } - - CASE(IndexGlobalLiteral16): - { - register0 = globalSpace + READ_8_FROM_PC(pc++); - (++stackTop)->i = READ_16_FROM_PC(pc); - pc += 2; -#ifdef WRENCH_COMPACT - goto indexTempLiteralPostLoad; -#else - stackTop->p2 = INIT_AS_INT; - wr_index[(WR_INT << 2) | register0->type](context, stackTop, register0, stackTop - 1); - CONTINUE; -#endif - } - - CASE(IndexGlobalLiteral8): - { - register0 = globalSpace + READ_8_FROM_PC(pc++); -#ifdef WRENCH_COMPACT - goto indexTempLiteral; -#else - (++stackTop)->i = READ_8_FROM_PC(pc++); - stackTop->p2 = INIT_AS_INT; - wr_index[(WR_INT << 2) | register0->type](context, stackTop, register0, stackTop - 1); - CONTINUE; -#endif - } - - CASE(AssignToGlobalAndPop): - { - register0 = globalSpace + READ_8_FROM_PC(pc++); -#ifdef WRENCH_COMPACT - goto doAssignToLocalAndPopPreLoad; -#else - register1 = --stackTop; - wr_assign[(register0->type<<2)|register1->type]( register0, register1 ); - CONTINUE; -#endif - } - - CASE(AssignToLocalAndPop): - { - register0 = frameBase + READ_8_FROM_PC(pc++); - -#ifdef WRENCH_COMPACT -doAssignToLocalAndPopPreLoad: -#endif - register1 = --stackTop; - -#ifdef WRENCH_COMPACT -doAssignToLocalAndPop: -#endif - wr_assign[(register0->type<<2)|register1->type]( register0, register1 ); - CONTINUE; - } - - CASE(AssignToArrayAndPop): - { - stackTop->p2 = INIT_AS_INT; // index - stackTop->i = READ_16_FROM_PC(pc); - pc += 2; - register1 = stackTop - 1; // value - register0 = stackTop - 2; // array - - wr_index[(WR_INT<<2)|register0->type]( context, stackTop, register0, stackTop + 1 ); - register0 = stackTop-- + 1; - -#ifdef WRENCH_COMPACT - goto doAssignToLocalAndPop; -#else - wr_assign[(register0->type << 2) | register1->type](register0, register1); - CONTINUE; -#endif - } - - CASE(LiteralInt8): - { - stackTop->i = (int32_t)(int8_t)READ_8_FROM_PC(pc++); - (stackTop++)->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(LiteralInt16): - { - stackTop->i = READ_16_FROM_PC(pc); - pc += 2; - (stackTop++)->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(LiteralInt8ToGlobal): - { - register0 = globalSpace + READ_8_FROM_PC(pc++); - register0->i = (int32_t)(int8_t)READ_8_FROM_PC(pc++); - register0->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(LiteralInt16ToGlobal): - { - register0 = globalSpace + READ_8_FROM_PC(pc++); - register0->i = READ_16_FROM_PC(pc); - register0->p2 = INIT_AS_INT; - pc += 2; - CONTINUE; - } - - CASE(LiteralInt32ToLocal): - { - register0 = frameBase + READ_8_FROM_PC(pc++); - register0->p2 = INIT_AS_INT; - goto load32ToTemp; - } - - CASE(LiteralInt8ToLocal): - { - register0 = frameBase + READ_8_FROM_PC(pc++); - register0->i = (int32_t)(int8_t)READ_8_FROM_PC(pc++); - register0->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(LiteralInt16ToLocal): - { - register0 = frameBase + READ_8_FROM_PC(pc++); - register0->i = READ_16_FROM_PC(pc); - register0->p2 = INIT_AS_INT; - pc += 2; - CONTINUE; - } - - CASE(LiteralFloatToGlobal): - { - register0 = globalSpace + READ_8_FROM_PC(pc++); - register0->p2 = INIT_AS_FLOAT; - goto load32ToTemp; - } - - CASE(LiteralFloatToLocal): - { - register0 = frameBase + READ_8_FROM_PC(pc++); - register0->p2 = INIT_AS_FLOAT; - goto load32ToTemp; - } - - CASE(LiteralInt32ToGlobal): - { - register0 = globalSpace + READ_8_FROM_PC(pc++); - register0->p2 = INIT_AS_INT; -load32ToTemp: - register0->i = READ_32_FROM_PC(pc); - pc += 4; - CONTINUE; - } - - CASE(GC_Command): - { - context->gcPauseCount = READ_16_FROM_PC(pc); - pc += 2; - CONTINUE; - } - - CASE(GPushIterator): - { - register0 = globalSpace + READ_8_FROM_PC(pc++); - wr_pushIterator[register0->type]( register0, globalSpace + READ_8_FROM_PC(pc++) ); - CONTINUE; - } - - CASE(LPushIterator): - { - register0 = frameBase + READ_8_FROM_PC(pc++); - wr_pushIterator[register0->type]( register0, globalSpace + READ_8_FROM_PC(pc++) ); - CONTINUE; - } - - CASE(GGNextKeyValueOrJump): { register1 = globalSpace + READ_8_FROM_PC(pc++); register0 = globalSpace + READ_8_FROM_PC(pc++); goto NextIterator; } - CASE(GLNextKeyValueOrJump): { register1 = globalSpace + READ_8_FROM_PC(pc++); register0 = frameBase + READ_8_FROM_PC(pc++); goto NextIterator; } - CASE(LGNextKeyValueOrJump): { register1 = frameBase + READ_8_FROM_PC(pc++); register0 = globalSpace + READ_8_FROM_PC(pc++); goto NextIterator; } - CASE(LLNextKeyValueOrJump): { register1 = frameBase + READ_8_FROM_PC(pc++); register0 = frameBase + READ_8_FROM_PC(pc++); goto NextIterator; } - CASE(GNextValueOrJump): { register0 = globalSpace + READ_8_FROM_PC(pc++); register1 = 0; goto NextIterator; } - CASE(LNextValueOrJump): - { - register0 = frameBase + READ_8_FROM_PC(pc++); - register1 = 0; -NextIterator: - register2 = globalSpace + READ_8_FROM_PC(pc++); - pc += wr_getNextValue( register2, register0, register1) ? 2 : READ_16_FROM_PC(pc); - CONTINUE; - } - - CASE(Switch): - { - hash = (--stackTop)->getHash(); // hash has been loaded - hashLoc = pc + 4 + 6*(hash % (uint16_t)READ_16_FROM_PC(pc)); // jump into the table - - if ( (uint32_t)READ_32_FROM_PC(hashLoc) != hash ) - { - hashLoc = pc - 2; // nope, point it at default vector - } - - hashLoc += 4; // yup, point hashLoc to jump vector - - pc += READ_16_FROM_PC(hashLoc); - CONTINUE; - } - - CASE(SwitchLinear): - { - hashLocInt = (--stackTop)->getHash(); // the "hashes" were all 0<=h<256 - - if ( hashLocInt < READ_8_FROM_PC(pc++) ) // catch selecting > size - { - hashLoc = pc + (hashLocInt<<1) + 2; // jump to vector - pc += READ_16_FROM_PC( hashLoc ); // and read it - } - else - { - pc += READ_16_FROM_PC( pc ); - } - CONTINUE; - } - -//------------------------------------------------------------------------------------------------------------- -#ifdef WRENCH_COMPACT //--------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------- - - CASE(PostIncrement): - { - register0 = stackTop - 1; - m_unaryPost[ register0->type ]( register0, register0, 1 ); - CONTINUE; - } - - CASE(PostDecrement): - { - register0 = stackTop - 1; - m_unaryPost[ register0->type ]( register0, register0, -1 ); - CONTINUE; - } - - CASE(BinaryRightShiftSkipLoad): { intCall = rightShiftI; goto targetFuncOpSkipLoad; } - CASE(BinaryLeftShiftSkipLoad): { intCall = leftShiftI; goto targetFuncOpSkipLoad; } - CASE(BinaryAndSkipLoad): { intCall = andI; goto targetFuncOpSkipLoad; } - CASE(BinaryOrSkipLoad): { intCall = orI; goto targetFuncOpSkipLoad; } - CASE(BinaryXORSkipLoad): { intCall = xorI; goto targetFuncOpSkipLoad; } - CASE(BinaryModSkipLoad): - { - intCall = modI; -targetFuncOpSkipLoad: - floatCall = blankF; -targetFuncOpSkipLoadNoClobberF: - register2 = stackTop++; -targetFuncOpSkipLoadAndReg2: - wr_funcBinary[(register1->type<<2)|register0->type]( register1, - register0, - register2, - intCall, - floatCall ); - CONTINUE; - } - - CASE(BinaryMultiplication): { floatCall = multiplicationF; intCall = multiplicationI; goto targetFuncOp; } - CASE(BinarySubtraction): { floatCall = subtractionF; intCall = subtractionI; goto targetFuncOp; } - CASE(BinaryDivision): { floatCall = divisionF; intCall = divisionI; goto targetFuncOp; } - CASE(BinaryRightShift): { floatCall = blankF; intCall = rightShiftI; goto targetFuncOp; } - CASE(BinaryLeftShift): { floatCall = blankF; intCall = leftShiftI; goto targetFuncOp; } - CASE(BinaryMod): { floatCall = blankF; intCall = modI; goto targetFuncOp; } - CASE(BinaryOr): { floatCall = blankF; intCall = orI; goto targetFuncOp; } - CASE(BinaryXOR): { floatCall = blankF; intCall = xorI; goto targetFuncOp; } - CASE(BinaryAnd): { floatCall = blankF; intCall = andI; goto targetFuncOp; } - CASE(BinaryAddition): - { - floatCall = addF; - intCall = wr_addI; -targetFuncOp: - register1 = --stackTop; - register0 = stackTop - 1; - register2 = register0; - goto targetFuncOpSkipLoadAndReg2; - } - - - CASE(SubtractAssign): { floatCall = subtractionF; intCall = subtractionI; goto binaryTableOp; } - CASE(AddAssign): { floatCall = addF; intCall = wr_addI; goto binaryTableOp; } - CASE(MultiplyAssign): { floatCall = multiplicationF; intCall = multiplicationI; goto binaryTableOp; } - CASE(DivideAssign): { floatCall = divisionF; intCall = divisionI; goto binaryTableOp; } - CASE(ModAssign): { intCall = modI; goto binaryTableOpBlankF; } - CASE(ORAssign): { intCall = orI; goto binaryTableOpBlankF; } - CASE(ANDAssign): { intCall = andI; goto binaryTableOpBlankF; } - CASE(XORAssign): { intCall = xorI; goto binaryTableOpBlankF; } - CASE(RightShiftAssign): { intCall = rightShiftI; goto binaryTableOpBlankF; } - CASE(LeftShiftAssign): - { - intCall = leftShiftI; -binaryTableOpBlankF: - floatCall = blankF; -binaryTableOp: - register0 = --stackTop; - register1 = stackTop - 1; - goto binaryTableOpAndPopCall; - } - - - CASE(Assign): - { - register0 = --stackTop; - register1 = stackTop - 1; - goto assignAndPopEx; - } - - CASE(SubtractAssignAndPop): { floatCall = subtractionF; intCall = subtractionI; goto binaryTableOpAndPop; } - CASE(AddAssignAndPop): { floatCall = addF; intCall = wr_addI; goto binaryTableOpAndPop; } - CASE(MultiplyAssignAndPop): { floatCall = multiplicationF; intCall = multiplicationI; goto binaryTableOpAndPop; } - CASE(DivideAssignAndPop): { floatCall = divisionF; intCall = divisionI; goto binaryTableOpAndPop; } - CASE(ModAssignAndPop): { intCall = modI; goto binaryTableOpAndPopBlankF; } - CASE(ORAssignAndPop): { intCall = orI; goto binaryTableOpAndPopBlankF; } - CASE(ANDAssignAndPop): { intCall = andI; goto binaryTableOpAndPopBlankF; } - CASE(XORAssignAndPop): { intCall = xorI; goto binaryTableOpAndPopBlankF; } - CASE(RightShiftAssignAndPop): { intCall = rightShiftI; goto binaryTableOpAndPopBlankF; } - CASE(LeftShiftAssignAndPop): - { - intCall = leftShiftI; - -binaryTableOpAndPopBlankF: - floatCall = blankF; -binaryTableOpAndPop: - register0 = --stackTop; - register1 = --stackTop; - -binaryTableOpAndPopCall: - wr_FuncAssign[(register0->type<<2)|register1->type]( register0, register1, intCall, floatCall ); - CONTINUE; - } - - CASE(AssignAndPop): - { - register0 = --stackTop; - register1 = --stackTop; -assignAndPopEx: - wr_assign[(register0->type<<2)|register1->type]( register0, register1 ); - CONTINUE; - } - - CASE(BinaryAdditionAndStoreGlobal) : { floatCall = addF; intCall = wr_addI; goto targetFuncStoreGlobalOp; } - CASE(BinarySubtractionAndStoreGlobal): { floatCall = subtractionF; intCall = subtractionI; goto targetFuncStoreGlobalOp; } - CASE(BinaryMultiplicationAndStoreGlobal): { floatCall = multiplicationF; intCall = multiplicationI; goto targetFuncStoreGlobalOp; } - CASE(BinaryDivisionAndStoreGlobal): - { - floatCall = divisionF; - intCall = divisionI; - -targetFuncStoreGlobalOp: - register0 = --stackTop; - register1 = --stackTop; - wr_funcBinary[(register0->type<<2)|register1->type]( register0, register1, globalSpace + READ_8_FROM_PC(pc++), intCall, floatCall ); - CONTINUE; - } - - CASE(BinaryAdditionAndStoreLocal): { floatCall = addF; intCall = wr_addI; goto targetFuncStoreLocalOp; } - CASE(BinarySubtractionAndStoreLocal): { floatCall = subtractionF; intCall = subtractionI; goto targetFuncStoreLocalOp; } - CASE(BinaryMultiplicationAndStoreLocal): { floatCall = multiplicationF; intCall = multiplicationI; goto targetFuncStoreLocalOp; } - CASE(BinaryDivisionAndStoreLocal): - { - floatCall = divisionF; - intCall = divisionI; - -targetFuncStoreLocalOp: - register0 = --stackTop; - register1 = --stackTop; - wr_funcBinary[(register0->type<<2)|register1->type]( register0, register1, frameBase + READ_8_FROM_PC(pc++), intCall, floatCall ); - CONTINUE; - } - - CASE(PreIncrement): - { - register0 = stackTop - 1; -compactPreIncrement: - intCall = wr_addI; - floatCall = addF; - -compactIncrementWork: - register1 = stackTop + 1; - register1->i = 1; - register1->p2 = INIT_AS_INT; - goto binaryTableOpAndPopCall; - } - - CASE(PreDecrement): - { - register0 = stackTop - 1; -compactPreDecrement: - intCall = subtractionI; - floatCall = subtractionF; - goto compactIncrementWork; - } - CASE(PreIncrementAndPop): - { - register0 = --stackTop; - goto compactPreIncrement; - } - - CASE(PreDecrementAndPop): - { - register0 = --stackTop; - goto compactPreDecrement; - } - - CASE(IncGlobal): - { - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto compactPreIncrement; - } - - CASE(DecGlobal): - { - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto compactPreDecrement; - } - CASE(IncLocal): - { - register0 = frameBase + READ_8_FROM_PC(pc++); - goto compactPreIncrement; - } - - CASE(DecLocal): - { - register0 = frameBase + READ_8_FROM_PC(pc++); - goto compactPreDecrement; - } - - CASE(BLA): - { - boolIntCall = CompareANDI; - boolFloatCall = CompareBlankF; -compactBLAPreLoad: - register0 = --stackTop; - register1 = --stackTop; -compactBLA: - pc += wr_Compare[(register0->type<<2)|register1->type]( register0, register1, boolIntCall, boolFloatCall ) ? 2 : READ_16_FROM_PC(pc); - CONTINUE; - } - - CASE(BLA8): - { - boolIntCall = CompareANDI; - boolFloatCall = CompareBlankF; -compactBLA8PreLoad: - register0 = --stackTop; -compactBLA8PreReg1: - register1 = --stackTop; -compactBLA8: - pc += wr_Compare[(register0->type<<2)|register1->type]( register0, register1, boolIntCall, boolFloatCall ) ? 2 : (int8_t)READ_8_FROM_PC(pc); - CONTINUE; - } - - CASE(BLO): - { - boolIntCall = CompareORI; - boolFloatCall = CompareBlankF; - register0 = --stackTop; - register1 = --stackTop; - goto compactBLA; - } - - CASE(BLO8): - { - boolIntCall = CompareORI; - boolFloatCall = CompareBlankF; - register0 = --stackTop; - register1 = --stackTop; - goto compactBLA8; - } - - CASE(GGBinaryMultiplication): - { - floatCall = multiplicationF; - intCall = multiplicationI; -CompactGGFunc: - register0 = globalSpace + READ_8_FROM_PC(pc++); - register1 = globalSpace + READ_8_FROM_PC(pc++); - goto targetFuncOpSkipLoadNoClobberF; - } - - CASE(GLBinaryMultiplication): - { - floatCall = multiplicationF; - intCall = multiplicationI; -CompactGLFunc: - register0 = frameBase + READ_8_FROM_PC(pc++); - register1 = globalSpace + READ_8_FROM_PC(pc++); - goto targetFuncOpSkipLoadNoClobberF; - } - - CASE(LLBinaryMultiplication): - { - floatCall = multiplicationF; - intCall = multiplicationI; -CompactFFFunc: - register0 = frameBase + READ_8_FROM_PC(pc++); - register1 = frameBase + READ_8_FROM_PC(pc++); - goto targetFuncOpSkipLoadNoClobberF; - } - - CASE(GGBinaryAddition): - { - floatCall = addF; - intCall = wr_addI; - goto CompactGGFunc; - } - - CASE(GLBinaryAddition): - { - floatCall = addF; - intCall = wr_addI; - goto CompactGLFunc; - } - - CASE(LLBinaryAddition): - { - floatCall = addF; - intCall = wr_addI; - goto CompactFFFunc; - } - - CASE(GGBinarySubtraction): - { - floatCall = subtractionF; - intCall = subtractionI; - goto CompactGGFunc; - } - - CASE(GLBinarySubtraction): - { - floatCall = subtractionF; - intCall = subtractionI; - goto CompactGLFunc; - } - - CASE(LGBinarySubtraction): - { - floatCall = subtractionF; - intCall = subtractionI; -CompactFGFunc: - register0 = globalSpace + READ_8_FROM_PC(pc++); - register1 = frameBase + READ_8_FROM_PC(pc++); - goto targetFuncOpSkipLoadNoClobberF; - } - - CASE(LLBinarySubtraction): - { - floatCall = subtractionF; - intCall = subtractionI; - goto CompactFFFunc; - } - - CASE(GGBinaryDivision): - { - floatCall = divisionF; - intCall = divisionI; - goto CompactGGFunc; - } - - CASE(GLBinaryDivision): - { - floatCall = divisionF; - intCall = divisionI; - goto CompactGLFunc; - } - - CASE(LGBinaryDivision): - { - floatCall = divisionF; - intCall = divisionI; - goto CompactFGFunc; - } - - CASE(LLBinaryDivision): - { - floatCall = divisionF; - intCall = divisionI; - goto CompactFFFunc; - } - - CASE(LogicalAnd): { boolIntCall = CompareANDI; goto compactReturnFuncNormal; } - CASE(LogicalOr): { boolIntCall = CompareORI; goto compactReturnFuncNormal; } - CASE(CompareLE): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactReturnFuncInverted; } - CASE(CompareGE): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactReturnFuncInverted; } - CASE(CompareGT): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactReturnFuncNormal; } - CASE(CompareEQ): { boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactReturnFuncNormal; } - CASE(CompareLT): - { - boolIntCall = CompareLTI; - boolFloatCall = CompareLTF; - -compactReturnFuncNormal: - register0 = --stackTop; -compactReturnFuncPostLoad: - register1 = stackTop - 1; - register1->i = (int)wr_Compare[(register0->type<<2)|register1->type]( register0, register1, boolIntCall, boolFloatCall ); - register1->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(CompareNE): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; - -compactReturnFuncInverted: - register0 = --stackTop; -compactReturnFuncInvertedPostLoad: - register1 = stackTop - 1; - register1->i = (int)!wr_Compare[(register0->type<<2)|register1->type]( register0, register1, boolIntCall, boolFloatCall ); - register1->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(GSCompareEQ): { register0 = globalSpace + READ_8_FROM_PC(pc++); boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactReturnFuncPostLoad; } - CASE(GSCompareNE): { register0 = globalSpace + READ_8_FROM_PC(pc++); boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactReturnFuncInvertedPostLoad; } - CASE(GSCompareGT): { register0 = globalSpace + READ_8_FROM_PC(pc++); boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactReturnFuncPostLoad; } - CASE(GSCompareLT): { register0 = globalSpace + READ_8_FROM_PC(pc++); boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactReturnFuncPostLoad; } - CASE(GSCompareGE): { register0 = globalSpace + READ_8_FROM_PC(pc++); boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactReturnFuncInvertedPostLoad; } - CASE(GSCompareLE): { register0 = globalSpace + READ_8_FROM_PC(pc++); boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactReturnFuncInvertedPostLoad; } - - CASE(LSCompareEQ): { register0 = frameBase + READ_8_FROM_PC(pc++); boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactReturnFuncPostLoad; } - CASE(LSCompareNE): { register0 = frameBase + READ_8_FROM_PC(pc++); boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactReturnFuncInvertedPostLoad; } - CASE(LSCompareGT): { register0 = frameBase + READ_8_FROM_PC(pc++); boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactReturnFuncPostLoad; } - CASE(LSCompareLT): { register0 = frameBase + READ_8_FROM_PC(pc++); boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactReturnFuncPostLoad; } - CASE(LSCompareGE): { register0 = frameBase + READ_8_FROM_PC(pc++); boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactReturnFuncInvertedPostLoad; } - CASE(LSCompareLE): { register0 = frameBase + READ_8_FROM_PC(pc++); boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactReturnFuncInvertedPostLoad; } - - CASE(CompareBLE): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactReturnFuncBInverted; } - CASE(CompareBGE): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactReturnFuncBInverted; } - CASE(CompareBGT): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactBLAPreLoad; } - CASE(CompareBLT): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactBLAPreLoad; } - CASE(CompareBEQ): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; - goto compactBLAPreLoad; - } - - CASE(CompareBNE): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactReturnFuncBInverted: - register0 = --stackTop; -compactReturnFuncBInvertedPreReg1: - register1 = --stackTop; -compactReturnFuncBInvertedPostReg1: - pc += wr_Compare[(register0->type<<2)|register1->type]( register0, register1, boolIntCall, boolFloatCall ) ? READ_16_FROM_PC(pc) : 2; - CONTINUE; - } - - CASE(CompareBLE8): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactReturnFuncBInverted8; } - CASE(CompareBGE8): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactReturnFuncBInverted8; } - CASE(CompareBGT8): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactBLA8PreLoad; } - CASE(CompareBLT8): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactBLA8PreLoad; } - CASE(CompareBEQ8): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; - goto compactBLA8PreLoad; - } - - CASE(CompareBNE8): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactReturnFuncBInverted8: - register0 = --stackTop; -compactReturnFuncBInverted8PreReg1: - register1 = --stackTop; -compactReturnFuncBInverted8Post: - pc += wr_Compare[(register0->type<<2)|register1->type]( register0, register1, boolIntCall, boolFloatCall ) ? (int8_t)READ_8_FROM_PC(pc) : 2; - CONTINUE; - } - - CASE(GGCompareLE): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactReturnCompareGGNEPost; } - CASE(GGCompareGE): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactReturnCompareGGNEPost; } - CASE(GGCompareNE): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactReturnCompareGGNEPost: - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto compactReturnCompareNEPost; - } - - CASE(GGCompareGT): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactEQCompareGGReg; } - CASE(GGCompareEQ): { boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactEQCompareGGReg; } - CASE(GGCompareLT): - { - boolIntCall = CompareLTI; - boolFloatCall = CompareLTF; -compactEQCompareGGReg: - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto compactReturnCompareEQPost; - } - - CASE(LLCompareGT): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactReturnCompareEQ; } - CASE(LLCompareLT): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactReturnCompareEQ; } - CASE(LLCompareEQ): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactReturnCompareEQ: - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); -compactReturnCompareEQPost: - stackTop->i = (int)wr_Compare[(register0->type<<2)|register1->type]( register0, register1, boolIntCall, boolFloatCall ); - (stackTop++)->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(LLCompareGE): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactReturnCompareNE; } - CASE(LLCompareLE): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactReturnCompareNE; } - CASE(LLCompareNE): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactReturnCompareNE: - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); -compactReturnCompareNEPost: - stackTop->i = (int)!wr_Compare[(register0->type<<2)|register1->type]( register0, register1, boolIntCall, boolFloatCall ); - (stackTop++)->p2 = INIT_AS_INT; - CONTINUE; - } - - - CASE(GSCompareGEBZ): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactCompareGInverted; } - CASE(GSCompareLEBZ): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareGInverted; } - CASE(GSCompareNEBZ): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactCompareGInverted: - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto compactReturnFuncBInvertedPreReg1; - } - - CASE(GSCompareEQBZ): { boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactCompareGNormal; } - CASE(GSCompareGTBZ): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareGNormal; } - CASE(GSCompareLTBZ): - { - boolIntCall = CompareLTI; - boolFloatCall = CompareLTF; -compactCompareGNormal: - register0 = globalSpace + READ_8_FROM_PC(pc++); - register1 = --stackTop; - goto compactBLA; - } - - CASE(LSCompareGEBZ): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactCompareLInverted; } - CASE(LSCompareLEBZ): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareLInverted; } - CASE(LSCompareNEBZ): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactCompareLInverted: - register0 = frameBase + READ_8_FROM_PC(pc++); - register1 = --stackTop; - goto compactReturnFuncBInvertedPostReg1; - } - - CASE(LSCompareEQBZ): { boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactCompareLNormal; } - CASE(LSCompareGTBZ): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareLNormal; } - CASE(LSCompareLTBZ): - { - boolIntCall = CompareLTI; - boolFloatCall = CompareLTF; -compactCompareLNormal: - register0 = frameBase + READ_8_FROM_PC(pc++); - register1 = --stackTop; - goto compactBLA; - } - - CASE(GSCompareGEBZ8): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactCompareG8Inverted; } - CASE(GSCompareLEBZ8): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareG8Inverted; } - CASE(GSCompareNEBZ8): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactCompareG8Inverted: - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto compactReturnFuncBInverted8PreReg1; - } - - CASE(GSCompareEQBZ8): { boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactCompareG8Normal; } - CASE(GSCompareGTBZ8): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareG8Normal; } - CASE(GSCompareLTBZ8): - { - boolIntCall = CompareLTI; - boolFloatCall = CompareLTF; -compactCompareG8Normal: - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto compactBLA8PreReg1; - } - - CASE(LSCompareGEBZ8): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactCompareL8Inverted; } - CASE(LSCompareLEBZ8): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareL8Inverted; } - CASE(LSCompareNEBZ8): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactCompareL8Inverted: - register0 = frameBase + READ_8_FROM_PC(pc++); - goto compactReturnFuncBInverted8PreReg1; - } - - CASE(LSCompareEQBZ8): { boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactCompareL8Normal; } - CASE(LSCompareGTBZ8): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareL8Normal; } - CASE(LSCompareLTBZ8): - { - boolIntCall = CompareLTI; - boolFloatCall = CompareLTF; -compactCompareL8Normal: - register0 = frameBase + READ_8_FROM_PC(pc++); - goto compactBLA8PreReg1; - } - - CASE(LLCompareLEBZ): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareLLInv; } - CASE(LLCompareGEBZ): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactCompareLLInv; } - CASE(LLCompareNEBZ): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactCompareLLInv: - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - goto compactReturnFuncBInvertedPostReg1; - } - CASE(LLCompareEQBZ): { boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactCompareLL; } - CASE(LLCompareGTBZ): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareLL; } - CASE(LLCompareLTBZ): - { - boolIntCall = CompareLTI; - boolFloatCall = CompareLTF; -compactCompareLL: - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - goto compactBLA; - } - - CASE(LLCompareLEBZ8): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareLLInv8; } - CASE(LLCompareGEBZ8): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactCompareLLInv8; } - CASE(LLCompareNEBZ8): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactCompareLLInv8: - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - goto compactReturnFuncBInverted8Post; - } - CASE(LLCompareEQBZ8): { boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactCompareLL8; } - CASE(LLCompareGTBZ8): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareLL8; } - CASE(LLCompareLTBZ8): - { - boolIntCall = CompareLTI; - boolFloatCall = CompareLTF; -compactCompareLL8: - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - goto compactBLA8; - } - - CASE(GGCompareGEBZ): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactCompareGGInv; } - CASE(GGCompareLEBZ): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareGGInv; } - CASE(GGCompareNEBZ): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactCompareGGInv: - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto compactReturnFuncBInvertedPostReg1; - } - - CASE(GGCompareEQBZ): { boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactCompareGG; } - CASE(GGCompareGTBZ): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareGG; } - CASE(GGCompareLTBZ): - { - boolIntCall = CompareLTI; - boolFloatCall = CompareLTF; -compactCompareGG: - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto compactBLA; - } - - CASE(GGCompareGEBZ8): { boolIntCall = CompareLTI; boolFloatCall = CompareLTF; goto compactCompareGGInv8; } - CASE(GGCompareLEBZ8): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareGGInv8; } - CASE(GGCompareNEBZ8): - { - boolIntCall = CompareEQI; - boolFloatCall = CompareEQF; -compactCompareGGInv8: - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto compactReturnFuncBInverted8Post; - } - CASE(GGCompareEQBZ8): { boolIntCall = CompareEQI; boolFloatCall = CompareEQF; goto compactCompareGG8; } - CASE(GGCompareGTBZ8): { boolIntCall = CompareGTI; boolFloatCall = CompareGTF; goto compactCompareGG8; } - CASE(GGCompareLTBZ8): - { - boolIntCall = CompareLTI; - boolFloatCall = CompareLTF; -compactCompareGG8: - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto compactBLA8; - } - - -//------------------------------------------------------------------------------------------------------------- -#else -//------------------------------------------------------------------------------------------------------------- -// NON-COMPACT version - - CASE(PostIncrement): - { - register0 = stackTop - 1; - wr_postinc[ register0->type ]( register0, register0 ); - CONTINUE; - } - - CASE(PostDecrement): - { - register0 = stackTop - 1; - wr_postdec[ register0->type ]( register0, register0 ); - CONTINUE; - } - - CASE(PreIncrement): { register0 = stackTop - 1; wr_preinc[ register0->type ]( register0 ); CONTINUE; } - CASE(PreDecrement): { register0 = stackTop - 1; wr_predec[ register0->type ]( register0 ); CONTINUE; } - CASE(PreIncrementAndPop): { register0 = --stackTop; wr_preinc[ register0->type ]( register0 ); CONTINUE; } - CASE(PreDecrementAndPop): { register0 = --stackTop; wr_predec[ register0->type ]( register0 ); CONTINUE; } - CASE(IncGlobal): { register0 = globalSpace + READ_8_FROM_PC(pc++); wr_preinc[ register0->type ]( register0 ); CONTINUE; } - CASE(DecGlobal): { register0 = globalSpace + READ_8_FROM_PC(pc++); wr_predec[ register0->type ]( register0 ); CONTINUE; } - CASE(IncLocal): { register0 = frameBase + READ_8_FROM_PC(pc++); wr_preinc[ register0->type ]( register0 ); CONTINUE; } - CASE(DecLocal): { register0 = frameBase + READ_8_FROM_PC(pc++); wr_predec[ register0->type ]( register0 ); CONTINUE; } - - CASE(BLA): - { - register0 = --stackTop; - register1 = --stackTop; - pc += wr_LogicalAND[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : READ_16_FROM_PC(pc); - CONTINUE; - } - - CASE(BLA8): - { - register0 = --stackTop; - register1 = --stackTop; - pc += wr_LogicalAND[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : (int8_t)READ_8_FROM_PC(pc); - CONTINUE; - } - - CASE(BLO): - { - register0 = --stackTop; - register1 = --stackTop; - pc += wr_LogicalOR[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : READ_16_FROM_PC(pc); - CONTINUE; - } - - CASE(BLO8): - { - register0 = --stackTop; - register1 = --stackTop; - pc += wr_LogicalOR[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : (int8_t)READ_8_FROM_PC(pc); - CONTINUE; - } - - - CASE(GGBinaryMultiplication): - { - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - wr_MultiplyBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - CASE(GLBinaryMultiplication): - { - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - wr_MultiplyBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - CASE(LLBinaryMultiplication): - { - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - wr_MultiplyBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - - CASE(GGBinaryAddition): - { - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - wr_AdditionBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - CASE(GLBinaryAddition): - { - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - wr_AdditionBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - CASE(LLBinaryAddition): - { - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - wr_AdditionBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - - CASE(GGBinarySubtraction): - { - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - wr_SubtractBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - CASE(GLBinarySubtraction): - { - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - wr_SubtractBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - CASE(LGBinarySubtraction): - { - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - wr_SubtractBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - CASE(LLBinarySubtraction): - { - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - wr_SubtractBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - - CASE(GGBinaryDivision): - { - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - wr_DivideBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - CASE(GLBinaryDivision): - { - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - wr_DivideBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - CASE(LGBinaryDivision): - { - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - wr_DivideBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - CASE(LLBinaryDivision): - { - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - wr_DivideBinary[(register0->type<<2)|register1->type]( register0, register1, stackTop++ ); - CONTINUE; - } - - - CASE(LogicalAnd): { returnFunc = wr_LogicalAND; goto returnFuncNormal; } - CASE(LogicalOr): { returnFunc = wr_LogicalOR; goto returnFuncNormal; } - CASE(CompareLE): { returnFunc = wr_CompareGT; goto returnFuncInverted; } - CASE(CompareGE): { returnFunc = wr_CompareLT; goto returnFuncInverted; } - CASE(CompareGT): { returnFunc = wr_CompareGT; goto returnFuncNormal; } - CASE(CompareLT): { returnFunc = wr_CompareLT; goto returnFuncNormal; } - CASE(CompareEQ): - { - returnFunc = wr_CompareEQ; -returnFuncNormal: - register0 = --stackTop; -returnFuncPostLoad: - register1 = stackTop - 1; - register1->i = (int)returnFunc[(register0->type<<2)|register1->type]( register0, register1 ); - register1->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(CompareNE): - { - returnFunc = wr_CompareEQ; -returnFuncInverted: - register0 = --stackTop; -returnFuncInvertedPostLoad: - register1 = stackTop - 1; - register1->i = (int)!returnFunc[(register0->type<<2)|register1->type]( register0, register1 ); - register1->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(GSCompareEQ): { register0 = globalSpace + READ_8_FROM_PC(pc++); returnFunc = wr_CompareEQ; goto returnFuncPostLoad; } - CASE(GSCompareNE): { register0 = globalSpace + READ_8_FROM_PC(pc++); returnFunc = wr_CompareEQ; goto returnFuncInvertedPostLoad; } - CASE(GSCompareGT): { register0 = globalSpace + READ_8_FROM_PC(pc++); returnFunc = wr_CompareGT; goto returnFuncPostLoad; } - CASE(GSCompareLT): { register0 = globalSpace + READ_8_FROM_PC(pc++); returnFunc = wr_CompareLT; goto returnFuncPostLoad; } - CASE(GSCompareGE): { register0 = globalSpace + READ_8_FROM_PC(pc++); returnFunc = wr_CompareLT; goto returnFuncInvertedPostLoad; } - CASE(GSCompareLE): { register0 = globalSpace + READ_8_FROM_PC(pc++); returnFunc = wr_CompareGT; goto returnFuncInvertedPostLoad; } - - CASE(LSCompareEQ): { register0 = frameBase + READ_8_FROM_PC(pc++); returnFunc = wr_CompareEQ; goto returnFuncPostLoad; } - CASE(LSCompareNE): { register0 = frameBase + READ_8_FROM_PC(pc++); returnFunc = wr_CompareEQ; goto returnFuncInvertedPostLoad; } - CASE(LSCompareGT): { register0 = frameBase + READ_8_FROM_PC(pc++); returnFunc = wr_CompareGT; goto returnFuncPostLoad; } - CASE(LSCompareLT): { register0 = frameBase + READ_8_FROM_PC(pc++); returnFunc = wr_CompareLT; goto returnFuncPostLoad; } - CASE(LSCompareGE): { register0 = frameBase + READ_8_FROM_PC(pc++); returnFunc = wr_CompareLT; goto returnFuncInvertedPostLoad; } - CASE(LSCompareLE): { register0 = frameBase + READ_8_FROM_PC(pc++); returnFunc = wr_CompareGT; goto returnFuncInvertedPostLoad; } - - CASE(CompareBLE): { returnFunc = wr_CompareGT; goto returnFuncBInverted; } - CASE(CompareBGE): { returnFunc = wr_CompareLT; goto returnFuncBInverted; } - CASE(CompareBGT): { returnFunc = wr_CompareGT; goto returnFuncBNormal; } - CASE(CompareBLT): { returnFunc = wr_CompareLT; goto returnFuncBNormal; } - CASE(CompareBEQ): - { - returnFunc = wr_CompareEQ; -returnFuncBNormal: - register0 = --stackTop; - register1 = --stackTop; - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : READ_16_FROM_PC(pc); - CONTINUE; - } - - CASE(CompareBNE): - { - returnFunc = wr_CompareEQ; -returnFuncBInverted: - register0 = --stackTop; - register1 = --stackTop; - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? READ_16_FROM_PC(pc) : 2; - CONTINUE; - } - - CASE(CompareBLE8): { returnFunc = wr_CompareGT; goto returnFuncBInverted8; } - CASE(CompareBGE8): { returnFunc = wr_CompareLT; goto returnFuncBInverted8; } - CASE(CompareBGT8): { returnFunc = wr_CompareGT; goto returnFuncBNormal8; } - CASE(CompareBLT8): { returnFunc = wr_CompareLT; goto returnFuncBNormal8; } - CASE(CompareBEQ8): - { - returnFunc = wr_CompareEQ; -returnFuncBNormal8: - register0 = --stackTop; - register1 = --stackTop; - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : (int8_t)READ_8_FROM_PC(pc); - CONTINUE; - } - - CASE(CompareBNE8): - { - returnFunc = wr_CompareEQ; -returnFuncBInverted8: - register0 = --stackTop; - register1 = --stackTop; - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? (int8_t)READ_8_FROM_PC(pc) : 2; - CONTINUE; - } - - CASE(GGCompareEQ): - { - returnFunc = wr_CompareEQ; - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto returnCompareEQPost; - } - CASE(GGCompareNE): - { - returnFunc = wr_CompareEQ; - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto returnCompareNEPost; - } - CASE(GGCompareGT): - { - returnFunc = wr_CompareGT; - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto returnCompareEQPost; - } - CASE(GGCompareGE): - { - returnFunc = wr_CompareLT; - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto returnCompareNEPost; - } - CASE(GGCompareLT): - { - returnFunc = wr_CompareLT; - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto returnCompareEQPost; - } - CASE(GGCompareLE): - { - returnFunc = wr_CompareGT; - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - goto returnCompareNEPost; - } - - - CASE(LLCompareGT): { returnFunc = wr_CompareGT; goto returnCompareEQ; } - CASE(LLCompareLT): { returnFunc = wr_CompareLT; goto returnCompareEQ; } - CASE(LLCompareEQ): - { - returnFunc = wr_CompareEQ; -returnCompareEQ: - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); -returnCompareEQPost: - stackTop->i = (int)returnFunc[(register0->type<<2)|register1->type]( register0, register1 ); - (stackTop++)->p2 = INIT_AS_INT; - CONTINUE; - } - - CASE(LLCompareGE): { returnFunc = wr_CompareLT; goto returnCompareNE; } - CASE(LLCompareLE): { returnFunc = wr_CompareGT; goto returnCompareNE; } - CASE(LLCompareNE): - { - returnFunc = wr_CompareEQ; -returnCompareNE: - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); -returnCompareNEPost: - stackTop->i = (int)!returnFunc[(register0->type<<2)|register1->type]( register0, register1 ); - (stackTop++)->p2 = INIT_AS_INT; - CONTINUE; - } - - - - CASE(GSCompareGEBZ): { returnFunc = wr_CompareLT; goto CompareGInverted; } - CASE(GSCompareLEBZ): { returnFunc = wr_CompareGT; goto CompareGInverted; } - CASE(GSCompareNEBZ): - { - returnFunc = wr_CompareEQ; -CompareGInverted: - register0 = globalSpace + READ_8_FROM_PC(pc++); - register1 = --stackTop; - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? READ_16_FROM_PC(pc) : 2; - CONTINUE; - } - - CASE(GSCompareEQBZ): { returnFunc = wr_CompareEQ; goto CompareGNormal; } - CASE(GSCompareGTBZ): { returnFunc = wr_CompareGT; goto CompareGNormal; } - CASE(GSCompareLTBZ): - { - returnFunc = wr_CompareLT; -CompareGNormal: - register0 = globalSpace + READ_8_FROM_PC(pc++); - register1 = --stackTop; - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : READ_16_FROM_PC(pc); - CONTINUE; - } - - CASE(LSCompareGEBZ): { returnFunc = wr_CompareLT; goto CompareLInverted; } - CASE(LSCompareLEBZ): { returnFunc = wr_CompareGT; goto CompareLInverted; } - CASE(LSCompareNEBZ): - { - returnFunc = wr_CompareEQ; -CompareLInverted: - register0 = frameBase + READ_8_FROM_PC(pc++); - register1 = --stackTop; - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? READ_16_FROM_PC(pc) : 2; - CONTINUE; - } - - CASE(LSCompareEQBZ): { returnFunc = wr_CompareEQ; goto CompareLNormal; } - CASE(LSCompareGTBZ): { returnFunc = wr_CompareGT; goto CompareLNormal; } - CASE(LSCompareLTBZ): - { - returnFunc = wr_CompareLT; -CompareLNormal: - register0 = frameBase + READ_8_FROM_PC(pc++); - register1 = --stackTop; - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : READ_16_FROM_PC(pc); - CONTINUE; - } - - CASE(GSCompareGEBZ8): { returnFunc = wr_CompareLT; goto CompareG8Inverted; } - CASE(GSCompareLEBZ8): { returnFunc = wr_CompareGT; goto CompareG8Inverted; } - CASE(GSCompareNEBZ8): - { - returnFunc = wr_CompareEQ; -CompareG8Inverted: - register0 = globalSpace + READ_8_FROM_PC(pc++); - register1 = --stackTop; - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? (int8_t)READ_8_FROM_PC(pc) : 2; - CONTINUE; - } - - CASE(GSCompareEQBZ8): { returnFunc = wr_CompareEQ; goto CompareG8Normal; } - CASE(GSCompareGTBZ8): { returnFunc = wr_CompareGT; goto CompareG8Normal; } - CASE(GSCompareLTBZ8): - { - returnFunc = wr_CompareLT; -CompareG8Normal: - register0 = globalSpace + READ_8_FROM_PC(pc++); - register1 = --stackTop; - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : (int8_t)READ_8_FROM_PC(pc); - CONTINUE; - } - - CASE(LSCompareGEBZ8): { returnFunc = wr_CompareLT; goto CompareL8Inverted; } - CASE(LSCompareLEBZ8): { returnFunc = wr_CompareGT; goto CompareL8Inverted; } - CASE(LSCompareNEBZ8): - { - returnFunc = wr_CompareEQ; -CompareL8Inverted: - register0 = frameBase + READ_8_FROM_PC(pc++); - register1 = --stackTop; - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? (int8_t)READ_8_FROM_PC(pc) : 2; - CONTINUE; - } - - CASE(LSCompareEQBZ8): { returnFunc = wr_CompareEQ; goto CompareL8Normal; } - CASE(LSCompareGTBZ8): { returnFunc = wr_CompareGT; goto CompareL8Normal; } - CASE(LSCompareLTBZ8): - { - returnFunc = wr_CompareLT; -CompareL8Normal: - register0 = frameBase + READ_8_FROM_PC(pc++); - register1 = --stackTop; - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : (int8_t)READ_8_FROM_PC(pc); - CONTINUE; - } - - CASE(LLCompareGEBZ): { returnFunc = wr_CompareLT; goto CompareLLInv; } - CASE(LLCompareLEBZ): { returnFunc = wr_CompareGT; goto CompareLLInv; } - CASE(LLCompareNEBZ): - { - returnFunc = wr_CompareEQ; -CompareLLInv: - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? READ_16_FROM_PC(pc) : 2; - CONTINUE; - } - CASE(LLCompareEQBZ): { returnFunc = wr_CompareEQ; goto CompareLL; } - CASE(LLCompareGTBZ): { returnFunc = wr_CompareGT; goto CompareLL; } - CASE(LLCompareLTBZ): - { - returnFunc = wr_CompareLT; -CompareLL: - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : READ_16_FROM_PC(pc); - CONTINUE; - } - - - CASE(LLCompareGEBZ8): { returnFunc = wr_CompareLT; goto CompareLL8Inv; } - CASE(LLCompareLEBZ8): { returnFunc = wr_CompareGT; goto CompareLL8Inv; } - CASE(LLCompareNEBZ8): - { - returnFunc = wr_CompareEQ; -CompareLL8Inv: - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? (int8_t)READ_8_FROM_PC(pc) : 2; - CONTINUE; - } - CASE(LLCompareEQBZ8): { returnFunc = wr_CompareEQ; goto CompareLL8; } - CASE(LLCompareGTBZ8): { returnFunc = wr_CompareGT; goto CompareLL8; } - CASE(LLCompareLTBZ8): - { - returnFunc = wr_CompareLT; -CompareLL8: - register1 = frameBase + READ_8_FROM_PC(pc++); - register0 = frameBase + READ_8_FROM_PC(pc++); - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : (int8_t)READ_8_FROM_PC(pc); - CONTINUE; - } - - - CASE(GGCompareGEBZ): { returnFunc = wr_CompareLT; goto CompareGGInv; } - CASE(GGCompareLEBZ): { returnFunc = wr_CompareGT; goto CompareGGInv; } - CASE(GGCompareNEBZ): - { - returnFunc = wr_CompareEQ; -CompareGGInv: - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? READ_16_FROM_PC(pc) : 2; - CONTINUE; - } - - CASE(GGCompareEQBZ): { returnFunc = wr_CompareEQ; goto CompareGG; } - CASE(GGCompareGTBZ): { returnFunc = wr_CompareGT; goto CompareGG; } - CASE(GGCompareLTBZ): - { - returnFunc = wr_CompareLT; -CompareGG: - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : READ_16_FROM_PC(pc); - CONTINUE; - } - - - CASE(GGCompareGEBZ8): { returnFunc = wr_CompareLT; goto CompareGG8Inv; } - CASE(GGCompareLEBZ8): { returnFunc = wr_CompareGT; goto CompareGG8Inv; } - CASE(GGCompareNEBZ8): - { - returnFunc = wr_CompareEQ; -CompareGG8Inv: - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? (int8_t)READ_8_FROM_PC(pc) : 2; - CONTINUE; - } - CASE(GGCompareEQBZ8): { returnFunc = wr_CompareEQ; goto CompareGG8; } - CASE(GGCompareGTBZ8): { returnFunc = wr_CompareGT; goto CompareGG8; } - CASE(GGCompareLTBZ8): - { - returnFunc = wr_CompareLT; -CompareGG8: - register1 = globalSpace + READ_8_FROM_PC(pc++); - register0 = globalSpace + READ_8_FROM_PC(pc++); - pc += returnFunc[(register0->type<<2)|register1->type]( register0, register1 ) ? 2 : (int8_t)READ_8_FROM_PC(pc); - CONTINUE; - } - - - CASE(BinaryRightShiftSkipLoad): { targetFunc = wr_RightShiftBinary; goto targetFuncOpSkipLoad; } - CASE(BinaryLeftShiftSkipLoad): { targetFunc = wr_LeftShiftBinary; goto targetFuncOpSkipLoad; } - CASE(BinaryAndSkipLoad): { targetFunc = wr_ANDBinary; goto targetFuncOpSkipLoad; } - CASE(BinaryOrSkipLoad): { targetFunc = wr_ORBinary; goto targetFuncOpSkipLoad; } - CASE(BinaryXORSkipLoad): { targetFunc = wr_XORBinary; goto targetFuncOpSkipLoad; } - CASE(BinaryModSkipLoad): - { - targetFunc = wr_ModBinary; -targetFuncOpSkipLoad: - targetFunc[(register1->type<<2)|register0->type]( register1, register0, stackTop++ ); - CONTINUE; - } - - CASE(BinaryMultiplication): { targetFunc = wr_MultiplyBinary; goto targetFuncOp; } - CASE(BinarySubtraction): { targetFunc = wr_SubtractBinary; goto targetFuncOp; } - CASE(BinaryDivision): { targetFunc = wr_DivideBinary; goto targetFuncOp; } - CASE(BinaryRightShift): { targetFunc = wr_RightShiftBinary; goto targetFuncOp; } - CASE(BinaryLeftShift): { targetFunc = wr_LeftShiftBinary; goto targetFuncOp; } - CASE(BinaryMod): { targetFunc = wr_ModBinary; goto targetFuncOp; } - CASE(BinaryOr): { targetFunc = wr_ORBinary; goto targetFuncOp; } - CASE(BinaryXOR): { targetFunc = wr_XORBinary; goto targetFuncOp; } - CASE(BinaryAnd): { targetFunc = wr_ANDBinary; goto targetFuncOp; } - CASE(BinaryAddition): - { - targetFunc = wr_AdditionBinary; -targetFuncOp: - register1 = --stackTop; - register0 = stackTop - 1; - targetFunc[(register1->type<<2)|register0->type]( register1, register0, register0 ); - CONTINUE; - } - - CASE(SubtractAssign): { voidFunc = wr_SubtractAssign; goto binaryTableOp; } - CASE(AddAssign): { voidFunc = wr_AddAssign; goto binaryTableOp; } - CASE(ModAssign): { voidFunc = wr_ModAssign; goto binaryTableOp; } - CASE(MultiplyAssign): { voidFunc = wr_MultiplyAssign; goto binaryTableOp; } - CASE(DivideAssign): { voidFunc = wr_DivideAssign; goto binaryTableOp; } - CASE(ORAssign): { voidFunc = wr_ORAssign; goto binaryTableOp; } - CASE(ANDAssign): { voidFunc = wr_ANDAssign; goto binaryTableOp; } - CASE(XORAssign): { voidFunc = wr_XORAssign; goto binaryTableOp; } - CASE(RightShiftAssign): { voidFunc = wr_RightShiftAssign; goto binaryTableOp; } - CASE(LeftShiftAssign): { voidFunc = wr_LeftShiftAssign; goto binaryTableOp; } - CASE(Assign): - { - voidFunc = wr_assign; -binaryTableOp: - register0 = --stackTop; - register1 = stackTop - 1; - voidFunc[(register0->type<<2)|register1->type]( register0, register1 ); - CONTINUE; - } - - CASE(SubtractAssignAndPop): { voidFunc = wr_SubtractAssign; goto binaryTableOpAndPop; } - CASE(AddAssignAndPop): { voidFunc = wr_AddAssign; goto binaryTableOpAndPop; } - CASE(ModAssignAndPop): { voidFunc = wr_ModAssign; goto binaryTableOpAndPop; } - CASE(MultiplyAssignAndPop): { voidFunc = wr_MultiplyAssign; goto binaryTableOpAndPop; } - CASE(DivideAssignAndPop): { voidFunc = wr_DivideAssign; goto binaryTableOpAndPop; } - CASE(ORAssignAndPop): { voidFunc = wr_ORAssign; goto binaryTableOpAndPop; } - CASE(ANDAssignAndPop): { voidFunc = wr_ANDAssign; goto binaryTableOpAndPop; } - CASE(XORAssignAndPop): { voidFunc = wr_XORAssign; goto binaryTableOpAndPop; } - CASE(RightShiftAssignAndPop): { voidFunc = wr_RightShiftAssign; goto binaryTableOpAndPop; } - CASE(LeftShiftAssignAndPop): { voidFunc = wr_LeftShiftAssign; goto binaryTableOpAndPop; } - CASE(AssignAndPop): - { - voidFunc = wr_assign; - -binaryTableOpAndPop: - register0 = --stackTop; - register1 = --stackTop; - voidFunc[(register0->type<<2)|register1->type]( register0, register1 ); - - CONTINUE; - } - - CASE(BinaryAdditionAndStoreGlobal) : { targetFunc = wr_AdditionBinary; goto targetFuncStoreGlobalOp; } - CASE(BinarySubtractionAndStoreGlobal): { targetFunc = wr_SubtractBinary; goto targetFuncStoreGlobalOp; } - CASE(BinaryMultiplicationAndStoreGlobal): { targetFunc = wr_MultiplyBinary; goto targetFuncStoreGlobalOp; } - CASE(BinaryDivisionAndStoreGlobal): - { - targetFunc = wr_DivideBinary; - -targetFuncStoreGlobalOp: - register0 = --stackTop; - register1 = --stackTop; - targetFunc[(register0->type<<2)|register1->type]( register0, register1, globalSpace + READ_8_FROM_PC(pc++) ); - CONTINUE; - } - - CASE(BinaryAdditionAndStoreLocal): { targetFunc = wr_AdditionBinary; goto targetFuncStoreLocalOp; } - CASE(BinarySubtractionAndStoreLocal): { targetFunc = wr_SubtractBinary; goto targetFuncStoreLocalOp; } - CASE(BinaryMultiplicationAndStoreLocal): { targetFunc = wr_MultiplyBinary; goto targetFuncStoreLocalOp; } - CASE(BinaryDivisionAndStoreLocal): - { - targetFunc = wr_DivideBinary; - -targetFuncStoreLocalOp: - register0 = --stackTop; - register1 = --stackTop; - targetFunc[(register0->type<<2)|register1->type]( register0, register1, frameBase + READ_8_FROM_PC(pc++) ); - CONTINUE; - } - - -//------------------------------------------------------------------------------------------------------------- -#endif//------------------------------------------------------------------------------------------------------- -//------------------------------------------------------------------------------------------------------------- - - -#ifndef WRENCH_JUMPTABLE_INTERPRETER - #ifdef _MSC_VER - default: __assume(0); // tells the compiler to make this a jump table - #endif - } - } -#endif -} - -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -//------------------------------------------------------------------------------ -unsigned char* wr_pack16( int16_t i, unsigned char* buf ) -{ - *buf = i & 0xFF; - *(buf + 1) = (i>>8) & 0xFF; - return buf; -} - -//------------------------------------------------------------------------------ -unsigned char* wr_pack32( int32_t l, unsigned char* buf ) -{ - *buf = l & 0xFF; - *(buf + 1) = (l>>8) & 0xFF; - *(buf + 2) = (l>>16) & 0xFF; - *(buf + 3) = (l>>24) & 0xFF; - return buf; -} - -#ifdef WRENCH_BIG_ENDIAN -//------------------------------------------------------------------------------ -int32_t wr_x32( const int32_t val ) -{ - int32_t v = READ_32_FROM_PC( (const unsigned char *)&val ); - return v; -} - -//------------------------------------------------------------------------------ -int16_t wr_x16( const int16_t val ) -{ - int16_t v = READ_16_FROM_PC( (const unsigned char *)&val ); - return v; -} -#endif - -//------------------------------------------------------------------------------ -int32_t* WrenchValue::makeInt() -{ - m_value->i = m_value->asInt(); - m_value->p2 = INIT_AS_INT; - return &(m_value->i); -} - -//------------------------------------------------------------------------------ -float* WrenchValue::makeFloat() -{ - m_value->f = m_value->asFloat(); - m_value->p2 = INIT_AS_FLOAT; - return &(m_value->f); -} - -//------------------------------------------------------------------------------ -WRValue* WrenchValue::asArrayMember( const int index ) -{ - if ( !IS_ARRAY(m_value->xtype) ) - { - // then make it one! - m_value->p2 = INIT_AS_ARRAY; - m_value->va = m_context->getSVA( index + 1, SV_VALUE, true ); - } - else if ( index >= (int)m_value->va->m_size ) - { - m_value->va = wr_growValueArray( m_value->va, index ); - } - - return (WRValue*)m_value->va->get( index ); -} - -//------------------------------------------------------------------------------ -WRState* wr_newState( int stackSize ) -{ - WRState* state = (WRState *)malloc( stackSize*sizeof(WRValue) + sizeof(WRState) ); - memset( (unsigned char*)state, 0, stackSize*sizeof(WRValue) + sizeof(WRState) ); - - state->stackSize = stackSize; - state->stack = (WRValue *)((unsigned char *)state + sizeof(WRState)); - - state->globalRegistry.init( 0, SV_VOID_HASH_TABLE ); - - return state; -} - -//------------------------------------------------------------------------------ -void wr_destroyState( WRState* w ) -{ - while( w->contextList ) - { - wr_destroyContext( w->contextList ); - } - - w->globalRegistry.clear(); - - free( w ); -} - -//------------------------------------------------------------------------------ -WRError wr_getLastError( WRState* w ) -{ - return (WRError)w->err; -} - -//------------------------------------------------------------------------------ -bool wr_executeFunctionZero( WRContext* context ) -{ - return wr_executeContext(context) ? true : false; -} - -//------------------------------------------------------------------------------ -WRContext* wr_newContext( WRState* w, const unsigned char* block, const int blockSize ) -{ - // CRC the code block, at least is it what the compiler intended? - const unsigned char* p = block + (blockSize - 4); - uint32_t hash = READ_32_FROM_PC( p ); - if ( hash != wr_hash_read8(block, (blockSize - 4)) ) - { - w->err = WR_ERR_bad_bytecode_CRC; - return 0; - } - - int needed = sizeof(WRContext) // class - + READ_8_FROM_PC(block) * sizeof(WRFunction) // local functions - + READ_8_FROM_PC(block+1) * sizeof(WRValue); // globals - - WRContext* C = (WRContext *)malloc( needed ); - - memset((char*)C, 0, needed); - - C->globals = READ_8_FROM_PC(block+1); - C->w = w; - - C->localFunctions = (WRFunction*)((unsigned char *)C + sizeof(WRContext) + C->globals * sizeof(WRValue)); - - C->registry.init( 0, SV_VOID_HASH_TABLE ); - C->registry.m_vNext = w->contextList; - C->bottom = block; - C->bottomSize = blockSize; - - return (w->contextList = C); -} - -//------------------------------------------------------------------------------ -WRValue* wr_executeContext( WRContext* context ) -{ - WRState* w = context->w; - if ( context->stopLocation ) - { - w->err = WR_ERR_execute_function_zero_called_more_than_once; - return 0; - } - - return wr_callFunction( context, (int32_t)0 ); -} - -//------------------------------------------------------------------------------ -WRContext* wr_run( WRState* w, const unsigned char* block, const int blockSize ) -{ - WRContext* C = wr_newContext( w, block, blockSize ); - - if ( C && !wr_executeContext(C) ) - { - wr_destroyContext( C ); - return 0; - } - - return C; -} - -//------------------------------------------------------------------------------ -void wr_destroyContext( WRContext* context ) -{ - if ( !context ) - { - return; - } - -#ifdef WRENCH_INCLUDE_DEBUG_CODE - free( context->debugInterface ); // in case it had been created -#endif - - WRContext* prev = 0; - - // unlink it - for( WRContext* c = context->w->contextList; c; c = (WRContext*)c->registry.m_vNext ) - { - if ( c == context ) - { - if ( prev ) - { - prev->registry.m_vNext = c->registry.m_vNext; - } - else - { - context->w->contextList = (WRContext*)context->w->contextList->registry.m_vNext; - } - - // free all memory allocations by forcing the gc to collect everything - context->gcPauseCount = 0; - context->globals = 0; - context->gc( 0 ); - - context->registry.clear(); - - free( context ); - - break; - } - prev = c; - } -} - -//------------------------------------------------------------------------------ -void wr_registerFunction( WRState* w, const char* name, WR_C_CALLBACK function, void* usr ) -{ - WRValue* V = w->globalRegistry.getAsRawValueHashTable( wr_hashStr(name) ); - V->usr = usr; - V->ccb = function; -} - -//------------------------------------------------------------------------------ -void wr_registerLibraryFunction( WRState* w, const char* signature, WR_LIB_CALLBACK function ) -{ - w->globalRegistry.getAsRawValueHashTable(wr_hashStr(signature))->lcb = function; -} - -//------------------------------------------------------------------------------ -void wr_registerLibraryConstant( WRState* w, const char* signature, const WRValue& value ) -{ - if ( value.p2 == WR_INT || value.p2 == WR_FLOAT ) - { - WRValue* C = w->globalRegistry.getAsRawValueHashTable( wr_hashStr(signature) ); - C->p2 = value.p2 | INIT_AS_LIB_CONST; - C->p = value.p; - } -} - -//------------------------------------------------------------------------------ -int WRValue::asInt() const -{ - if ( type == WR_INT ) - { - return i; - } - else if ( type == WR_FLOAT ) - { - return (int)f; - } - - return singleValue().asInt(); -} - -//------------------------------------------------------------------------------ -float WRValue::asFloat() const -{ - if ( type == WR_FLOAT ) - { - return f; - } - else if ( type == WR_INT ) - { - return (float)i; - } - - return singleValue().asFloat(); -} - -//------------------------------------------------------------------------------ -void WRValue::setInt( const int val ) -{ - if ( type == WR_REF ) - { - r->setInt( val ); - } - else - { - p2 = INIT_AS_INT; - i = val; - } -} - -//------------------------------------------------------------------------------ -void WRValue::setFloat( const float val ) -{ - if ( type == WR_REF ) - { - r->setFloat( val ); - } - else - { - p2 = INIT_AS_FLOAT; - f = val; - } -} - -//------------------------------------------------------------------------------ -WRValue* WRValue::indexArray( WRContext* context, const uint32_t index, const bool create ) -{ - if ( !IS_ARRAY(xtype) || va->m_type != SV_VALUE ) - { - if ( !create ) - { - return 0; - } - - p2 = INIT_AS_ARRAY; - va = context->getSVA( index + 1, SV_VALUE, true ); - } - - if ( index >= va->m_size ) - { - if ( !create ) - { - return 0; - } - - va = wr_growValueArray( va, index ); - } - - return va->m_Vdata + index; -} - -//------------------------------------------------------------------------------ -WRValue* WRValue::indexHash( WRContext* context, const uint32_t hash, const bool create ) -{ - if ( !IS_HASH_TABLE(xtype) ) - { - if ( !create ) - { - return 0; - } - - p2 = INIT_AS_HASH_TABLE; - va = context->getSVA( 0, SV_HASH_TABLE, false ); - } - - return create ? (WRValue*)va->get(hash) : va->exists(hash, false, false); -} - -//------------------------------------------------------------------------------ -void* WRValue::array( unsigned int* len, char arrayType ) const -{ - if ( type == WR_REF ) - { - return r->array( len, arrayType ); - } - - if ( (xtype != WR_EX_ARRAY) || (va->m_type != arrayType) ) - { - return 0; - } - - if ( len ) - { - *len = va->m_size; - } - - return va->m_data; -} - -//------------------------------------------------------------------------------ -char* WRValue::asString( char* string, size_t maxLen ) const -{ - if ( type == WR_REF ) - { - return r->asString( string, maxLen ); - } - else if ( type == WR_FLOAT ) - { - wr_ftoa( f, string, maxLen ); - } - else if ( type == WR_INT ) - { - wr_itoa( i, string, maxLen ); - } - else if ( xtype == WR_EX_ARRAY && va->m_type == SV_CHAR ) - { - unsigned int s = 0; - while( (string[s] = va->m_Cdata[s]) ) - { - if ( (s >= va->m_size) || (maxLen && (s >= maxLen)) ) - { - string[s] = '\0'; - break; - } - - ++s; - } - } - else - { - singleValue().asString( string, maxLen ); // never give up, never surrender - } - - return string; -} - -//------------------------------------------------------------------------------ -WRValue* wr_callFunction( WRContext* context, const char* functionName, const WRValue* argv, const int argn ) -{ - return wr_callFunction( context, wr_hashStr(functionName), argv, argn ); -} - -//------------------------------------------------------------------------------ -WRValue* wr_callFunction( WRContext* context, const int32_t hash, const WRValue* argv, const int argn ) -{ - WRValue* cF = 0; - WRState* w = context->w; - - if ( hash ) - { - if ( context->stopLocation == 0 ) - { - w->err = WR_ERR_run_must_be_called_by_itself_first; - return 0; - } - - cF = context->registry.getAsRawValueHashTable( hash ); - if ( !cF->wrf ) - { - w->err = WR_ERR_wrench_function_not_found; - return 0; - } - } - - return wr_callFunction( context, cF ? cF->wrf : 0, argv, argn ); -} - -//------------------------------------------------------------------------------ -WRValue* wr_returnValueFromLastCall( WRState* w ) -{ - return w->stack; // this is where it ends up -} - -//------------------------------------------------------------------------------ -WRFunction* wr_getFunction( WRContext* context, const char* functionName ) -{ - return context->registry.getAsRawValueHashTable(wr_hashStr(functionName))->wrf; -} - -//------------------------------------------------------------------------------ -WRValue* wr_getGlobalRef( WRContext* context, const char* label ) -{ - char globalLabel[64] = "::"; - if ( !label ) - { - return 0; - } - size_t len = strlen(label); - uint32_t match; - if ( len < 3 || (label[0] == ':' && label[1] == ':') ) - { - match = wr_hashStr( label ); - } - else - { - strncpy( globalLabel + 2, label, 61 ); - match = wr_hashStr( globalLabel ); - } - - // grab the globals block if it exists (check hash) - if ( (int)((context->globals + 2) * sizeof(uint32_t)) > context->bottomSize ) - { - return 0; // not enough room for globals to exist - } - - const unsigned char* symbolsBlock = (context->bottom + (context->bottomSize - - (sizeof(uint32_t) // code hash - + sizeof(uint32_t) // symbols hash - + context->globals * sizeof(uint32_t)))); // globals - - uint32_t hash = READ_32_FROM_PC( symbolsBlock + context->globals*sizeof(uint32_t) ); - if ( hash != wr_hash_read8( symbolsBlock, context->globals * sizeof(uint32_t) ) ) - { - return 0; // bad CRC - } - - for( unsigned int i=0; iglobals; ++i ) - { - uint32_t symbolHash = READ_32_FROM_PC(symbolsBlock + i*sizeof(uint32_t)); - if ( match == symbolHash ) - { - return ((WRValue *)(context + 1)) + i; // global space lives immediately past the context - } - } - - return 0; -} - -//------------------------------------------------------------------------------ -WRValue& wr_makeInt( WRValue* val, int i ) -{ - val->p2 = INIT_AS_INT; - val->i = i; - return *val; -} - -//------------------------------------------------------------------------------ -WRValue& wr_makeFloat( WRValue* val, float f ) -{ - val->p2 = INIT_AS_FLOAT; - val->f = f; - return *val; -} - -//------------------------------------------------------------------------------ -WRValue& wr_makeString( WRContext* context, WRValue* val, const char* data, const int len ) -{ - const int slen = len ? len : strlen(data); - val->p2 = INIT_AS_ARRAY; - val->va = context->getSVA( slen, SV_CHAR, false ); - memcpy( (unsigned char *)val->va->m_data, data, slen ); - return *val; -} - -//------------------------------------------------------------------------------ -void wr_makeContainer( WRValue* val, const uint16_t sizeHint ) -{ - val->p2 = INIT_AS_HASH_TABLE; - val->va = (WRGCObject*)malloc( sizeof(WRGCObject) ); - val->va->init( sizeHint, SV_VOID_HASH_TABLE ); - val->va->m_skipGC = 1; -} - -//------------------------------------------------------------------------------ -void wr_addValueToContainer( WRValue* container, const char* name, WRValue* value ) -{ - WRValue* entry = container->va->getAsRawValueHashTable( wr_hashStr(name) ); - entry->r = value; - entry->p2 = INIT_AS_REF; -} - -//------------------------------------------------------------------------------ -void wr_addArrayToContainer( WRValue* container, const char* name, char* array, const uint32_t size ) -{ - assert( size <= 0x1FFFFF ); - - WRValue* entry = container->va->getAsRawValueHashTable( wr_hashStr(name) ); - entry->c = array; - entry->p2 = INIT_AS_RAW_ARRAY | (size<<8); -} - -//------------------------------------------------------------------------------ -void wr_destroyContainer( WRValue* val ) -{ - if ( val->xtype != WR_EX_HASH_TABLE ) - { - return; - } - - val->va->clear(); - free( val->va ); - val->init(); -} - -#ifndef WRENCH_WITHOUT_COMPILER - -//------------------------------------------------------------------------------ -const char* wr_asciiDump( const void* d, unsigned int len, WRstr& str, int markByte ) -{ - const unsigned char* data = (char unsigned *)d; - str.clear(); - for( unsigned int i=0; im_type), 1 ); - serializer.write( (char *)&(temp16 = value.va->m_size), 2 ); - - if ( value.va->m_type == SV_CHAR ) - { - serializer.write( value.va->m_SCdata, value.va->m_size ); - return true; - } - else if ( value.va->m_type == SV_VALUE ) - { - for( uint32_t i=0; im_size; ++i ) - { - if ( !serializeEx(serializer, value.va->m_Vdata[i]) ) - { - return false; - } - } - } - - return true; - } - - case WR_EX_HASH_TABLE: - { - serializer.write( (char *)&(temp16 = value.va->m_mod), 2 ); - - for( uint32_t i=0; im_mod; ++i ) - { - if ( !value.va->m_hashTable[i] ) - { - serializer.write( &(temp = 0), 1 ); - } - - serializer.write( &(temp = 1), 1 ); - - serializeEx( serializer, value.va->m_Vdata[i<<1] ); - serializeEx( serializer, value.va->m_Vdata[(i<<1) + 1] ); - } - - return true; - } - - default: break; - } - } - } - - return false; -} - -//------------------------------------------------------------------------------ -bool deserializeEx( WRValue& value, WRValueSerializer& serializer, WRContext* context ) -{ - char temp; - uint16_t temp16; - - if ( !serializer.read(&temp , 1) ) - { - return false; - } - - value.p2 = (int)temp; - - switch( (uint8_t)temp ) - { - case WR_INT: - case WR_FLOAT: - { - return serializer.read( (char *)&value.ui, 4 ); // bitpattern - } - - case WR_EX: - { - if ( !serializer.read(&temp, 1) ) - { - return false; - } - - switch( (uint8_t)temp ) - { - case WR_EX_ARRAY: - { - if ( !serializer.read(&temp, 1) // type - || !serializer.read((char *)&temp16, 2) ) // size - { - return false; - } - - value.p2 = INIT_AS_ARRAY; - - switch( (uint8_t)temp ) - { - case SV_CHAR: - { - value.va = context->getSVA( temp16, SV_CHAR, false ); - if ( !serializer.read(value.va->m_SCdata, temp16) ) - { - return false; - } - return true; - } - - case SV_VALUE: - { - value.va = context->getSVA( temp16, SV_VALUE, false ); - for( uint16_t i=0; im_Vdata[i], serializer, context) ) - { - return false; - } - } - - return true; - } - } - - break; - } - - case WR_EX_HASH_TABLE: - { - if ( !serializer.read((char *)&temp16, 2) ) // size - { - return false; - } - - value.p2 = INIT_AS_HASH_TABLE; - value.va = context->getSVA( temp16 - 1, SV_HASH_TABLE, false ); - - for( uint16_t i=0; im_Vdata[i<<1].init(); - value.va->m_Vdata[(i<<1) + 1].init(); - value.va->m_hashTable[i] = 0; - } - else - { - if ( !deserializeEx(value.va->m_Vdata[i<<1], serializer, context) - || !deserializeEx(value.va->m_Vdata[(i<<1)+1], serializer, context) ) - { - return false; - } - value.va->m_hashTable[i] = value.va->m_Vdata[(i<<1)+1].getHash() ^ HASH_SCRAMBLER; - } - } - - return true; - } - - default: break; - } - } - } - - return false; -} - -//------------------------------------------------------------------------------ -bool wr_serialize( char** buf, int* len, const WRValue& value ) -{ - WRValueSerializer S; - if ( !serializeEx(S, value) ) - { - return false; - } - - S.getOwnership( buf, len ); - return true; -} - -//------------------------------------------------------------------------------ -bool wr_deserialize( WRContext* context, WRValue& value, const char* buf, const int len ) -{ - WRValueSerializer S( buf, len ); - return deserializeEx( value, S, context ); -} -/******************************************************************************* -Copyright (c) 2023 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - - -//------------------------------------------------------------------------------ -WRDebugClientInterface::WRDebugClientInterface( size_t (*receiveFunction)( char* data, const size_t length, const int timeoutMilliseconds ), - size_t (*writeFunction)( const char* data, const size_t length ) ) -{ - init(); - - m_receiveFunction = receiveFunction; - m_writeFunction = writeFunction; -} - -//------------------------------------------------------------------------------ -WRDebugClientInterface::WRDebugClientInterface( WRDebugServerInterface* localServer ) -{ - init(); - - m_localServer = localServer; -} - -//------------------------------------------------------------------------------ -void WRDebugClientInterface::init() -{ - memset( this, 0, sizeof(*this) ); - - m_packetQ = new SimpleLL(); - m_globals = new SimpleLL(); - m_functions = new SimpleLL(); - m_packet = new WrenchPacket; -} - -//------------------------------------------------------------------------------ -WRDebugClientInterface::~WRDebugClientInterface() -{ - delete m_packetQ; - delete m_globals; - delete m_functions; - delete m_packet; - - delete[] m_sourceBlock; -} - -/* -//------------------------------------------------------------------------------ -const WRDebugMessage& WRDebugClientInterface::status() -{ - if ( m_localServer ) - { - return m_localServer->m_msg; - } - else if ( m_writeFunction && m_receiveFunction ) - { - WrenchPacket packet; - packet.type = RequestStatus; - - char data[1 + (4 * 2)]; - m_msg.type = Err; - - if ( m_writeFunction((char *)&packet, sizeof(WrenchPacket)) == sizeof(WrenchPacket) - && m_receiveFunction(data, 4*2, -1) ) - { - m_msg.type = data[0]; - m_msg.line = READ_32_FROM_PC( (const unsigned char *)(data + 1) ); - m_msg.function = READ_32_FROM_PC( (const unsigned char *)(data + 5) ); - } - } - else - { - m_msg.type = Err; - } - - return m_msg; -} -*/ -//------------------------------------------------------------------------------ -void WRDebugClientInterface::load( const char* byteCode, const int size ) -{ - WrenchPacket packet; - packet.type = Load; - wr_pack32( size, (unsigned char *)&packet.payloadSize ); - if ( size ) - { - packet.payload = (char*)malloc( size ); - memcpy( packet.payload, byteCode, size ); - } - - transmit( packet ); -} - -//------------------------------------------------------------------------------ -void WRDebugClientInterface::run() -{ - WrenchPacket packet; - packet.type = Run; - - transmit( packet ); -} - -//------------------------------------------------------------------------------ -bool WRDebugClientInterface::getSourceCode( const char** data, int* len ) -{ - WrenchPacket packet; - packet.type = RequestSourceBlock; - - if ( !transmit(packet) ) - { - return false; - } - - WrenchPacket* P = receive(); - if ( !P || (P->type != ReplySource) ) - { - return false; - } - - m_sourceBlock = new char[ P->payloadSize ]; - memcpy( m_sourceBlock, P->payload, P->payloadSize ); - *len = P->payloadSize; - *data = m_sourceBlock; - return true; -} - -//------------------------------------------------------------------------------ -uint32_t WRDebugClientInterface::getSourceCodeHash() -{ - WrenchPacket packet; - packet.type = RequestSourceBlock; - - if ( !transmit(packet) ) - { - return 0; - } - - return 1; -} - -//------------------------------------------------------------------------------ -WrenchPacket* WRDebugClientInterface::receive( const int timeoutMilliseconds ) -{ - if ( m_localServer ) - { - WrenchPacket* p = m_localServer->m_packetQ->head(); - if ( p ) - { - *m_packet = *p; - m_localServer->m_packetQ->popHead(); - return m_packet; - } - } - else - { - m_packet->clear(); - - size_t read = m_receiveFunction( (char *)m_packet, sizeof(WrenchPacket), timeoutMilliseconds ); - if ( read == sizeof(WrenchPacket) ) - { - m_packet->xlate(); - if ( m_packet->payloadSize ) - { - m_packet->payload = (char *)malloc( m_packet->payloadSize ); - if ( !m_receiveFunction(m_packet->allocate(m_packet->payloadSize), m_packet->payloadSize, timeoutMilliseconds) ) - { - m_packet->clear(); - return 0; - } - } - - return m_packet; - } - } - - return 0; -} - -//------------------------------------------------------------------------------ -bool WRDebugClientInterface::transmit( WrenchPacket& packet ) -{ - packet.xlate(); - - if ( m_localServer ) - { - m_localServer->processPacket( &packet ); - // memory will be free'd as if it came in off the wire - return true; - } - else if ( m_writeFunction ) - { - size_t xmit = 5 + packet.payloadSize; - bool ret = m_writeFunction( (char*)&packet, xmit ) == xmit; - - // must free any allocated memory if it was allocated - free( packet.payload ); - - return ret; - } - - return false; - -} - -//------------------------------------------------------------------------------ -void WRDebugClientInterface::loadSourceBlock() -{ - if ( m_sourceBlockHash ) - { - return; - } -} - -//------------------------------------------------------------------------------ -void WRDebugClientInterface::loadSymbols() -{ - if ( m_symbolsLoaded ) - { - return; - } -} - -//------------------------------------------------------------------------------ -void WRDebugClientInterface::populateSymbols( const char* block, const int size ) -{ - m_globals->clear(); - m_functions->clear(); - - int pos = 0; - int globals = READ_16_FROM_PC( (const unsigned char *)block ); - pos += 2; - int functions = READ_16_FROM_PC( (const unsigned char *)(block + pos) ); - pos += 2; - - for( int g=0; gaddTail(); - sscanf( block + pos, "%63s", s->label ); - while( block[pos++] ); - } - - for( int f=0; faddTail(); - sscanf( block + pos, "%63s", func->label ); - while( block[pos++] ); - - int locals = (int)block[pos++]; - - for( int l=0; llocals.addTail(); - sscanf( block + pos, "%63s", s->label ); - while( block[pos++] ); - } - } -} -/******************************************************************************* -Copyright (c) 2023 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -//------------------------------------------------------------------------------ -WRDebugServerInterface::WRDebugServerInterface( WRState* w, - const unsigned char* bytes, - const int len, - bool (*receiveFunction)( char* data, const size_t length, const int timeoutMilliseconds ), - size_t (*writeFunction)( const char* data, const size_t length ) ) -{ - memset( this, 0, sizeof(*this) ); - - m_lineBreaks = new SimpleLL(); - m_packetQ = new SimpleLL(); - - m_receiveFunction = receiveFunction; - m_writeFunction = writeFunction; - m_w = w; - - loadBytes( bytes, len ); -} - -//------------------------------------------------------------------------------ -WRDebugServerInterface::~WRDebugServerInterface() -{ - delete m_packetQ; - delete m_lineBreaks; - - free( m_localBytes ); -} - -//------------------------------------------------------------------------------ -WRContext* WRDebugServerInterface::loadBytes( const unsigned char* bytes, const int len ) -{ - if ( m_context ) - { - wr_destroyContext( m_context ); - m_context = 0; - } - - if ( !bytes || !len ) - { - return 0; - } - - m_context = wr_newContext( m_w, bytes, len ); - m_firstCall = true; - - if ( !m_context ) - { - return 0; - } - - int debugBlockOffset = 4 // EOF hash - + (m_context->globals ? (4 + m_context->globals*4) : 0) // globals block plus it's CRC - + 28; // debug block plus it's crc - - if ( debugBlockOffset < m_context->bottomSize ) - { - const unsigned char* data = m_context->bottom + (m_context->bottomSize - debugBlockOffset); - uint32_t hash = wr_hash_read8( data, 24 ); - - //flags = READ_32_FROM_PC( data ); - data += 4; // flags - - m_embeddedSourceSize = READ_32_FROM_PC( data ); - data += 4; - - m_embeddedSourceHash = READ_32_FROM_PC( data ); - data += 4; - - uint32_t offset = READ_32_FROM_PC( data ); - data += 4; - m_embeddedSource = offset ? m_context->bottom + offset : 0; - - m_symbolBlockSize = READ_32_FROM_PC( data ); - data += 4; - - offset = READ_32_FROM_PC( data ); - data += 4; - - m_symbolBlock = offset ? m_context->bottom + offset : 0; - - if ( hash != (uint32_t)READ_32_FROM_PC( data ) ) - { - return 0; - } - } - - return m_context; -} - -/* -//------------------------------------------------------------------------------ -void WRDebugInterface::setBreakpoint( const int lineNumber ) -{ - unsigned char command[5]; - command[0] = SetBreak; - wr_pack32( (int32_t)lineNumber, command + 1 ); - - transmit( command, 5 ); -} - -//------------------------------------------------------------------------------ -bool WRDebugInterface::getSourceCode( WRContext* context, - const char** data, - int* len ) -{ - WrenchDebugBlock block; - if ( !context || !data || !block.populate(context) || !block.sourceCodeSize ) - { - return false; - } - - *data = (const char *)(context->bottom + block.sourceCodeOffset); - if ( len ) - { - *len = block.sourceCodeSize; - } - - return block.sourceCodeSize > 0; -} - -//------------------------------------------------------------------------------ -uint32_t WRDebugInterface::getSourceCodeHash( WRContext* context ) -{ - WrenchDebugBlock block; - return (context && block.populate(context)) ? block.sourceCodeCRC : 0; -} - -//------------------------------------------------------------------------------ -void WRDebugInterface::injectDataFromWrench( const char* data, const int len ) -{ - switch( data[0] ) - { - case SetBreak: - { - int line = READ_32_FROM_PC( data + 1 ); - int first = -1; - for( int i=0; ifirst(); L ; L = m_lineBreaks->next() ) - { - if ( L->line == m_onLine ) - { - m_brk = true; - break; - } - } - - if ( m_lineSteps && !--m_lineSteps ) - { - m_brk = true; - } - - m_brk = true; - - } - else if ( type == FunctionCall ) - { - m_onFunction = codeword & PayloadMask; - - printf( "Calling[%d] @ line[%d] stack[%d]\n", m_onFunction, m_onLine, (int)(stackTop - m_context->w->stack) ); - } - else if ( type == Returned ) - { - printf( "returning @ stack[%d]\n", (int)(stackTop - m_context->w->stack) ); - } -} - -//------------------------------------------------------------------------------ -void WRDebugServerInterface::processPacket( WrenchPacket* packet ) -{ - packet->xlate(); - - WrenchPacket* reply = m_packetQ->addTail(); - - switch( packet->type ) - { - case Run: - { - if ( !m_context ) - { - reply->type = Err; - } - else if ( m_firstCall ) - { - m_firstCall = false; - wr_executeContext( m_context ); - } - else - { - WRFunction f; - f.offset = 0; - wr_callFunction( m_context, &f, 0, 0 ); // signal "continue" - } - - reply->type = Halted; - break; - } - - case Load: - { - int32_t size = packet->payloadSize; - if ( size ) - { - free( m_localBytes ); - m_localBytes = packet->payload; // memory was "malloced" before being sent to us - m_localBytesLen = size; - loadBytes( (unsigned char*)m_localBytes, m_localBytesLen ); - } - else if ( m_context ) - { - loadBytes( m_context->bytes, m_context->bytesLen ); - } - - break; - } - - case RequestSourceBlock: - { - if ( !m_context ) - { - reply->type = Err; - } - else if ( !m_embeddedSourceSize ) - { - reply->type = ReplyUnavailable; - } - else - { - reply->type = ReplySource; - reply->payload = (char *)malloc( m_embeddedSourceSize ); - for( int i=0; ipayload[i] = (unsigned char)READ_8_FROM_PC( m_embeddedSource + i ); - } - } - - break; - } - - case RequestSourceHash: - { - if ( !m_context ) - { - reply->type = Err; - } - else - { - reply->type = ReplySourceHash; - WrenchPacketGenericPayload *p = reply->genericPayload(); - p->hash = m_embeddedSourceHash; - p->xlate(); - } - break; - } - - default: - { - break; - } - } -} -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -//------------------------------------------------------------------------------ -WRGCObject* wr_growValueArray( WRGCObject* va, int newMinIndex ) -{ - int size_of = (va->m_type == SV_CHAR) ? 1 : sizeof(WRValue); - -#ifdef WRENCH_COMPACT - WRGCObject* newArray = (WRGCObject*)malloc( sizeof(WRGCObject) ); - newArray->init( newMinIndex + 1, (WRGCObjectType)va->m_type ); - - newArray->m_next = va->m_next; - va->m_next = newArray; - - int size_el = va->m_size * size_of; - memcpy(newArray->m_Cdata, va->m_Cdata, size_el); - memset(newArray->m_Cdata + size_el, 0, (newArray->m_size * size_of) - size_el); - - return newArray; - -#else - // actually increases the size because this is the only place - // "realloc" is used so that code gets pulled in - va->m_data = realloc( va->m_data, size_of * (newMinIndex+1) ); - - int size_el = va->m_size * size_of; - va->m_size = newMinIndex + 1; - - memset(va->m_Cdata + size_el, 0, (va->m_size * size_of) - size_el); - - return va; -#endif -} - -//------------------------------------------------------------------------------ -WRValue& WRValue::singleValue() const -{ - static WRValue temp; - - if ( (temp = deref()).type > WR_FLOAT ) - { - temp.ui = temp.getHash(); - temp.p2 = INIT_AS_INT; - } - - return temp; -} - -//------------------------------------------------------------------------------ -WRValue& WRValue::deref() const -{ - static WRValue temp; - - if ( type == WR_REF ) - { - return r->deref(); - } - - if ( !IS_ARRAY_MEMBER(xtype) ) - { - return const_cast(*this); - } - - temp.p2 = INIT_AS_INT; - unsigned int s = DECODE_ARRAY_ELEMENT_FROM_P2(p2); - - if ( IS_RAW_ARRAY(r->xtype) ) - { - temp.ui = (s < (uint32_t)(EX_RAW_ARRAY_SIZE_FROM_P2(r->p2))) ? (uint32_t)(unsigned char)(r->c[s]) : 0; - } - else if ( s < r->va->m_size ) - { - if ( r->va->m_type == SV_VALUE ) - { - return r->va->m_Vdata[s]; - } - else - { - temp.ui = (uint32_t)(unsigned char)r->va->m_Cdata[s]; - } - } - - return temp; -} - -//------------------------------------------------------------------------------ -uint32_t WRValue::getHashEx() const -{ - // QUICKLY return the easy answers, thats why this code looks a bit - // convoluted - if ( type == WR_REF ) - { - return r->getHash(); - } - - if ( IS_ARRAY_MEMBER(xtype) ) - { - return deref().getHash(); - } - else if ( xtype == WR_EX_ARRAY && va->m_type == SV_CHAR ) - { - return wr_hash( va->m_Cdata, va->m_size ); - } - - return 0; -} - - -//------------------------------------------------------------------------------ -void wr_valueToArray( const WRValue* array, WRValue* value ) -{ - uint32_t s = DECODE_ARRAY_ELEMENT_FROM_P2(array->p2); - - if ( IS_RAW_ARRAY(array->r->xtype ) ) - { - if ( s < (uint32_t)(EX_RAW_ARRAY_SIZE_FROM_P2(array->r->p2)) ) - { - array->r->c[s] = value->ui; - } - } - else if ( array->r->va->m_type == SV_CHAR) - { - if ( s < array->r->va->m_size ) - { - array->r->va->m_Cdata[s] = value->ui; - } - } - else - { - if ( s >= array->r->va->m_size ) - { - array->r->va = wr_growValueArray(array->r->va, s); - } - - WRValue* V = array->r->va->m_Vdata + s; - wr_assign[(V->type<<2)+value->type](V, value); - } -} - -//------------------------------------------------------------------------------ -void wr_countOfArrayElement( WRValue* array, WRValue* target ) -{ - array = &array->deref(); - - if ( IS_EXARRAY_TYPE(array->xtype) ) - { - target->i = array->va->m_size; - } - else - { - target->i = 0; - } - - target->p2 = INIT_AS_INT; -} - -//------------------------------------------------------------------------------ -// put from into TO as a hash table, if to is not a hash table, make it -// one, leave the result in target -void wr_assignToHashTable( WRContext* c, WRValue* index, WRValue* value, WRValue* table ) -{ - if ( value->type == WR_REF ) - { - wr_assignToHashTable( c, index, value->r, table ); - return; - } - - if ( table->type == WR_REF ) - { - wr_assignToHashTable( c, index, value, table->r ); - return; - } - - if ( index->type == WR_REF ) - { - wr_assignToHashTable( c, index->r, value, table ); - return; - } - - if ( table->xtype != WR_EX_HASH_TABLE ) - { - table->p2 = INIT_AS_HASH_TABLE; - table->va = c->getSVA( 0, SV_HASH_TABLE, false ); - } - - WRValue *entry = (WRValue *)table->va->get( index->getHash() ); - - *entry++ = *value; - *entry = *index; -} - -//------------------------------------------------------------------------------ -bool doLogicalNot_X( WRValue* value ) { return value->i == 0; } // float also uses 0x0000000 as 'zero' -bool doLogicalNot_R( WRValue* value ) { return wr_LogicalNot[ value->r->type ]( value->r ); } -bool doLogicalNot_E( WRValue* value ) -{ - WRValue& V = value->singleValue(); - return wr_LogicalNot[ V.type ]( &V ); -} -WRReturnSingleFunc wr_LogicalNot[4] = -{ - doLogicalNot_X, doLogicalNot_X, doLogicalNot_R, doLogicalNot_E -}; - - -//------------------------------------------------------------------------------ -void doNegate_I( WRValue* value, WRValue* target ) { target->p2 = INIT_AS_INT; target->i = -value->i; } -void doNegate_F( WRValue* value, WRValue* target ) { target->p2 = INIT_AS_FLOAT; target->f = -value->f; } -void doNegate_E( WRValue* value, WRValue* target ) -{ - WRValue& V = value->singleValue(); - return wr_negate[ V.type ]( &V, target ); -} -void doNegate_R( WRValue* value, WRValue* target ) -{ - wr_negate[ value->r->type ]( value->r, target ); -} - -WRSingleTargetFunc wr_negate[4] = -{ - doNegate_I, doNegate_F, doNegate_R, doNegate_E -}; - -//------------------------------------------------------------------------------ -uint32_t doBitwiseNot_I( WRValue* value ) { return ~value->ui; } -uint32_t doBitwiseNot_F( WRValue* value ) { return 0; } -uint32_t doBitwiseNot_R( WRValue* value ) { return wr_bitwiseNot[ value->r->type ]( value->r ); } -uint32_t doBitwiseNot_E( WRValue* value ) -{ - WRValue& V = value->singleValue(); - return wr_bitwiseNot[ V.type ]( &V ); -} -WRUint32Call wr_bitwiseNot[4] = -{ - doBitwiseNot_I, doBitwiseNot_F, doBitwiseNot_R, doBitwiseNot_E -}; - -//------------------------------------------------------------------------------ -void pushIterator_X( WRValue* on, WRValue* to ) { on->init(); } -void pushIterator_R( WRValue* on, WRValue* to ) { wr_pushIterator[on->r->type]( on->r, to ); } -void pushIterator_E( WRValue* on, WRValue* to ) -{ - if ( on->xtype == WR_EX_ARRAY || on->xtype == WR_EX_HASH_TABLE ) - { - to->va = on->va; - to->p2 = INIT_AS_ITERATOR; - } -} - -WRVoidFunc wr_pushIterator[4] = -{ - pushIterator_X, pushIterator_X, pushIterator_R, pushIterator_E -}; - -//------------------------------------------------------------------------------ -void doAssign_X_E( WRValue* to, WRValue* from ) -{ - if ( IS_ARRAY_MEMBER(from->xtype) ) - { - WRValue& V = from->deref(); - wr_assign[(WR_EX<<2)+V.type](to, &V); - } - else - { - *to = *from; - } -} - -void doAssign_E_X( WRValue* to, WRValue* from ) -{ - if ( IS_ARRAY_MEMBER(to->xtype) ) - { - wr_valueToArray( to, from ); - } - else - { - *to = *from; - } -} - -void doAssign_E_E( WRValue* to, WRValue* from ) -{ - if ( IS_ARRAY_MEMBER(from->xtype) ) - { - WRValue& V = from->deref(); - wr_assign[(WR_EX<<2)+V.type](to, &V); - return; - } - else if ( IS_ARRAY_MEMBER(to->xtype) - && IS_EXARRAY_TYPE(to->r->xtype) - && (to->r->va->m_type == SV_VALUE) ) - { - unsigned int index = DECODE_ARRAY_ELEMENT_FROM_P2(to->p2); - - if ( index > to->r->va->m_size ) - { - if ( to->r->va->m_skipGC ) - { - return; - } - - to->r->va = wr_growValueArray( to->r->va, index ); - } - - to->r->va->m_Vdata[index] = *from; - return; - } - - *to = *from; -} - - -//================================================================================== -//================================================================================== -//================================================================================== -//================================================================================== - -#ifdef WRENCH_COMPACT - - -void doAssign_R_R( WRValue* to, WRValue* from ) { wr_assign[(to->r->type<<2)|from->r->type](to->r, from->r); } -void doAssign_R_X( WRValue* to, WRValue* from ) { wr_assign[(to->r->type<<2)|from->type](to->r, from); } -void doAssign_X_R( WRValue* to, WRValue* from ) { wr_assign[(to->type<<2)|from->r->type](to, from->r); } -void doAssign_X_X( WRValue* to, WRValue* from ) { *to = *from; } -WRVoidFunc wr_assign[16] = -{ - doAssign_X_X, doAssign_X_X, doAssign_X_R, doAssign_X_E, - doAssign_X_X, doAssign_X_X, doAssign_X_R, doAssign_X_E, - doAssign_R_X, doAssign_R_X, doAssign_R_R, doAssign_R_X, - doAssign_E_X, doAssign_E_X, doAssign_X_R, doAssign_E_E, -}; - - -extern bool CompareEQI( int a, int b ); -extern bool CompareEQF( float a, float b ); - -//------------------------------------------------------------------------------ -void unaryPost_E( WRValue* value, WRValue* stack, int add ) -{ - WRValue& V = value->singleValue(); - WRValue temp; - m_unaryPost[ V.type ]( &V, &temp, add ); - wr_valueToArray( value, &V ); - *stack = temp; -} -void unaryPost_I( WRValue* value, WRValue* stack, int add ) { stack->p2 = INIT_AS_INT; stack->i = value->i; value->i += add; } -void unaryPost_R( WRValue* value, WRValue* stack, int add ) { m_unaryPost[ value->r->type ]( value->r, stack, add ); } -void unaryPost_F( WRValue* value, WRValue* stack, int add ) { stack->p2 = INIT_AS_FLOAT; stack->f = value->f; value->f += add; } -WRVoidPlusFunc m_unaryPost[4] = -{ - unaryPost_I, unaryPost_F, unaryPost_R, unaryPost_E -}; - -void FuncAssign_R_E( WRValue* to, WRValue* from, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - wr_FuncAssign[(to->r->type<<2)|WR_EX](to->r, from, intCall, floatCall); -} -void FuncAssign_E_X( WRValue* to, WRValue* from, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - WRValue& V = to->singleValue(); - - wr_FuncAssign[(V.type<<2)|from->type]( &V, from, intCall, floatCall ); - - wr_valueToArray( to, &V ); - *from = V; -} -void FuncAssign_E_E( WRValue* to, WRValue* from, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - if ( IS_ARRAY_MEMBER(from->xtype) ) - { - WRValue& V = from->deref(); - - wr_FuncAssign[(WR_EX<<2)|V.type]( to, &V, intCall, floatCall ); - *from = to->deref(); - } - else if ( intCall == wr_addI - && IS_ARRAY(to->xtype) - && to->va->m_type == SV_CHAR - && !to->va->m_skipGC - && IS_ARRAY(from->xtype) - && from->va->m_type == SV_CHAR ) - { - char* t = to->va->m_SCdata; - to->va->m_SCdata = (char*)malloc( to->va->m_size + from->va->m_size + 1 ); - memcpy( to->va->m_SCdata, t, to->va->m_size ); - memcpy( to->va->m_SCdata + to->va->m_size, from->va->m_SCdata, from->va->m_size + 1 ); - to->va->m_size = to->va->m_size + from->va->m_size; - free( t ); - } -} -void FuncAssign_X_E( WRValue* to, WRValue* from, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - WRValue& V = from->singleValue(); - wr_FuncAssign[(to->type<<2)|V.type](to, &V, intCall, floatCall); - *from = *to; -} -void FuncAssign_E_R( WRValue* to, WRValue* from, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - wr_FuncAssign[(WR_EX<<2)|from->r->type](to, from->r, intCall, floatCall); -} - -void FuncAssign_R_R( WRValue* to, WRValue* from, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - WRValue temp = *from->r; wr_FuncAssign[(to->r->type<<2)|temp.type](to->r, &temp, intCall, floatCall); *from = *to->r; -} - -void FuncAssign_R_X( WRValue* to, WRValue* from, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - wr_FuncAssign[(to->r->type<<2)|from->type](to->r, from, intCall, floatCall); *from = *to->r; -} - -void FuncAssign_X_R( WRValue* to, WRValue* from, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - wr_FuncAssign[(to->type<<2)+from->r->type](to, from->r, intCall, floatCall); *from = *to; -} - -void FuncAssign_F_F( WRValue* to, WRValue* from, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - to->f = floatCall( to->f, from->f ); -} - -void FuncAssign_I_I( WRValue* to, WRValue* from, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - to->i = intCall( to->i, from->i ); -} - -void FuncAssign_I_F( WRValue* to, WRValue* from, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - to->p2 = INIT_AS_FLOAT; - to->f = floatCall( (float)to->i, from->f ); -} -void FuncAssign_F_I( WRValue* to, WRValue* from, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - to->f = floatCall( to->f, (float)from->i ); -} - -WRFuncAssignFunc wr_FuncAssign[16] = -{ - FuncAssign_I_I, FuncAssign_I_F, FuncAssign_X_R, FuncAssign_X_E, - FuncAssign_F_I, FuncAssign_F_F, FuncAssign_X_R, FuncAssign_X_E, - FuncAssign_R_X, FuncAssign_R_X, FuncAssign_R_R, FuncAssign_R_E, - FuncAssign_E_X, FuncAssign_E_X, FuncAssign_E_R, FuncAssign_E_E, -}; - - -//------------------------------------------------------------------------------ -void FuncBinary_E_X( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - WRValue& V = to->singleValue(); - wr_funcBinary[(V.type<<2)|from->type](&V, from, target, intCall, floatCall); -} -void FuncBinary_E_E( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - if ( IS_ARRAY_MEMBER(to->xtype) && IS_ARRAY_MEMBER(from->xtype) ) - { - WRValue V1 = to->deref(); - WRValue& V2 = from->deref(); - wr_funcBinary[(V1.type<<2)|V2.type](&V1, &V2, target, intCall, floatCall ); - } - else if ( intCall == wr_addI - && IS_ARRAY(to->xtype) - && to->va->m_type == SV_CHAR - && !to->va->m_skipGC - && IS_ARRAY(from->xtype) - && from->va->m_type == SV_CHAR ) - { - target->p2 = INIT_AS_ARRAY; - target->va = from->va->m_creatorContext->getSVA( from->va->m_size + to->va->m_size, SV_CHAR, false ); - memcpy( target->va->m_SCdata, from->va->m_SCdata, from->va->m_size ); - memcpy( target->va->m_SCdata + from->va->m_size, to->va->m_SCdata, to->va->m_size ); - } -} -void FuncBinary_X_E( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - WRValue& V = from->singleValue(); - wr_funcBinary[(to->type<<2)|V.type](to, &V, target, intCall, floatCall); -} -void FuncBinary_E_R( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - wr_funcBinary[(WR_EX<<2)|from->r->type](to, from->r, target, intCall, floatCall ); -} - -void FuncBinary_R_E( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - wr_funcBinary[(to->r->type<<2)|WR_EX](to->r, from, target, intCall, floatCall ); -} - -void FuncBinary_X_R( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - wr_funcBinary[(to->type<<2)+from->r->type](to, from->r, target, intCall, floatCall); -} - -void FuncBinary_R_X( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - wr_funcBinary[(to->r->type<<2)|from->type](to->r, from, target, intCall, floatCall); -} - -void FuncBinary_R_R( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - wr_funcBinary[(to->r->type<<2)|from->r->type](to->r, from->r, target, intCall, floatCall); -} - -void FuncBinary_I_I( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - target->p2 = INIT_AS_INT; target->i = intCall( to->i, from->i ); -} - -void FuncBinary_I_F( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - target->p2 = INIT_AS_FLOAT; target->f = floatCall( (float)to->i, from->f ); -} - -void FuncBinary_F_I( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - target->p2 = INIT_AS_FLOAT; target->f = floatCall( to->f, (float)from->i ); -} - -void FuncBinary_F_F( WRValue* to, WRValue* from, WRValue* target, WRFuncIntCall intCall, WRFuncFloatCall floatCall ) -{ - target->p2 = INIT_AS_FLOAT; target->f = floatCall( to->f, from->f ); -} - -WRTargetCallbackFunc wr_funcBinary[16] = -{ - FuncBinary_I_I, FuncBinary_I_F, FuncBinary_X_R, FuncBinary_X_E, - FuncBinary_F_I, FuncBinary_F_F, FuncBinary_X_R, FuncBinary_X_E, - FuncBinary_R_X, FuncBinary_R_X, FuncBinary_R_R, FuncBinary_R_E, - FuncBinary_E_X, FuncBinary_E_X, FuncBinary_E_R, FuncBinary_E_E, -}; - - - -bool Compare_E_E( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ) -{ - WRValue V1 = to->singleValue(); - WRValue& V2 = from->singleValue(); - return wr_Compare[(V1.type<<2)|V2.type](&V1, &V2, intCall, floatCall); -} -bool Compare_E_X( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ) -{ - WRValue& V = to->singleValue(); - return wr_Compare[(V.type<<2)|from->type](&V, from, intCall, floatCall); -} -bool Compare_X_E( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ) -{ - WRValue& V = from->singleValue(); - return wr_Compare[(to->type<<2)|V.type](to, &V, intCall, floatCall ); -} -bool Compare_R_E( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ) { return wr_Compare[(to->r->type<<2)|WR_EX](to->r, from, intCall, floatCall); } -bool Compare_E_R( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ) { return wr_Compare[(WR_EX<<2)|from->r->type](to, from->r, intCall, floatCall); } -bool Compare_R_R( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ) { return wr_Compare[(to->r->type<<2)|from->r->type](to->r, from->r, intCall, floatCall); } -bool Compare_R_X( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ) { return wr_Compare[(to->r->type<<2)|from->type](to->r, from, intCall, floatCall); } -bool Compare_X_R( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ) { return wr_Compare[(to->type<<2)+from->r->type](to, from->r, intCall, floatCall); } -bool Compare_I_I( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ) { return intCall( to->i, from->i ); } -bool Compare_I_F( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ) { return floatCall( (float)to->i, from->f); } -bool Compare_F_I( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ) { return floatCall( to->f, (float)from->i); } -bool Compare_F_F( WRValue* to, WRValue* from, WRCompareFuncIntCall intCall, WRCompareFuncFloatCall floatCall ) { return floatCall( to->f, from->f); } -WRBoolCallbackReturnFunc wr_Compare[16] = -{ - Compare_I_I, Compare_I_F, Compare_X_R, Compare_X_E, - Compare_F_I, Compare_F_F, Compare_X_R, Compare_X_E, - Compare_R_X, Compare_R_X, Compare_R_R, Compare_R_E, - Compare_E_X, Compare_E_X, Compare_E_R, Compare_E_E, -}; - -//================================================================================== -//================================================================================== -//================================================================================== -//================================================================================== -#else - -// NOT COMPACT - -void doAssign_R_E( WRValue* to, WRValue* from ) { wr_assign[(to->r->type<<2)|WR_EX](to->r, from); } -void doAssign_R_R( WRValue* to, WRValue* from ) { wr_assign[(to->r->type<<2)|from->r->type](to->r, from->r); } -void doAssign_E_R( WRValue* to, WRValue* from ) { wr_assign[(WR_EX<<2)|from->r->type](to, from->r); } -void doAssign_R_I( WRValue* to, WRValue* from ) { wr_assign[(to->r->type<<2)|WR_INT](to->r, from); } -void doAssign_R_F( WRValue* to, WRValue* from ) { wr_assign[(to->r->type<<2)|WR_FLOAT](to->r, from); } -void doAssign_I_R( WRValue* to, WRValue* from ) { wr_assign[(WR_INT<<2)|from->r->type](to, from->r); } -void doAssign_F_R( WRValue* to, WRValue* from ) { wr_assign[(WR_FLOAT<<2)|from->r->type](to, from->r); } -void doAssign_X_X( WRValue* to, WRValue* from ) { *to = *from; } -WRVoidFunc wr_assign[16] = -{ - doAssign_X_X, doAssign_X_X, doAssign_I_R, doAssign_X_E, - doAssign_X_X, doAssign_X_X, doAssign_F_R, doAssign_X_E, - doAssign_R_I, doAssign_R_F, doAssign_R_R, doAssign_R_E, - doAssign_E_X, doAssign_E_X, doAssign_E_R, doAssign_E_E, -}; - - -void doVoidFuncBlank( WRValue* to, WRValue* from ) {} - -#define X_INT_ASSIGN( NAME, OPERATION ) \ -void NAME##Assign_E_R( WRValue* to, WRValue* from )\ -{\ - NAME##Assign[(WR_EX<<2)|from->r->type]( to, from->r );\ -}\ -void NAME##Assign_R_E( WRValue* to, WRValue* from ) \ -{\ - NAME##Assign[(to->r->type<<2)|WR_EX](to->r, from);\ -}\ -void NAME##Assign_E_I( WRValue* to, WRValue* from )\ -{\ - WRValue& V = to->singleValue();\ - \ - NAME##Assign[(V.type<<2)|WR_INT]( &V, from );\ - \ - wr_valueToArray( to, &V );\ - *from = V;\ -}\ -void NAME##Assign_E_E( WRValue* to, WRValue* from ) \ -{\ - WRValue& V = from->singleValue();\ - \ - NAME##Assign[(WR_EX<<2)+V.type]( to, &V );\ - *from = to->deref();\ -}\ -void NAME##Assign_I_E( WRValue* to, WRValue* from )\ -{\ - WRValue& V = from->singleValue();\ - NAME##Assign[(WR_INT<<2)+V.type](to, &V);\ - *from = *to;\ -}\ -void NAME##Assign_R_R( WRValue* to, WRValue* from ) { WRValue temp = *from->r; NAME##Assign[(to->r->type<<2)|temp.type](to->r, &temp); *from = *to->r; }\ -void NAME##Assign_R_I( WRValue* to, WRValue* from ) { NAME##Assign[(to->r->type<<2)|WR_INT](to->r, from); *from = *to->r; }\ -void NAME##Assign_I_R( WRValue* to, WRValue* from ) { WRValue temp = *from->r; NAME##Assign[(WR_INT<<2)+temp.type](to, &temp); *from = *to; }\ -void NAME##Assign_I_I( WRValue* to, WRValue* from ) { to->i OPERATION##= from->i; }\ -WRVoidFunc NAME##Assign[16] = \ -{\ - NAME##Assign_I_I, doVoidFuncBlank, NAME##Assign_I_R, NAME##Assign_I_E,\ - doVoidFuncBlank, doVoidFuncBlank, doVoidFuncBlank, doVoidFuncBlank,\ - NAME##Assign_R_I, doVoidFuncBlank, NAME##Assign_R_R, NAME##Assign_R_E,\ - NAME##Assign_E_I, doVoidFuncBlank, NAME##Assign_E_R, NAME##Assign_E_E,\ -};\ - - -X_INT_ASSIGN( wr_Mod, % ); -X_INT_ASSIGN( wr_OR, | ); -X_INT_ASSIGN( wr_AND, & ); -X_INT_ASSIGN( wr_XOR, ^ ); -X_INT_ASSIGN( wr_RightShift, >> ); -X_INT_ASSIGN( wr_LeftShift, << ); - - -#define X_ASSIGN( NAME, OPERATION ) \ -void NAME##Assign_E_I( WRValue* to, WRValue* from )\ -{\ - WRValue& V = to->singleValue();\ - \ - NAME##Assign[(V.type<<2)|WR_INT]( &V, from );\ - \ - wr_valueToArray( to, &V );\ - *from = V;\ -}\ -void NAME##Assign_E_F( WRValue* to, WRValue* from )\ -{\ - WRValue& V = to->singleValue();\ - \ - NAME##Assign[(V.type<<2)|WR_FLOAT]( &V, from );\ - \ - wr_valueToArray( to, &V );\ - *from = V;\ -}\ -void NAME##Assign_E_E( WRValue* to, WRValue* from ) \ -{\ - WRValue& V = from->singleValue();\ - \ - NAME##Assign[(WR_EX<<2)|V.type]( to, &V );\ - *from = to->deref();\ -}\ -void NAME##Assign_I_E( WRValue* to, WRValue* from )\ -{\ - WRValue& V = from->singleValue();\ - NAME##Assign[(WR_INT<<2)|V.type](to, &V);\ - *from = *to;\ -}\ -void NAME##Assign_F_E( WRValue* to, WRValue* from )\ -{\ - WRValue& V = from->singleValue();\ - NAME##Assign[(WR_FLOAT<<2)|V.type](to, &V);\ - *from = *to;\ -}\ -void NAME##Assign_E_R( WRValue* to, WRValue* from )\ -{\ - NAME##Assign[(WR_EX<<2)|from->r->type]( to, from->r );\ -}\ -void NAME##Assign_R_E( WRValue* to, WRValue* from ) { NAME##Assign[(to->r->type<<2)|WR_EX](to->r, from); *from = *to->r; } \ -void NAME##Assign_R_R( WRValue* to, WRValue* from ) { WRValue temp = *from->r; NAME##Assign[(to->r->type<<2)|temp.type](to->r, &temp); *from = *to->r; }\ -void NAME##Assign_R_I( WRValue* to, WRValue* from ) { NAME##Assign[(to->r->type<<2)|WR_INT](to->r, from); *from = *to->r; }\ -void NAME##Assign_R_F( WRValue* to, WRValue* from ) { NAME##Assign[(to->r->type<<2)|WR_FLOAT](to->r, from); *from = *to->r; }\ -void NAME##Assign_I_R( WRValue* to, WRValue* from ) { NAME##Assign[(WR_INT<<2)+from->r->type](to, from->r); *from = *to; }\ -void NAME##Assign_F_R( WRValue* to, WRValue* from ) { NAME##Assign[(WR_FLOAT<<2)+from->r->type](to, from->r); *from = *to; }\ -void NAME##Assign_F_F( WRValue* to, WRValue* from ) { to->f OPERATION##= from->f; }\ -void NAME##Assign_I_I( WRValue* to, WRValue* from ) { to->i OPERATION##= from->i; }\ -void NAME##Assign_I_F( WRValue* to, WRValue* from ) { to->p2 = INIT_AS_FLOAT; to->f = (float)to->i OPERATION from->f; }\ -void NAME##Assign_F_I( WRValue* to, WRValue* from ) { from->p2 = INIT_AS_FLOAT; to->f OPERATION##= (float)from->i; }\ -WRVoidFunc NAME##Assign[16] = \ -{\ - NAME##Assign_I_I, NAME##Assign_I_F, NAME##Assign_I_R, NAME##Assign_I_E,\ - NAME##Assign_F_I, NAME##Assign_F_F, NAME##Assign_F_R, NAME##Assign_F_E,\ - NAME##Assign_R_I, NAME##Assign_R_F, NAME##Assign_R_R, NAME##Assign_R_E,\ - NAME##Assign_E_I, NAME##Assign_E_F, NAME##Assign_E_R, NAME##Assign_E_E,\ -};\ - - -X_ASSIGN( wr_Subtract, - ); -//X_ASSIGN( wr_Add, + ); -X_ASSIGN( wr_Multiply, * ); -X_ASSIGN( wr_Divide, / ); - - - - - -void wr_AddAssign_E_I( WRValue* to, WRValue* from ) -{ - WRValue& V = to->singleValue(); - - wr_AddAssign[(V.type<<2)|WR_INT]( &V, from ); - - wr_valueToArray( to, &V ); - *from = V; -} - -void wr_AddAssign_E_F( WRValue* to, WRValue* from ) -{ - WRValue& V = to->singleValue(); - - wr_AddAssign[(V.type<<2)|WR_FLOAT]( &V, from ); - - wr_valueToArray( to, &V ); - *from = V; -} -void wr_AddAssign_E_E( WRValue* to, WRValue* from ) -{ - if ( IS_ARRAY_MEMBER(from->xtype) ) - { - WRValue& V = from->deref(); - - wr_AddAssign[(WR_EX<<2)|V.type]( to, &V ); - *from = to->deref(); - } - else if ( IS_ARRAY(to->xtype) - && to->va->m_type == SV_CHAR - && !to->va->m_skipGC - && IS_ARRAY(from->xtype) - && from->va->m_type == SV_CHAR ) - { - char* t = to->va->m_SCdata; - to->va->m_SCdata = (char*)malloc( to->va->m_size + from->va->m_size + 1 ); - memcpy( to->va->m_SCdata, t, to->va->m_size ); - memcpy( to->va->m_SCdata + to->va->m_size, from->va->m_SCdata, from->va->m_size + 1 ); - to->va->m_size = to->va->m_size + from->va->m_size; - free( t ); - } -} -void wr_AddAssign_I_E( WRValue* to, WRValue* from ) -{ - WRValue& V = from->singleValue(); - wr_AddAssign[(WR_INT<<2)|V.type](to, &V); - *from = *to; -} -void wr_AddAssign_F_E( WRValue* to, WRValue* from ) -{ - WRValue& V = from->singleValue(); - wr_AddAssign[(WR_FLOAT<<2)|V.type](to, &V); - *from = *to; -} -void wr_AddAssign_E_R( WRValue* to, WRValue* from ) -{ - wr_AddAssign[(WR_EX<<2)|from->r->type]( to, from ); -} -void wr_AddAssign_R_E( WRValue* to, WRValue* from ) { wr_AddAssign[(to->r->type<<2)|WR_EX](to->r, from); *from = *to->r; } -void wr_AddAssign_R_R( WRValue* to, WRValue* from ) { WRValue temp = *from->r; wr_AddAssign[(to->r->type<<2)|temp.type](to->r, &temp); *from = *to->r; } -void wr_AddAssign_R_I( WRValue* to, WRValue* from ) { wr_AddAssign[(to->r->type<<2)|WR_INT](to->r, from); *from = *to->r; } -void wr_AddAssign_R_F( WRValue* to, WRValue* from ) { wr_AddAssign[(to->r->type<<2)|WR_FLOAT](to->r, from); *from = *to->r; } -void wr_AddAssign_I_R( WRValue* to, WRValue* from ) { wr_AddAssign[(WR_INT<<2)+from->r->type](to, from->r); *from = *to; } -void wr_AddAssign_F_R( WRValue* to, WRValue* from ) { wr_AddAssign[(WR_FLOAT<<2)+from->r->type](to, from->r); *from = *to; } -void wr_AddAssign_F_F( WRValue* to, WRValue* from ) { to->f += from->f; } -void wr_AddAssign_I_I( WRValue* to, WRValue* from ) { to->i += from->i; } -void wr_AddAssign_I_F( WRValue* to, WRValue* from ) { to->p2 = INIT_AS_FLOAT; to->f = (float)to->i + from->f; } -void wr_AddAssign_F_I( WRValue* to, WRValue* from ) { from->p2 = INIT_AS_FLOAT; to->f += (float)from->i; } -WRVoidFunc wr_AddAssign[16] = -{ - wr_AddAssign_I_I, wr_AddAssign_I_F, wr_AddAssign_I_R, wr_AddAssign_I_E, - wr_AddAssign_F_I, wr_AddAssign_F_F, wr_AddAssign_F_R, wr_AddAssign_F_E, - wr_AddAssign_R_I, wr_AddAssign_R_F, wr_AddAssign_R_R, wr_AddAssign_R_E, - wr_AddAssign_E_I, wr_AddAssign_E_F, wr_AddAssign_E_R, wr_AddAssign_E_E, -}; - - -//------------------------------------------------------------------------------ -#define X_BINARY( NAME, OPERATION ) \ -void NAME##Binary_E_I( WRValue* to, WRValue* from, WRValue* target )\ -{\ - WRValue& V = to->singleValue();\ - NAME##Binary[(V.type<<2)|WR_INT](&V, from, target);\ -}\ -void NAME##Binary_E_F( WRValue* to, WRValue* from, WRValue* target )\ -{\ - WRValue& V = to->singleValue();\ - NAME##Binary[(V.type<<2)|WR_FLOAT](&V, from, target);\ -}\ -void NAME##Binary_E_E( WRValue* to, WRValue* from, WRValue* target )\ -{\ - WRValue V1 = to->singleValue();\ - WRValue& V2 = from->singleValue();\ - NAME##Binary[(V1.type<<2)|V2.type](&V1, &V2, target);\ -}\ -void NAME##Binary_I_E( WRValue* to, WRValue* from, WRValue* target )\ -{\ - WRValue& V = from->singleValue();\ - NAME##Binary[(WR_INT<<2)|V.type](to, &V, target);\ -}\ -void NAME##Binary_F_E( WRValue* to, WRValue* from, WRValue* target )\ -{\ - WRValue& V = from->singleValue();\ - NAME##Binary[(WR_FLOAT<<2)|V.type](to, &V, target);\ -}\ -void NAME##Binary_R_E( WRValue* to, WRValue* from, WRValue* target ) { NAME##Binary[(to->r->type<<2)|WR_EX]( to->r, from, target); }\ -void NAME##Binary_E_R( WRValue* to, WRValue* from, WRValue* target ) { NAME##Binary[(WR_EX<<2)+from->r->type](to, from->r, target); }\ -void NAME##Binary_I_R( WRValue* to, WRValue* from, WRValue* target ) { NAME##Binary[(WR_INT<<2)+from->r->type](to, from->r, target); }\ -void NAME##Binary_R_F( WRValue* to, WRValue* from, WRValue* target ) { NAME##Binary[(to->r->type<<2)|WR_FLOAT](to->r, from, target); }\ -void NAME##Binary_R_R( WRValue* to, WRValue* from, WRValue* target ) { NAME##Binary[(to->r->type<<2)|from->r->type](to->r, from->r, target); }\ -void NAME##Binary_R_I( WRValue* to, WRValue* from, WRValue* target ) { NAME##Binary[(to->r->type<<2)|WR_INT](to->r, from, target); }\ -void NAME##Binary_F_R( WRValue* to, WRValue* from, WRValue* target ) { NAME##Binary[(WR_FLOAT<<2)+from->r->type](to, from->r, target); }\ -void NAME##Binary_I_I( WRValue* to, WRValue* from, WRValue* target ) { target->p2 = INIT_AS_INT; target->i = to->i OPERATION from->i; }\ -void NAME##Binary_I_F( WRValue* to, WRValue* from, WRValue* target ) { target->p2 = INIT_AS_FLOAT; target->f = (float)to->i OPERATION from->f; }\ -void NAME##Binary_F_I( WRValue* to, WRValue* from, WRValue* target ) { target->p2 = INIT_AS_FLOAT; target->f = to->f OPERATION (float)from->i; }\ -void NAME##Binary_F_F( WRValue* to, WRValue* from, WRValue* target ) { target->p2 = INIT_AS_FLOAT; target->f = to->f OPERATION from->f; }\ -WRTargetFunc NAME##Binary[16] = \ -{\ - NAME##Binary_I_I, NAME##Binary_I_F, NAME##Binary_I_R, NAME##Binary_I_E,\ - NAME##Binary_F_I, NAME##Binary_F_F, NAME##Binary_F_R, NAME##Binary_F_E,\ - NAME##Binary_R_I, NAME##Binary_R_F, NAME##Binary_R_R, NAME##Binary_R_E,\ - NAME##Binary_E_I, NAME##Binary_E_F, NAME##Binary_E_R, NAME##Binary_E_E,\ -};\ - -//X_BINARY( wr_Addition, + ); -- broken out so strings work -X_BINARY( wr_Multiply, * ); -X_BINARY( wr_Subtract, - ); -X_BINARY( wr_Divide, / ); - - - -void wr_AdditionBinary_E_I( WRValue* to, WRValue* from, WRValue* target ) -{ - WRValue& V = to->singleValue(); - wr_AdditionBinary[(V.type<<2)|WR_INT](&V, from, target); -} -void wr_AdditionBinary_E_F( WRValue* to, WRValue* from, WRValue* target ) -{ - WRValue& V = to->singleValue(); - wr_AdditionBinary[(V.type<<2)|WR_FLOAT](&V, from, target); -} -void wr_AdditionBinary_E_E( WRValue* to, WRValue* from, WRValue* target ) -{ - if ( IS_ARRAY_MEMBER(to->xtype) && IS_ARRAY_MEMBER(from->xtype) ) - { - WRValue V1 = to->deref(); - WRValue& V2 = from->deref(); - wr_AdditionBinary[(V1.type<<2)|V2.type](&V1, &V2, target); - } - else if ( IS_ARRAY(to->xtype) - && to->va->m_type == SV_CHAR - && !to->va->m_skipGC - && IS_ARRAY(from->xtype) - && from->va->m_type == SV_CHAR ) - { - target->p2 = INIT_AS_ARRAY; - target->va = from->va->m_creatorContext->getSVA( from->va->m_size + to->va->m_size, SV_CHAR, false ); - memcpy( target->va->m_SCdata, from->va->m_SCdata, from->va->m_size ); - memcpy( target->va->m_SCdata + from->va->m_size, to->va->m_SCdata, to->va->m_size ); - } -} -void wr_AdditionBinary_I_E( WRValue* to, WRValue* from, WRValue* target ) -{ - WRValue& V = from->singleValue(); - wr_AdditionBinary[(WR_INT<<2)|V.type](to, &V, target); -} -void wr_AdditionBinary_F_E( WRValue* to, WRValue* from, WRValue* target ) -{ - WRValue& V = from->singleValue(); - wr_AdditionBinary[(WR_FLOAT<<2)|V.type](to, &V, target); -} -void wr_AdditionBinary_R_E( WRValue* to, WRValue* from, WRValue* target ) { wr_AdditionBinary[(to->r->type<<2)|WR_EX]( to->r, from, target); } -void wr_AdditionBinary_E_R( WRValue* to, WRValue* from, WRValue* target ) { wr_AdditionBinary[(WR_EX<<2)+from->r->type](to, from->r, target); } -void wr_AdditionBinary_I_R( WRValue* to, WRValue* from, WRValue* target ) { wr_AdditionBinary[(WR_INT<<2)+from->r->type](to, from->r, target); } -void wr_AdditionBinary_R_F( WRValue* to, WRValue* from, WRValue* target ) { wr_AdditionBinary[(to->r->type<<2)|WR_FLOAT](to->r, from, target); } -void wr_AdditionBinary_R_R( WRValue* to, WRValue* from, WRValue* target ) { wr_AdditionBinary[(to->r->type<<2)|from->r->type](to->r, from->r, target); } -void wr_AdditionBinary_R_I( WRValue* to, WRValue* from, WRValue* target ) { wr_AdditionBinary[(to->r->type<<2)|WR_INT](to->r, from, target); } -void wr_AdditionBinary_F_R( WRValue* to, WRValue* from, WRValue* target ) { wr_AdditionBinary[(WR_FLOAT<<2)+from->r->type](to, from->r, target); } -void wr_AdditionBinary_I_I( WRValue* to, WRValue* from, WRValue* target ) { target->p2 = INIT_AS_INT; target->i = to->i + from->i; } -void wr_AdditionBinary_I_F( WRValue* to, WRValue* from, WRValue* target ) { target->p2 = INIT_AS_FLOAT; target->f = (float)to->i + from->f; } -void wr_AdditionBinary_F_I( WRValue* to, WRValue* from, WRValue* target ) { target->p2 = INIT_AS_FLOAT; target->f = to->f + (float)from->i; } -void wr_AdditionBinary_F_F( WRValue* to, WRValue* from, WRValue* target ) { target->p2 = INIT_AS_FLOAT; target->f = to->f + from->f; } -WRTargetFunc wr_AdditionBinary[16] = -{ - wr_AdditionBinary_I_I, wr_AdditionBinary_I_F, wr_AdditionBinary_I_R, wr_AdditionBinary_I_E, - wr_AdditionBinary_F_I, wr_AdditionBinary_F_F, wr_AdditionBinary_F_R, wr_AdditionBinary_F_E, - wr_AdditionBinary_R_I, wr_AdditionBinary_R_F, wr_AdditionBinary_R_R, wr_AdditionBinary_R_E, - wr_AdditionBinary_E_I, wr_AdditionBinary_E_F, wr_AdditionBinary_E_R, wr_AdditionBinary_E_E, -}; - - - - -void doTargetFuncBlank( WRValue* to, WRValue* from, WRValue* target ) {} - -//------------------------------------------------------------------------------ -#define X_INT_BINARY( NAME, OPERATION ) \ -void NAME##Binary_E_I( WRValue* to, WRValue* from, WRValue* target )\ -{\ - WRValue& V = to->singleValue();\ - NAME##Binary[(V.type<<2)|WR_INT](&V, from, target);\ -}\ -void NAME##Binary_E_E( WRValue* to, WRValue* from, WRValue* target )\ -{\ - WRValue V1 = to->singleValue();\ - WRValue& V2 = from->singleValue();\ - NAME##Binary[(V1.type<<2)|V2.type](&V1, &V2, target);\ -}\ -void NAME##Binary_I_E( WRValue* to, WRValue* from, WRValue* target )\ -{\ - WRValue& V = from->singleValue();\ - NAME##Binary[(WR_INT<<2)+V.type](to, &V, target);\ -}\ -void NAME##Binary_E_R( WRValue* to, WRValue* from, WRValue* target ) { NAME##Binary[(WR_EX)|from->r->type](to, from->r, target); }\ -void NAME##Binary_R_E( WRValue* to, WRValue* from, WRValue* target ) { NAME##Binary[(to->r->type<<2)|WR_EX]( to->r, from, target); }\ -void NAME##Binary_I_R( WRValue* to, WRValue* from, WRValue* target ) { NAME##Binary[(WR_INT<<2)+from->r->type](to, from->r, target); }\ -void NAME##Binary_R_R( WRValue* to, WRValue* from, WRValue* target ) { NAME##Binary[(to->r->type<<2)|from->r->type](to->r, from->r, target); }\ -void NAME##Binary_R_I( WRValue* to, WRValue* from, WRValue* target ) { NAME##Binary[(to->r->type<<2)|WR_INT](to->r, from, target); }\ -void NAME##Binary_I_I( WRValue* to, WRValue* from, WRValue* target ) { target->p2 = INIT_AS_INT; target->i = to->i OPERATION from->i; }\ -WRTargetFunc NAME##Binary[16] = \ -{\ - NAME##Binary_I_I, doTargetFuncBlank, NAME##Binary_I_R, NAME##Binary_I_E,\ - doTargetFuncBlank, doTargetFuncBlank, doTargetFuncBlank, doTargetFuncBlank,\ - NAME##Binary_R_I, doTargetFuncBlank, NAME##Binary_R_R, NAME##Binary_R_E,\ - NAME##Binary_E_I, doTargetFuncBlank, NAME##Binary_E_R, NAME##Binary_E_E,\ -};\ - - -X_INT_BINARY( wr_LeftShift, << ); -X_INT_BINARY( wr_RightShift, >> ); -X_INT_BINARY( wr_Mod, % ); -X_INT_BINARY( wr_AND, & ); -X_INT_BINARY( wr_OR, | ); -X_INT_BINARY( wr_XOR, ^ ); - - - - - -#define X_COMPARE( NAME, OPERATION ) \ -bool NAME##_E_E( WRValue* to, WRValue* from )\ -{\ - WRValue V1 = to->singleValue();\ - WRValue& V2 = from->singleValue();\ - return NAME[(V1.type<<2)|V2.type](&V1, &V2);\ -}\ -bool NAME##_E_I( WRValue* to, WRValue* from )\ -{\ - WRValue& V = to->singleValue();\ - return NAME[(V.type<<2)|WR_INT](&V, from);\ -}\ -bool NAME##_E_F( WRValue* to, WRValue* from )\ -{\ - WRValue& V = to->singleValue();\ - return NAME[(V.type<<2)|WR_FLOAT](&V, from);\ -}\ -bool NAME##_I_E( WRValue* to, WRValue* from )\ -{\ - WRValue& V = from->singleValue();\ - return NAME[(WR_INT<<2)|V.type](to, &V);\ -}\ -bool NAME##_F_E( WRValue* to, WRValue* from )\ -{\ - WRValue& V = from->singleValue();\ - return NAME[(WR_FLOAT<<2)|V.type](to, &V);\ -}\ -bool NAME##_R_E( WRValue* to, WRValue* from ) { return NAME[(to->r->type<<2)|WR_EX](to->r, from); }\ -bool NAME##_E_R( WRValue* to, WRValue* from ) { return NAME[(WR_EX<<2)|from->r->type](to, from->r); }\ -bool NAME##_R_R( WRValue* to, WRValue* from ) { return NAME[(to->r->type<<2)|from->r->type](to->r, from->r); }\ -bool NAME##_R_I( WRValue* to, WRValue* from ) { return NAME[(to->r->type<<2)|WR_INT](to->r, from); }\ -bool NAME##_R_F( WRValue* to, WRValue* from ) { return NAME[(to->r->type<<2)|WR_FLOAT](to->r, from); }\ -bool NAME##_I_R( WRValue* to, WRValue* from ) { return NAME[(WR_INT<<2)+from->r->type](to, from->r); }\ -bool NAME##_F_R( WRValue* to, WRValue* from ) { return NAME[(WR_FLOAT<<2)+from->r->type](to, from->r); }\ -bool NAME##_I_I( WRValue* to, WRValue* from ) { return to->i OPERATION from->i; }\ -bool NAME##_I_F( WRValue* to, WRValue* from ) { to->p2 = INIT_AS_FLOAT; return to->f OPERATION from->f; }\ -bool NAME##_F_I( WRValue* to, WRValue* from ) { return to->f OPERATION (float)from->i; }\ -bool NAME##_F_F( WRValue* to, WRValue* from ) { return to->f OPERATION from->f; }\ -WRReturnFunc NAME[16] = \ -{\ - NAME##_I_I, NAME##_I_F, NAME##_I_R, NAME##_I_E,\ - NAME##_F_I, NAME##_F_F, NAME##_F_R, NAME##_F_E,\ - NAME##_R_I, NAME##_R_F, NAME##_R_R, NAME##_R_E,\ - NAME##_E_I, NAME##_E_F, NAME##_E_R, NAME##_E_E,\ -};\ - -X_COMPARE( wr_CompareGT, > ); -X_COMPARE( wr_CompareLT, < ); -X_COMPARE( wr_LogicalAND, && ); -X_COMPARE( wr_LogicalOR, || ); -X_COMPARE( wr_CompareEQ, == ); - -//------------------------------------------------------------------------------ -#define X_UNARY_PRE( NAME, OPERATION ) \ -void NAME##_E( WRValue* value )\ -{\ - WRValue& V = value->singleValue();\ - NAME [ V.type ]( &V );\ - wr_valueToArray( value, &V );\ -}\ -void NAME##_I( WRValue* value ) { OPERATION value->i; }\ -void NAME##_F( WRValue* value ) { OPERATION value->f; }\ -void NAME##_R( WRValue* value ) { NAME [ value->r->type ]( value->r ); *value = *value->r; }\ -WRUnaryFunc NAME[4] = \ -{\ - NAME##_I, NAME##_F, NAME##_R, NAME##_E\ -};\ - -X_UNARY_PRE( wr_preinc, ++ ); -X_UNARY_PRE( wr_predec, -- ); - -//------------------------------------------------------------------------------ -#define X_UNARY_POST( NAME, OPERATION ) \ -void NAME##_E( WRValue* value, WRValue* stack )\ -{\ - WRValue& V = value->singleValue();\ - WRValue temp; \ - NAME [ V.type ]( &V, &temp );\ - wr_valueToArray( value, &V );\ - *stack = temp; \ -}\ -void NAME##_I( WRValue* value, WRValue* stack ) { stack->p2 = INIT_AS_INT; stack->i = value->i OPERATION; }\ -void NAME##_R( WRValue* value, WRValue* stack ) { NAME[ value->r->type ]( value->r, stack ); }\ -void NAME##_F( WRValue* value, WRValue* stack ) { stack->p2 = INIT_AS_FLOAT; stack->f = value->f OPERATION; }\ -WRVoidFunc NAME[4] = \ -{\ - NAME##_I, NAME##_F, NAME##_R, NAME##_E\ -};\ - -X_UNARY_POST( wr_postinc, ++ ); -X_UNARY_POST( wr_postdec, -- ); - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -//------------------------------------------------------------------------------ -void elementToTarget( const uint32_t index, WRValue* target, WRValue* value ) -{ - if ( target == value ) - { - // this happens when a return value is used directly instead of - // assigned to something. So it should be safe to just - // dereference the value and use it directly rather than - // preserve the whole array - - if ( value->va->m_type == SV_VALUE ) - { - *target = value->va->m_Vdata[ index ]; - } - else if ( value->va->m_type == SV_CHAR ) - { - target->p2 = INIT_AS_INT; - target->ui = value->va->m_Cdata[ index ]; - } - else // SV_HASH_TABLE, right? - { - *target = *(WRValue *)value->va->get( index ); - } - } - else - { - target->r = value; - target->p2 = INIT_AS_ARRAY_MEMBER | ENCODE_ARRAY_ELEMENT_TO_P2( index ); - } -} - -//------------------------------------------------------------------------------ -void doIndexHash( WRValue* value, WRValue* target, uint32_t hash ) -{ - if ( value->xtype == WR_EX_HASH_TABLE ) - { - if ( IS_EX_SINGLE_CHAR_RAW_P2( (target->r = (WRValue*)value->va->get(hash))->p2) ) - { - target->p2 = INIT_AS_ARRAY_MEMBER; - } - else - { - target->p2 = INIT_AS_REF; - } - } - else // naming an element of a struct "S.element" - { - const unsigned char* table = value->va->m_ROMHashTable + ((hash % value->va->m_mod) * 5); - - if ( (uint32_t)READ_32_FROM_PC(table) == hash ) - { - target->p2 = INIT_AS_REF; - target->p = ((WRValue*)(value->va->m_data)) + READ_8_FROM_PC(table + 4); - } - else - { - target->init(); - } - } -} - -//------------------------------------------------------------------------------ -void doIndex_I_X( WRContext* c, WRValue* index, WRValue* value, WRValue* target ) -{ - // all we know is the value is not an array, so make it one - value->p2 = INIT_AS_ARRAY; - value->va = c->getSVA( index->ui + 1, SV_VALUE, true ); - - elementToTarget( index->ui, target, value ); -} - -//------------------------------------------------------------------------------ -void doIndex_I_E( WRContext* c, WRValue* index, WRValue* value, WRValue* target ) -{ - value = &value->deref(); - - if (EXPECTS_HASH_INDEX(value->xtype)) - { - doIndexHash( value, target, index->ui ); - return; - } - else if ( IS_RAW_ARRAY(value->xtype) ) - { - if ( index->ui >= (uint32_t)(EX_RAW_ARRAY_SIZE_FROM_P2(value->p2)) ) - { - goto boundsFailed; - } - } - else if ( !(value->xtype == WR_EX_ARRAY) ) - { - // nope, make it one of this size and return a ref - WRValue* I = &index->deref(); - - if ( I->type == WR_INT ) - { - value->p2 = INIT_AS_ARRAY; - value->va = c->getSVA( index->ui+1, SV_VALUE, true ); - } - else - { - value->p2 = INIT_AS_HASH_TABLE; - value->va = c->getSVA( 0, SV_HASH_TABLE, false ); - doIndexHash( value, target, I->getHash() ); - return; - } - } - else if ( index->ui >= value->va->m_size ) - { - if ( value->va->m_skipGC ) - { -boundsFailed: - target->init(); - return; - } - - value->va = wr_growValueArray( value->va, index->ui ); - } - - elementToTarget( index->ui, target, value ); -} - -//------------------------------------------------------------------------------ -void doIndex_E_E( WRContext* c, WRValue* index, WRValue* value, WRValue* target ) -{ - WRValue* V = &value->deref(); - - // nope, make it one of this size and return a ref - WRValue* I = &index->deref(); - if ( I->type == WR_INT ) - { - wr_index[WR_INT<<2 | V->type](c, I, V, target); - } - else - { - if ( !IS_HASH_TABLE(V->xtype) ) - { - V->p2 = INIT_AS_HASH_TABLE; - V->va = c->getSVA( 0, SV_HASH_TABLE, false ); - } - doIndexHash( V, target, I->getHash() ); - } -} - -//------------------------------------------------------------------------------ -void doIndex_R_R( WRContext* c, WRValue* index, WRValue* value, WRValue* target ) -{ - wr_index[(index->r->type<<2)|value->r->type](c, index->r, value->r, target); -} - -void doVoidIndexFunc( WRContext* c, WRValue* index, WRValue* value, WRValue* target ) {} - - -#ifdef WRENCH_REALLY_COMPACT - -void doIndex_X_R( WRContext* c, WRValue* index, WRValue* value, WRValue* target ) { wr_index[(index->type<<2)|value->r->type](c, index, value->r, target); } -void doIndex_R_X( WRContext* c, WRValue* index, WRValue* value, WRValue* target ) { wr_index[(index->r->type<<2)|value->type](c, index->r, value, target); } - -WRStateFunc wr_index[16] = -{ - doIndex_I_X, doIndex_I_X, doIndex_X_R, doIndex_I_E, - doVoidIndexFunc, doVoidIndexFunc, doVoidIndexFunc, doVoidIndexFunc, - doIndex_R_X, doIndex_R_X, doIndex_R_R, doIndex_R_X, - doVoidIndexFunc, doVoidIndexFunc, doIndex_X_R, doIndex_E_E, -}; - - -#else - -void doIndex_I_R( WRContext* c, WRValue* index, WRValue* value, WRValue* target ) { wr_index[(WR_INT<<2)|value->r->type](c, index, value->r, target); } -void doIndex_R_I( WRContext* c, WRValue* index, WRValue* value, WRValue* target ) { wr_index[(index->r->type<<2)|WR_INT](c, index->r, value, target); } -void doIndex_R_F( WRContext* c, WRValue* index, WRValue* value, WRValue* target ) { wr_index[(index->r->type<<2)|WR_FLOAT](c, index->r, value, target); } -void doIndex_R_E( WRContext* c, WRValue* index, WRValue* value, WRValue* target ) { wr_index[(index->r->type<<2)|WR_EX](c, index->r, value, target); } -void doIndex_E_R( WRContext* c, WRValue* index, WRValue* value, WRValue* target ) { wr_index[(WR_EX<<2)|value->r->type](c, index, value->r, target); } - -WRStateFunc wr_index[16] = -{ - doIndex_I_X, doIndex_I_X, doIndex_I_R, doIndex_I_E, - doVoidIndexFunc, doVoidIndexFunc, doVoidIndexFunc, doVoidIndexFunc, - doIndex_R_I, doIndex_R_F, doIndex_R_R, doIndex_R_E, - doVoidIndexFunc, doVoidIndexFunc, doIndex_E_R, doIndex_E_E, -}; - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -// standard functions that sort of come up a lot - -int32_t wr_Seed; - -//------------------------------------------------------------------------------ -uint32_t wr_hash_read8( const void *dat, const int len ) -{ - // fnv-1 - uint32_t hash = 0x811C9DC5; - const unsigned char* data = (const unsigned char *)dat; - - for( int i=0; i=0 && pos < len; --digit ) - { - string[pos++] = buf[digit]; - } - string[pos] = 0; - return pos; -} - -//------------------------------------------------------------------------------ -int wr_ftoa( float f, char* string, size_t len ) -{ - size_t pos = 0; - - // sign stuff - if (f < 0) - { - f = -f; - string[pos++] = '-'; - } - - if ( pos > len ) - { - string[0] = 0; - return 0; - } - - f += 5.f / (float)10e6; // round value to 5 places - - int i = (int)f; - - if ( i ) - { - f -= i; - pos += wr_itoa( i, string + pos, len - pos ); - } - else - { - string[pos++] = '0'; - } - - string[pos++] = '.'; - - for( int p=0; pos < len && p<5; ++p ) // convert non-integer to 5 digits of precision - { - f *= 10.0; - char c = (char)f; - string[pos++] = '0' + c; - f -= c; - } - - for( --pos; pos > 0 && string[pos] == '0' && string[pos] != '.' ; --pos ); // knock off trailing zeros and decimal if appropriate - - if ( string[pos] != '.' ) - { - ++pos; - } - - string[pos] = 0; - return pos; -} - -//------------------------------------------------------------------------------ -void wr_std_rand( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn > 0 ) - { - WRValue* args = stackTop - argn; - - int32_t a = args[0].asInt(); - int32_t b; - if ( argn > 1 ) - { - a = args[0].asInt(); - b = args[1].asInt() - a; - } - else - { - a = 0; - b = args[0].asInt(); - } - - if ( b > 0 ) - { - int32_t k = wr_Seed / 127773; - wr_Seed = 16807 * (wr_Seed - k * 127773) - 2836 * k; - stackTop->i = a + ((uint32_t)wr_Seed % (uint32_t)b); - } - } -} - -//------------------------------------------------------------------------------ -void wr_std_srand( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn == 1 ) - { - wr_Seed = (uint32_t)((stackTop - 1)->asInt()); - } -} - -#if __arm__ || WIN32 || _WIN32 || __linux__ || __MINGW32__ || __APPLE__ || __MINGW64__ || __clang__ -#include -//------------------------------------------------------------------------------ -void wr_std_time( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_INT; - stackTop->i = (int32_t)time(0); -} -#else -//------------------------------------------------------------------------------ -void wr_std_time( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); -} -#endif - - -//------------------------------------------------------------------------------ -void wr_loadStdLib( WRState* w ) -{ - wr_registerLibraryFunction( w, "std::rand", wr_std_rand ); - wr_registerLibraryFunction( w, "std::srand", wr_std_srand ); - wr_registerLibraryFunction( w, "std::time", wr_std_time ); -} - -//------------------------------------------------------------------------------ -void wr_loadAllLibs( WRState* w ) -{ - wr_loadMathLib( w ); - wr_loadStdLib( w ); - wr_loadIOLib( w ); - wr_loadStringLib( w ); - wr_loadMessageLib( w ); - wr_loadSysLib( w ); - wr_loadSerializeLib( w ); -} -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -#if defined(WRENCH_WIN32_FILE_IO) \ - || defined(WRENCH_LINUX_FILE_IO) \ - || defined(WRENCH_SPIFFS_FILE_IO) \ - || defined(WRENCH_LITTLEFS_FILE_IO) \ - || defined(WRENCH_CUSTOM_FILE_IO) - -//------------------------------------------------------------------------------ -void wr_loadIOLib( WRState* w ) -{ - wr_registerLibraryFunction( w, "io::readFile", wr_read_file ); // open+read+close a file - wr_registerLibraryFunction( w, "io::writeFile", wr_write_file ); // open+write+close a file - wr_registerLibraryFunction( w, "io::getline", wr_getline ); // get a line of text from input - - wr_registerLibraryFunction( w, "time::clock", wr_clock ); // return current unix time - wr_registerLibraryFunction( w, "time::ms", wr_clock ); // return ms count that always increases - - wr_registerLibraryFunction( w, "io::open", wr_ioOpen ); //( name, flags, mode ); // returning a file handle 'fd' ; 'mode' specifies unix file access permissions. - wr_registerLibraryFunction( w, "io::close", wr_ioClose ); //( fd ); - wr_registerLibraryFunction( w, "io::read", wr_ioRead ); //( fd, data, max_count ); - wr_registerLibraryFunction( w, "io::write", wr_ioWrite ); //( fd, data, count ); - wr_registerLibraryFunction( w, "io::seek", wr_ioSeek ); //( fd, offset, whence ); - - wr_ioPushConstants( w ); -} - -#else - -void wr_loadIOLib( WRState* w ) -{ -} - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -#ifdef WRENCH_LINUX_FILE_IO - -#include -#include -#include - -//------------------------------------------------------------------------------ -void wr_read_file( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn == 1 ) - { - WRValue* arg = stackTop - 1; - const char* fileName = (const char*)arg->array(); - - struct stat sbuf; - int ret = stat( fileName, &sbuf ); - - if ( ret == 0 ) - { - FILE *infil = fopen( fileName, "rb" ); - if ( infil ) - { - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( (int)sbuf.st_size, SV_CHAR, false ); - if ( fread( stackTop->va->m_Cdata, sbuf.st_size, 1, infil ) != 1 ) - { - stackTop->init(); - return; - } - } - - fclose( infil ); - } - } -} - -//------------------------------------------------------------------------------ -void wr_write_file( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn == 2 ) - { - WRValue* arg1 = stackTop - 2; - unsigned int len; - const char* data = (char*)((stackTop - 1)->array(&len)); - if ( !data ) - { - return; - } - - const char* fileName = (const char*)arg1->array(); - if ( !fileName ) - { - return; - } - - FILE *outfil = fopen( fileName, "wb" ); - if ( !outfil ) - { - return; - } - - stackTop->i = (int)fwrite( data, len, 1, outfil ); - fclose( outfil ); - } -} - -//------------------------------------------------------------------------------ -void wr_getline( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - char buf[256]; - int pos = 0; - for (;;) - { - int in = fgetc( stdin ); - - if ( in == EOF || in == '\n' || in == '\r' || pos >= 256 ) - { - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( pos, SV_CHAR, false ); - memcpy( stackTop->va->m_Cdata, buf, pos ); - break; - } - - buf[pos++] = in; - } -} - -//------------------------------------------------------------------------------ -void wr_clock( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->i = (int)clock(); -} - -//------------------------------------------------------------------------------ -void wr_milliseconds(WRValue* stackTop, const int argn, WRContext* c ) -{ - struct timeval tv; - gettimeofday( &tv, NULL ); - stackTop->ui = (uint32_t)((tv.tv_usec/1000) + (tv.tv_sec * 1000)); -} - -//------------------------------------------------------------------------------ -void wr_ioOpen( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - stackTop->i = -1; - - if ( argn ) - { - WRValue* args = stackTop - argn; - - const char* fileName = (const char*)args->array(); - if ( fileName ) - { - int mode = (argn > 1) ? args[1].asInt() : O_RDWR | O_CREAT; - stackTop->i = open( fileName, mode, 0666 ); - } - } -} - -//------------------------------------------------------------------------------ -void wr_ioClose( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn ) - { - close( (stackTop - argn)->i ); - } -} - -//------------------------------------------------------------------------------ -void wr_ioRead( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_ARRAY; - - if ( argn > 1 ) - { - WRValue* args = stackTop - argn; - int toRead = args[1].asInt(); - if ( toRead > 0 ) - { - stackTop->va = c->getSVA( toRead, SV_CHAR, false ); - - int result = read( args[0].asInt(), stackTop->va->m_Cdata, toRead ); - if ( result > 0 ) - { - stackTop->va->m_size = result; - return; - } - } - } - - stackTop->va = c->getSVA( 0, SV_CHAR, false ); -} - -//------------------------------------------------------------------------------ -void wr_ioWrite( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn > 1 ) - { - WRValue* args = stackTop - argn; - - int fd = args[0].asInt(); - WRValue& data = args[1].deref(); - - if ( IS_ARRAY(data.xtype) ) - { - uint32_t size = data.va->m_size; - if ( argn > 2 ) - { - size = args[2].asInt(); - } - - if ( data.va->m_type == SV_CHAR ) - { - stackTop->ui = write( fd, data.va->m_Cdata, (size > data.va->m_size) ? data.va->m_size : size ); - } - else if ( data.va->m_type == SV_VALUE ) - { - // .. does this even make sense? - } - } - else if ( IS_RAW_ARRAY(data.xtype) ) - { - uint32_t size = EX_RAW_ARRAY_SIZE_FROM_P2(data.r->p2); - if ( argn > 2 ) - { - size = args[2].asInt(); - } - - stackTop->ui = write( fd, data.r->c, (size > EX_RAW_ARRAY_SIZE_FROM_P2(data.r->p2)) ? EX_RAW_ARRAY_SIZE_FROM_P2(data.r->p2) : size ); - } - } -} - -//------------------------------------------------------------------------------ -void wr_ioSeek( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn > 1 ) - { - WRValue* args = stackTop - argn; - - int fd = args[0].asInt(); - int offset = args[1].asInt(); - int whence = argn > 2 ? args[2].asInt() : SEEK_SET; - - stackTop->ui = lseek( fd, offset, whence ); - } -} - -//------------------------------------------------------------------------------ -void wr_ioPushConstants( WRState* w ) -{ - WRValue C; - - wr_registerLibraryConstant( w, "io::O_RDONLY", wr_makeInt(&C, O_RDONLY) ); - wr_registerLibraryConstant( w, "io::O_RDWR", wr_makeInt(&C, O_RDWR) ); - wr_registerLibraryConstant( w, "io::O_APPEND", wr_makeInt(&C, O_APPEND) ); - wr_registerLibraryConstant( w, "io::O_CREAT", wr_makeInt(&C, O_CREAT) ); - wr_registerLibraryConstant( w, "io::O_TRUNC", wr_makeInt(&C, O_TRUNC) ); - wr_registerLibraryConstant( w, "io::O_EXCL", wr_makeInt(&C, O_EXCL) ); - - wr_registerLibraryConstant( w, "io::SEEK_SET", wr_makeInt(&C, SEEK_SET) ); - wr_registerLibraryConstant( w, "io::SEEK_CUR", wr_makeInt(&C, SEEK_CUR) ); - wr_registerLibraryConstant( w, "io::SEEK_END", wr_makeInt(&C, SEEK_END) ); -} - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -#ifdef WRENCH_WIN32_FILE_IO - -#include - -#include -#include - -//------------------------------------------------------------------------------ -void wr_read_file( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - char buf[256]; - - if ( argn == 1 ) - { - WRValue* arg = stackTop - 1; - const char* fileName = arg->asString(buf, 256); - - struct _stat sbuf; - int ret = _stat( fileName, &sbuf ); - - if ( ret == 0 ) - { - FILE *infil = fopen( fileName, "rb" ); - if ( infil ) - { - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( (int)sbuf.st_size, SV_CHAR, false ); - if ( fread( stackTop->va->m_Cdata, sbuf.st_size, 1, infil ) != 1 ) - { - stackTop->init(); - return; - } - } - - fclose( infil ); - } - } -} - -//------------------------------------------------------------------------------ -void wr_write_file( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - char buf[256]; - if ( argn == 2 ) - { - WRValue* arg1 = stackTop - 2; - unsigned int len; - const char* data = (char*)((stackTop - 1)->array(&len)); - if ( !data ) - { - return; - } - - const char* fileName = arg1->asString(buf, 256); - if ( !fileName ) - { - return; - } - - FILE *outfil = fopen( fileName, "wb" ); - if ( !outfil ) - { - return; - } - - stackTop->i = (int)fwrite( data, len, 1, outfil ); - fclose( outfil ); - } -} - -//------------------------------------------------------------------------------ -void wr_getline( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - char buf[256]; - int pos = 0; - for (;;) - { - int in = fgetc( stdin ); - - if ( in == EOF || in == '\n' || in == '\r' || pos >= 256 ) - { - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( pos, SV_CHAR, false ); - memcpy( stackTop->va->m_Cdata, buf, pos ); - break; - } - - buf[pos++] = in; - } -} - -//------------------------------------------------------------------------------ -void wr_clock( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->i = (int)clock(); -} - -//------------------------------------------------------------------------------ -void wr_milliseconds(WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->ui = (uint32_t)GetTickCount(); -} - -//------------------------------------------------------------------------------ -void wr_ioOpen( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - stackTop->i = -1; - char buf[256]; - - if ( argn ) - { - WRValue* args = stackTop - argn; - - const char* fileName = args->asString(buf, 256); - if ( fileName ) - { - int mode = (argn > 1) ? args[1].asInt() : O_RDWR | O_CREAT; - - stackTop->i = _open( fileName, mode | O_BINARY ); - } - } -} - -//------------------------------------------------------------------------------ -void wr_ioClose( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn ) - { - _close( (stackTop - argn)->i ); - } -} - -//------------------------------------------------------------------------------ -void wr_ioRead( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_ARRAY; - - if ( argn > 1 ) - { - WRValue* args = stackTop - argn; - int fd = args[0].asInt(); - int toRead = args[1].asInt(); - if ( toRead > 0 ) - { - stackTop->va = c->getSVA( toRead, SV_CHAR, false ); - - int result = _read( fd, stackTop->va->m_Cdata, toRead ); - if ( result > 0 ) - { - stackTop->va->m_size = result; - return; - } - } - } - - stackTop->va = c->getSVA( 0, SV_CHAR, false ); -} - -//------------------------------------------------------------------------------ -void wr_ioWrite( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn > 1 ) - { - WRValue* args = stackTop - argn; - - int fd = args[0].asInt(); - WRValue& data = args[1].deref(); - - if ( IS_ARRAY(data.xtype) ) - { - uint32_t size = data.va->m_size; - if ( argn > 2 ) - { - size = args[2].asInt(); - } - - if ( data.va->m_type == SV_CHAR ) - { - stackTop->ui = _write( fd, data.va->m_Cdata, (size > data.va->m_size) ? data.va->m_size : size ); - } - else if ( data.va->m_type == SV_VALUE ) - { - // .. does this even make sense? - } - } - else if ( IS_RAW_ARRAY(data.xtype) ) - { - uint32_t size = EX_RAW_ARRAY_SIZE_FROM_P2(data.r->p2); - if ( argn > 2 ) - { - size = args[2].asInt(); - } - - stackTop->ui = _write( fd, data.r->c, (size > EX_RAW_ARRAY_SIZE_FROM_P2(data.r->p2)) ? EX_RAW_ARRAY_SIZE_FROM_P2(data.r->p2) : size ); - } - } -} - -//------------------------------------------------------------------------------ -void wr_ioSeek( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn > 1 ) - { - WRValue* args = stackTop - argn; - - int fd = args[0].asInt(); - int offset = args[1].asInt(); - int whence = argn > 2 ? args[2].asInt() : SEEK_SET; - - stackTop->ui = _lseek( fd, offset, whence ); - } -} - -//------------------------------------------------------------------------------ -void wr_ioPushConstants( WRState* w ) -{ - WRValue C; - - wr_registerLibraryConstant( w, "io::O_RDONLY", wr_makeInt(&C, O_RDONLY) ); - wr_registerLibraryConstant( w, "io::O_RDWR", wr_makeInt(&C, O_RDWR) ); - wr_registerLibraryConstant( w, "io::O_APPEND", wr_makeInt(&C, O_APPEND) ); - wr_registerLibraryConstant( w, "io::O_CREAT", wr_makeInt(&C, O_CREAT) ); - wr_registerLibraryConstant( w, "io::O_TRUNC", wr_makeInt(&C, O_TRUNC) ); - wr_registerLibraryConstant( w, "io::O_EXCL", wr_makeInt(&C, O_EXCL) ); - - wr_registerLibraryConstant( w, "io::SEEK_SET", wr_makeInt(&C, SEEK_SET) ); - wr_registerLibraryConstant( w, "io::SEEK_CUR", wr_makeInt(&C, SEEK_CUR) ); - wr_registerLibraryConstant( w, "io::SEEK_END", wr_makeInt(&C, SEEK_END) ); -} - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -#ifdef WRENCH_SPIFFS_FILE_IO - -//------------------------------------------------------------------------------ -void wr_read_file( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_write_file( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_getline( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_clock( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_milliseconds(WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_ioOpen( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_ioClose( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_ioRead( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_ioWrite( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_ioSeek( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_ioPushConstants( WRState* w ) -{ - WRValue C; - - wr_registerLibraryConstant( w, "io::O_RDONLY", wr_makeInt(&C, O_RDONLY) ); //( fd, offset, whence ); - wr_registerLibraryConstant( w, "io::O_RDWR", wr_makeInt(&C, O_RDWR) ); //( fd, offset, whence ); - wr_registerLibraryConstant( w, "io::O_APPEND", wr_makeInt(&C, O_APPEND) ); //( fd, offset, whence ); - wr_registerLibraryConstant( w, "io::O_CREAT", wr_makeInt(&C, O_CREAT) ); //( fd, offset, whence ); - - wr_registerLibraryConstant( w, "io::SEEK_SET", wr_makeInt(&C, SEEK_SET) ); //( fd, offset, whence ); - wr_registerLibraryConstant( w, "io::SEEK_CUR", wr_makeInt(&C, SEEK_CUR) ); //( fd, offset, whence ); - wr_registerLibraryConstant( w, "io::SEEK_END", wr_makeInt(&C, SEEK_END) ); //( fd, offset, whence ); -} - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -#ifdef WRENCH_LITTLEFS_FILE_IO - - -//------------------------------------------------------------------------------ -void wr_read_file( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_write_file( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_getline( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_clock( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_milliseconds(WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_ioOpen( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_ioClose( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_ioRead( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_ioWrite( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_ioSeek( WRValue* stackTop, const int argn, WRContext* c ) -{ -} - -//------------------------------------------------------------------------------ -void wr_ioPushConstants( WRState* w ) -{ - WRValue C; - - wr_registerLibraryConstant( w, "io::O_RDONLY", wr_makeInt(&C, O_RDONLY) ); //( fd, offset, whence ); - wr_registerLibraryConstant( w, "io::O_RDWR", wr_makeInt(&C, O_RDWR) ); //( fd, offset, whence ); - wr_registerLibraryConstant( w, "io::O_APPEND", wr_makeInt(&C, O_APPEND) ); //( fd, offset, whence ); - wr_registerLibraryConstant( w, "io::O_CREAT", wr_makeInt(&C, O_CREAT) ); //( fd, offset, whence ); - - wr_registerLibraryConstant( w, "io::SEEK_SET", wr_makeInt(&C, SEEK_SET) ); //( fd, offset, whence ); - wr_registerLibraryConstant( w, "io::SEEK_CUR", wr_makeInt(&C, SEEK_CUR) ); //( fd, offset, whence ); - wr_registerLibraryConstant( w, "io::SEEK_END", wr_makeInt(&C, SEEK_END) ); //( fd, offset, whence ); -} - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -#include -#include - -//------------------------------------------------------------------------------ -int wr_strlenEx( WRValue* val ) -{ - if ( val->type == WR_REF ) - { - return wr_strlenEx( val->r ); - } - - return (val->xtype == WR_EX_ARRAY && val->va->m_type == SV_CHAR) ? val->va->m_size : 0; -} - -//------------------------------------------------------------------------------ -void wr_strlen( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->i = argn == 1 ? wr_strlenEx( stackTop - 1 ) : 0; -} - -//------------------------------------------------------------------------------ -int wr_sprintfEx( char* outbuf, const char* fmt, WRValue* args, const int argn ) -{ - if ( !fmt ) - { - return 0; - } - - enum - { - zeroPad = 1<<0, - negativeJustify = 1<<1, - secondPass = 1<<2, - negativeSign = 1<<3, - parsingSigned = 1<<4, - altRep = 1<<5, - }; - - char* out = outbuf; - int listPtr = 0; - -resetState: - - char padChar = ' '; - unsigned char columns = 0; - char flags = 0; - - for(;;) - { - char c = *fmt; - fmt++; - - if ( !c ) - { - *out = 0; - break; - } - - if ( !(secondPass & flags) ) - { - if ( c != '%' ) // literal - { - *out++ = c; - } - else // possibly % format specifier - { - flags |= secondPass; - } - } - else if ( c >= '0' && c <= '9' ) // width - { - columns *= 10; - columns += c - '0'; - if ( !columns ) // leading zero - { - flags |= zeroPad; - padChar = '0'; - } - } - else if ( c == '#' ) - { - flags |= altRep; - } - else if ( c == '-' ) // left-justify - { - flags |= negativeJustify; - } -#ifdef WRENCH_FLOAT_SPRINTF - else if (c == '.') // ignore, we might be reading a floating point decimal position - { } -#endif - else if ( c == 'c' ) // character - { - if ( listPtr < argn ) - { - *out++ = (char)(args[listPtr++].asInt()); - } - goto resetState; - } - else if ( c == '%' ) // literal % - { - *out++ = c; - goto resetState; - } - else // string or integer - { - char buf[20]; // buffer for integer - - const char *ptr; // pointer to first char of integer - - if ( c == 's' ) // string - { - buf[0] = 0; - if ( listPtr < argn ) - { - ptr = (char *)args[listPtr].array(); - if ( !ptr ) - { - return 0; - } - ++listPtr; - } - else - { - ptr = buf; - } - - padChar = ' '; // in case some joker uses a 0 in their column spec - -copyToString: - - // get the string length so it can be formatted, don't - // copy it, just count it - unsigned char len = 0; - for ( ; *ptr; ptr++ ) - { - len++; - } - - ptr -= len; - - // Right-justify - if ( !(flags & negativeJustify) ) - { - for ( ; columns > len; columns-- ) - { - *out++ = padChar; - } - } - - if ( flags & negativeSign ) - { - *out++ = '-'; - } - - if ( flags & altRep || c == 'p' ) - { - *out++ = '0'; - *out++ = 'x'; - } - - for (unsigned char l = 0; l < len; ++l ) - { - *out++ = *ptr++; - } - - // Left-justify - for ( ; columns > len; columns-- ) - { - *out++ = ' '; - } - - goto resetState; - } - else - { - unsigned char base; - unsigned char width; - unsigned int val; - - if ( c == 'd' || c == 'i' ) - { - flags |= parsingSigned; - goto parseDecimal; - } -#ifdef WRENCH_FLOAT_SPRINTF - else if ( c == 'f' || c == 'g' ) - { - char floatBuf[32]; - int i = 30; - floatBuf[31] = 0; - const char *f = fmt; - for( ; *f != '%'; --f, --i ) - { - floatBuf[i] = *f; - } - floatBuf[i] = '%'; - - // suck in whatever lib we need for this - const int chars = snprintf( buf, 31, floatBuf + i, args[listPtr++].asFloat()); - - // your system not have snprintf? the unsafe version is: -// const int chars = sprintf( buf, floatBuf + i, args[listPtr++].asFloat()); - - for( int j=0; j '9' ) // handle bases higher than 10 - { - d += 'A' - ('9' + 1); - if ( c == 'x' ) // lowercase - { - d += 'a' - 'A'; - } - } - - *--p = d; - - } while ( p != buf ); - - ptr--; // was one past char we want - - goto copyToString; - } - else // invalid format specifier - { - goto resetState; - } - } - } - } - - return out - outbuf; -} - -//------------------------------------------------------------------------------ -void wr_format( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if( argn >= 1 ) - { - WRValue* args = stackTop - argn; - char outbuf[512]; - char inbuf[512]; - args[0].asString( inbuf, 512 ); - - int size = wr_sprintfEx( outbuf, inbuf, args + 1, argn - 1 ); - - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( size, SV_CHAR, false ); - memcpy( stackTop->va->m_Cdata, outbuf, size ); - } -} - -//------------------------------------------------------------------------------ -void wr_printf( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - -#ifdef WRENCH_STD_FILE - if( argn >= 1 ) - { - WRValue* args = stackTop - argn; - - char outbuf[512]; - stackTop->i = wr_sprintfEx( outbuf, args[0].c_str(), args + 1, argn - 1 ); - - printf( "%s", outbuf ); - } -#endif -} - -//------------------------------------------------------------------------------ -void wr_sprintf( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - WRValue* args = stackTop - argn; - - if ( argn < 2 - || args[0].type != WR_REF - || args[1].xtype != WR_EX_ARRAY - || args[1].va->m_type != SV_CHAR ) - { - return; - } - - char outbuf[512]; - char inbuf[512]; - args[1].asString(inbuf); - stackTop->i = wr_sprintfEx( outbuf, inbuf, args + 2, argn - 2 ); - - args[0].r->p2 = INIT_AS_ARRAY; - args[0].r->va = c->getSVA( stackTop->i, SV_CHAR, false ); - memcpy( args[0].r->va->m_Cdata, outbuf, stackTop->i ); -} - -//------------------------------------------------------------------------------ -void wr_isspace( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn == 1 ) - { - stackTop->i = (int)isspace( (char)(stackTop - 1)->asInt() ); - } -} - -//------------------------------------------------------------------------------ -void wr_isalpha( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn == 1 ) - { - stackTop->i = (int)isalpha( (char)(stackTop - 1)->asInt() ); - } -} - -//------------------------------------------------------------------------------ -void wr_isdigit( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn == 1 ) - { - stackTop->i = (int)isdigit( (char)(stackTop - 1)->asInt() ); - } -} - -//------------------------------------------------------------------------------ -void wr_isalnum( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn == 1 ) - { - stackTop->i = (int)isalnum( (char)(stackTop - 1)->asInt() ); - } -} - -//------------------------------------------------------------------------------ -void wr_mid( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn < 2 ) - { - return; - } - - WRValue* args = stackTop - argn; - unsigned int len; - const char* data = (char *)(args[0].array(&len)); - - if( !data || len <= 0 ) - { - return; - } - - unsigned int start = args[1].asInt(); - stackTop->p2 = INIT_AS_ARRAY; - - unsigned int chars = 0; - if ( start < len ) - { - chars = (argn > 2) ? args[2].asInt() : len; - if ( chars > (len - start) ) - { - chars = len - start; - } - } - - stackTop->va = c->getSVA( chars, SV_CHAR, false ); - memcpy( stackTop->va->m_Cdata, data + start, chars ); -} - -//------------------------------------------------------------------------------ -void wr_strchr( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->i = -1; - - if ( argn < 2 ) - { - return; - } - - WRValue* args = stackTop - argn; - - const char* str = (const char*)(args[0].array()); - if ( !str ) - { - return; - } - - char ch = (char)args[1].asInt(); - const char* found = strchr( str, ch ); - if ( found ) - { - stackTop->i = found - str; - } -} - -//------------------------------------------------------------------------------ -void wr_tolower( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn == 1 ) - { - WRValue& value = (stackTop - 1)->deref(); - if ( value.xtype == WR_EX_ARRAY && value.va->m_type == SV_CHAR ) - { - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( value.va->m_size, SV_CHAR, false ); - - for( uint32_t i=0; im_size; ++i ) - { - stackTop->va->m_SCdata[i] = tolower(value.va->m_SCdata[i]); - } - } - else if ( value.type == WR_INT ) - { - stackTop->i = (int)tolower( (stackTop - 1)->asInt() ); - } - } -} - -//------------------------------------------------------------------------------ -void wr_toupper( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn == 1 ) - { - WRValue& value = (stackTop - 1)->deref(); - if ( value.xtype == WR_EX_ARRAY && value.va->m_type == SV_CHAR ) - { - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( value.va->m_size, SV_CHAR, false ); - - for( uint32_t i=0; im_size; ++i ) - { - stackTop->va->m_SCdata[i] = toupper(value.va->m_SCdata[i]); - } - } - else if ( value.type == WR_INT ) - { - stackTop->i = (int)toupper( (stackTop - 1)->asInt() ); - } - - } -} - -//------------------------------------------------------------------------------ -void wr_tol( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn == 2 ) - { - const char* str = (const char*)stackTop[-2].array(); - if ( str ) - { - stackTop->i = (int)strtol( str, 0, stackTop[-1].asInt() ); - } - } - else if ( argn == 1 ) - { - const char* str = (const char*)stackTop[-1].array(); - if ( str ) - { - stackTop->i = (int)strtol( str, 0, 10 ); - } - } -} - -//------------------------------------------------------------------------------ -void wr_concat( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn < 2 ) - { - return; - } - - WRValue* args = stackTop - argn; - unsigned int len1 = 0; - unsigned int len2 = 0; - const char* data1 = (const char*)args[0].array( &len1 ); - const char* data2 = (const char*)args[1].array( &len2 ); - - if( !data1 || !data2 ) - { - return; - } - - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( len1 + len2, SV_CHAR, false ); - memcpy( stackTop->va->m_Cdata, data1, len1 ); - memcpy( stackTop->va->m_Cdata + len1, data2, len2 ); -} - -//------------------------------------------------------------------------------ -void wr_left( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn < 2 ) - { - return; - } - - WRValue* args = stackTop - argn; - unsigned int len = 0; - const char* data = (const char*)args[0].array(&len ); - if ( !data ) - { - return; - } - unsigned int chars = args[1].asInt(); - - stackTop->p2 = INIT_AS_ARRAY; - - if ( chars > len ) - { - chars = len; - } - - stackTop->va = c->getSVA( chars, SV_CHAR, false ); - memcpy( stackTop->va->m_Cdata, data, chars ); -} - -//------------------------------------------------------------------------------ -void wr_right( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn < 2 ) - { - return; - } - - WRValue* args = stackTop - argn; - unsigned int len = 0; - const char* data = (const char*)args[0].array(&len ); - if ( !data ) - { - return; - } - unsigned int chars = args[1].asInt(); - - if ( chars > len ) - { - chars = len; - } - - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( chars, SV_CHAR, false ); - memcpy( stackTop->va->m_Cdata, data + (len - chars), chars ); -} - -//------------------------------------------------------------------------------ -void wr_trimright( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - WRValue* args = stackTop - argn; - const char* data; - int len = 0; - - if ( argn < 1 || ((data = (const char*)args->array((unsigned int *)&len)) == 0) ) - { - return; - } - - while( --len >= 0 && isspace(data[len]) ); - - ++len; - - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( len, SV_CHAR, false ); - memcpy( stackTop->va->m_Cdata, data, len ); -} - -//------------------------------------------------------------------------------ -void wr_trimleft( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - WRValue* args = stackTop - argn; - const char* data; - unsigned int len = 0; - - if ( argn < 1 || ((data = (const char*)args->array(&len)) == 0) ) - { - return; - } - - unsigned int marker = 0; - while( marker < len && isspace(data[marker]) ) { ++marker; } - - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( len - marker, SV_CHAR, false ); - memcpy( stackTop->va->m_Cdata, data + marker, len - marker ); -} - -//------------------------------------------------------------------------------ -void wr_trim( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - WRValue* args = stackTop - argn; - const char* data; - int len = 0; - if ( argn < 1 || ((data = (const char*)args->array((unsigned int*)&len)) == 0) ) - { - return; - } - - int marker = 0; - while( marker < len && isspace(data[marker]) ) { ++marker; } - - while( --len >= marker && isspace(data[len]) ); - - ++len; - - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( len - marker, SV_CHAR, false ); - memcpy( stackTop->va->m_Cdata, data + marker, len - marker ); -} - -//------------------------------------------------------------------------------ -void wr_insert( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn < 3 ) - { - return; - } - - WRValue* args = stackTop - argn; - unsigned int len1 = 0; - unsigned int len2 = 0; - const char* data1 = (const char*)args[0].array( &len1 ); - const char* data2 = (const char*)args[1].array( &len2 ); - - if( !data1 || !data2 ) - { - return; - } - - unsigned int pos = args[2].asInt(); - if ( pos >= len1 ) - { - pos = len1; - } - - stackTop->p2 = INIT_AS_ARRAY; - unsigned int newlen = len1 + len2; - stackTop->va = c->getSVA( newlen, SV_CHAR, false ); - - memcpy( stackTop->va->m_Cdata, data1, pos ); - memcpy( stackTop->va->m_Cdata + pos, data2, len2 ); - memcpy( stackTop->va->m_Cdata + pos + len2, data1 + pos, len1 - pos ); -} - - -//------------------------------------------------------------------------------ -void wr_loadStringLib( WRState* w ) -{ - wr_registerLibraryFunction( w, "str::strlen", wr_strlen ); - wr_registerLibraryFunction( w, "str::sprintf", wr_sprintf ); - wr_registerLibraryFunction( w, "str::printf", wr_printf ); - wr_registerLibraryFunction( w, "str::format", wr_format ); - wr_registerLibraryFunction( w, "str::isspace", wr_isspace ); - wr_registerLibraryFunction( w, "str::isdigit", wr_isdigit ); - wr_registerLibraryFunction( w, "str::isalpha", wr_isalpha ); - wr_registerLibraryFunction( w, "str::mid", wr_mid ); - wr_registerLibraryFunction( w, "str::chr", wr_strchr ); - wr_registerLibraryFunction( w, "str::tolower", wr_tolower ); - wr_registerLibraryFunction( w, "str::toupper", wr_toupper ); - wr_registerLibraryFunction( w, "str::tol", wr_tol ); - wr_registerLibraryFunction( w, "str::concat", wr_concat ); - wr_registerLibraryFunction( w, "str::left", wr_left ); - wr_registerLibraryFunction( w, "str::trunc", wr_left ); - wr_registerLibraryFunction( w, "str::right", wr_right ); - wr_registerLibraryFunction( w, "str::substr", wr_mid ); - wr_registerLibraryFunction( w, "str::trimright", wr_trimright ); - wr_registerLibraryFunction( w, "str::trimleft", wr_trimleft ); - wr_registerLibraryFunction( w, "str::trim", wr_trim ); - wr_registerLibraryFunction( w, "str::insert", wr_insert ); -} -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -#include - -//------------------------------------------------------------------------------ -void wr_math_sin( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = sinf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_cos( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = cosf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_tan( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = tanf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_sinh( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = sinhf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_cosh( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = coshf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_tanh( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = tanhf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_asin( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = asinf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_acos( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = acosf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_atan( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = atanf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_atan2( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 2 ) - { - stackTop->f = atan2f( (stackTop - 1)->asFloat(), (stackTop - 2)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_log( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = logf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_log10( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = log10f( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_exp( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = expf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_sqrt( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = sqrtf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_ceil( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = ceilf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_floor( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = floorf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_abs( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = (float)fabs( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_pow( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 2 ) - { - stackTop->f = powf( (stackTop - 1)->asFloat(), (stackTop - 2)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_fmod( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 2 ) - { - stackTop->f = fmodf( (stackTop - 1)->asFloat(), (stackTop - 2)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_trunc( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = truncf( (stackTop - 1)->asFloat() ); - } -} - -//------------------------------------------------------------------------------ -void wr_math_ldexp( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 2 ) - { - stackTop->f = ldexpf( (stackTop - 1)->asFloat(), (stackTop - 2)->asInt() ); - } -} - -//------------------------------------------------------------------------------ -const float wr_PI = 3.14159265358979323846f; -const float wr_toDegrees = (180.f / wr_PI); -const float wr_toRadians = (1.f / wr_toDegrees); - -//------------------------------------------------------------------------------ -void wr_math_rad2deg( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = wr_toDegrees * (stackTop - 1)->asFloat(); - } -} - -//------------------------------------------------------------------------------ -void wr_math_deg2rad( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->p2 = INIT_AS_FLOAT; - if ( argn == 1 ) - { - stackTop->f = wr_toRadians * (stackTop - 1)->asFloat(); - } -} - -//------------------------------------------------------------------------------ -void wr_loadMathLib( WRState* w ) -{ - wr_registerLibraryFunction( w, "math::sin", wr_math_sin ); - wr_registerLibraryFunction( w, "math::cos", wr_math_cos ); - wr_registerLibraryFunction( w, "math::tan", wr_math_tan ); - wr_registerLibraryFunction( w, "math::sinh", wr_math_sinh ); - wr_registerLibraryFunction( w, "math::cosh", wr_math_cosh ); - wr_registerLibraryFunction( w, "math::tanh", wr_math_tanh ); - wr_registerLibraryFunction( w, "math::asin", wr_math_asin ); - wr_registerLibraryFunction( w, "math::acos", wr_math_acos ); - wr_registerLibraryFunction( w, "math::atan", wr_math_atan ); - wr_registerLibraryFunction( w, "math::atan2", wr_math_atan2 ); - wr_registerLibraryFunction( w, "math::log", wr_math_log ); - wr_registerLibraryFunction( w, "math::ln", wr_math_log ); - wr_registerLibraryFunction( w, "math::log10", wr_math_log10 ); - wr_registerLibraryFunction( w, "math::exp", wr_math_exp ); - wr_registerLibraryFunction( w, "math::pow", wr_math_pow ); - wr_registerLibraryFunction( w, "math::fmod", wr_math_fmod ); - wr_registerLibraryFunction( w, "math::trunc", wr_math_trunc ); - wr_registerLibraryFunction( w, "math::sqrt", wr_math_sqrt ); - wr_registerLibraryFunction( w, "math::ceil", wr_math_ceil ); - wr_registerLibraryFunction( w, "math::floor", wr_math_floor ); - wr_registerLibraryFunction( w, "math::abs", wr_math_abs ); - wr_registerLibraryFunction( w, "math::ldexp", wr_math_ldexp ); - - wr_registerLibraryFunction( w, "math::deg2rad", wr_math_deg2rad ); - wr_registerLibraryFunction( w, "math::rad2deg", wr_math_rad2deg ); -} -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -//------------------------------------------------------------------------------ -// key : anything hashable -// clear : [OPTIONAL] default 0/'false' -// -// returns : value if it was there -void wr_mboxRead( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn > 0 ) - { - WRValue* args = stackTop - argn; - int clear = (argn > 1) ? args[1].asInt() : 0; - - WRValue* msg = c->w->globalRegistry.exists( args[0].getHash(), true, clear ); - - if ( msg ) - { - *stackTop = *msg; - return; - } - - } - - stackTop->init(); -} - -//------------------------------------------------------------------------------ -// key : anything hashable -// value: stores the "hash" -void wr_mboxWrite( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn > 1 ) - { - WRValue* args = stackTop - argn; - - WRValue* msg = c->w->globalRegistry.getAsRawValueHashTable( args[0].getHash() ); - msg->ui = args[1].getHash(); - msg->p2 = INIT_AS_INT; - } -} - -//------------------------------------------------------------------------------ -// key : anything hashable -void wr_mboxClear( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn > 0 ) - { - c->w->globalRegistry.exists((stackTop - argn)->getHash(), true, true ); - } -} - -//------------------------------------------------------------------------------ -// key : anything hashable -// -// return : true/false -void wr_mboxPeek( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn > 0 - && c->w->globalRegistry.exists((stackTop - argn)->getHash(), true, false) ) - { - stackTop->i = 1; - } -} - -//------------------------------------------------------------------------------ -void wr_loadMessageLib( WRState* w ) -{ - wr_registerLibraryFunction( w, "msg::read", wr_mboxRead ); // read (true to clear, false to leave) - wr_registerLibraryFunction( w, "msg::write", wr_mboxWrite ); // write message - wr_registerLibraryFunction( w, "msg::clear", wr_mboxClear ); // remove if exists - wr_registerLibraryFunction( w, "msg::peek", wr_mboxPeek ); // does the message exist? -} -/******************************************************************************* -Copyright (c) 2023 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -//------------------------------------------------------------------------------ -// returns : 0 - function does not exist -// : 1 - function is native (callback) -// : 2 - function is in wrench (was in source code) -void wr_function( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - - if ( argn > 0 ) - { - WRValue* arg = stackTop - argn; - const char* name = (char *)(arg->array()); - if ( name ) - { - uint32_t hash = wr_hashStr( name ); - if ( c->w->globalRegistry.exists(hash, true, false) ) - { - stackTop->i = 1; - } - else if ( c->registry.exists(hash, true, false) ) - { - stackTop->i = 2; - } - } - } -} - -//------------------------------------------------------------------------------ -// takes a single argument: how many invokations to ignore -void wr_gcPause( WRValue* stackTop, const int argn, WRContext* c ) -{ - if ( argn > 0 ) - { - WRValue* arg = stackTop - argn; - c->gcPauseCount = arg->asInt(); - } -} - -//------------------------------------------------------------------------------ -void wr_loadSysLib( WRState* w ) -{ - wr_registerLibraryFunction( w, "sys::function", wr_function ); - wr_registerLibraryFunction( w, "sys::gcPause", wr_gcPause ); -}/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -//------------------------------------------------------------------------------ -void wr_stdSerialize( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - if ( argn ) - { - char* buf; - int len; - if ( wr_serialize(&buf, &len, *(stackTop - argn) ) ) - { - stackTop->p2 = INIT_AS_ARRAY; - stackTop->va = c->getSVA( 0, SV_CHAR, false ); - free( stackTop->va->m_Cdata ); - stackTop->va->m_data = buf; - stackTop->va->m_size = len; - } - } -} - -//------------------------------------------------------------------------------ -void wr_stdDeserialize( WRValue* stackTop, const int argn, WRContext* c ) -{ - stackTop->init(); - if ( argn ) - { - WRValue& V = (stackTop - argn)->deref(); - if ( IS_ARRAY(V.xtype) && V.va->m_type == SV_CHAR ) - { - if ( !wr_deserialize( c, *stackTop, V.va->m_SCdata, V.va->m_size ) ) - { - stackTop->init(); - } - } - } -} - -//------------------------------------------------------------------------------ -void wr_loadSerializeLib( WRState* w ) -{ - wr_registerLibraryFunction( w, "std::serialize", wr_stdSerialize ); - wr_registerLibraryFunction( w, "std::deserialize", wr_stdDeserialize ); -} -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#ifdef ARDUINO - -#include "wrench.h" - -#include -#include - -//------------------------------------------------------------------------------ -void wr_loadEsp32Lib( WRState* w ) -{ -} - -#endif -/******************************************************************************* -Copyright (c) 2022 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ - -#include "wrench.h" - -#ifdef ARDUINO - -#include -#include - -//------------------------------------------------------------------------------ -void wr_std_delay( WRValue* stackTop, const int argn, WRContext* c) -{ - // std::delay( milliseconds ) - if( argn == 1) - { - delay( (unsigned long)stackTop[-1].asInt() ); // a signed int will work up to 25 days - } -} - -//------------------------------------------------------------------------------ -void wr_io_pinMode( WRValue* stackTop, const int argn, WRContext* c) -{ - // io::pinMode( pin, <0 input, 1 output> ) - if( argn == 2) - { - pinMode( stackTop[-2].asInt(), (stackTop[-1].asInt() == 0) ? INPUT : OUTPUT ); - } -} - -//------------------------------------------------------------------------------ -void wr_io_digitalWrite( WRValue* stackTop, const int argn, WRContext* c) -{ - // io::digitalWrite( pin, value ) - if( argn == 2) - { - digitalWrite(stackTop[-2].asInt(), stackTop[-1].asInt()); - } -} - -//------------------------------------------------------------------------------ -void wr_io_analogRead( WRValue* stackTop, const int argn, WRContext* c) -{ - // io::analogRead( pin ) - if ( argn == 1) - { - stackTop->i = analogRead( stackTop[-1].asInt() ); - } -} - -//------------------------------------------------------------------------------ -void wr_lcd_begin( WRValue* stackTop, const int argn, WRContext* c) -{ - // lcd::begin( columns, rows ) - // returns status - if( argn == 2) - { -// stackTop->i = lcd.begin( stackTop[-2].asInt(), stackTop[-1].asInt()); - } -} - -//------------------------------------------------------------------------------ -void wr_lcd_setCursor( WRValue* stackTop, const int argn, WRContext* c) -{ - // lcd::setCursor( column, row ) - if( argn == 2 ) - { -// lcd.setCursor( stackTop[-2].asInt(), stackTop[-1].asInt()); - } -} - -//------------------------------------------------------------------------------ -void wr_lcd_print( WRValue* stackTop, const int argn, WRContext* c) -{ - // lcd::print( arg ) - // returns number of chars printed - if( argn == 1 ) - { -// char buf[61]; -// stackTop->i = lcd.print( stackTop[-1].asString(buf, sizeof(buf)-1)); - } -} - -//------------------------------------------------------------------------------ -void wr_wire_begin( WRValue* stackTop, const int argn, WRContext* c) -{ - Wire.begin(); -} - -//------------------------------------------------------------------------------ -void wr_wire_beginTransmission( WRValue* stackTop, const int argn, WRContext* c) -{ - if( argn == 1) - { - Wire.beginTransmission( stackTop[-1].asInt() ); - } -} - -//------------------------------------------------------------------------------ -void wr_wire_write( WRValue* stackTop, const int argn, WRContext* c) -{ - if( argn == 1) - { - Wire.write( stackTop[-1].asInt() ); - } - else if( argn == 2) - { - // To be added ! - } -} - -//------------------------------------------------------------------------------ -void wr_wire_endTransmission( WRValue* stackTop, const int argn, WRContext* c) -{ - if( argn == 0) - { - stackTop->i = Wire.endTransmission(); - } - else if( argn == 1) - { - stackTop->i = Wire.endTransmission( stackTop[-1].asInt() ); - } -} - -//------------------------------------------------------------------------------ -void wr_wire_requestFrom( WRValue* stackTop, const int argn, WRContext* c) -{ - // wire::requestFrom( address, bytes ) - if( argn == 2) - { - stackTop->i = Wire.requestFrom( stackTop[-2].asInt(), stackTop[-1].asInt() ); - } -} - -//------------------------------------------------------------------------------ -void wr_wire_available( WRValue* stackTop, const int argn, WRContext* c) -{ - stackTop->i = Wire.available(); -} - -//------------------------------------------------------------------------------ -void wr_wire_read( WRValue* stackTop, const int argn, WRContext* c) -{ - stackTop->i = Wire.read(); -} - -//------------------------------------------------------------------------------ -void wr_loadArduinoWireLib( WRState* w ) -{ - wr_registerLibraryFunction( w, "wire::begin", wr_wire_begin); - wr_registerLibraryFunction( w, "wire::beginTransmission", wr_wire_beginTransmission); - wr_registerLibraryFunction( w, "wire::write", wr_wire_write); - wr_registerLibraryFunction( w, "wire::endTransmission", wr_wire_endTransmission); - wr_registerLibraryFunction( w, "wire::requestFrom", wr_wire_requestFrom); - wr_registerLibraryFunction( w, "wire::available", wr_wire_available); - wr_registerLibraryFunction( w, "wire::read", wr_wire_read); -} - -//------------------------------------------------------------------------------ -void wr_loadArduinoSTDLib( WRState* w ) -{ - wr_registerLibraryFunction( w, "std::delay", wr_std_delay ); -} - -//------------------------------------------------------------------------------ -void wr_loadArduinoIOLib( WRState* w ) -{ - wr_registerLibraryFunction( w, "io::pinMode", wr_io_pinMode ); - wr_registerLibraryFunction( w, "io::digitalWrite", wr_io_digitalWrite ); - wr_registerLibraryFunction( w, "io::analogRead", wr_io_analogRead ); -} - -//------------------------------------------------------------------------------ -void wr_loadArduinoLCDLib( WRState* w ) -{ - wr_registerLibraryFunction( w, "lcd::begin", wr_lcd_begin ); - wr_registerLibraryFunction( w, "lcd::setCursor", wr_lcd_setCursor ); - wr_registerLibraryFunction( w, "lcd::print", wr_lcd_print ); -} - -//------------------------------------------------------------------------------ -void wr_loadArduinoLib( WRState* w ) -{ - wr_loadArduinoLCDLib( w ); - wr_loadArduinoIOLib( w ); - wr_loadArduinoSTDLib( w ); - wr_loadArduinoWireLib( w ); -} - -#endif diff --git a/src/wrench.h b/src/wrench.h deleted file mode 100644 index 9e09e2f..0000000 --- a/src/wrench.h +++ /dev/null @@ -1,972 +0,0 @@ -#define WRENCH_COMBINED -/******************************************************************************* -Copyright (c) 2023 Curt Hartung -- curt.hartung@gmail.com - -MIT Licence - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*******************************************************************************/ -#ifndef _WRENCH_H -#define _WRENCH_H -/*------------------------------------------------------------------------------*/ - -#define WRENCH_VERSION_MAJOR 3 -#define WRENCH_VERSION_MINOR 2 -#define WRENCH_VERSION_BUILD 5 - -/************************************************************************ -The compiler was not designed to be particularly memory or space efficient, for -small embedded systems it is strongly reccommeded (nay, required) that -only bytecode be executed. This flag allows the source code to be -explicitly unavailable. Esp32-class processors have no trouble compiling -on-the-fly but ATMega/SAMD21 are a no-go here. -*/ -//#define WRENCH_WITHOUT_COMPILER -/***********************************************************************/ - -/*********************************************************************** -Cause the interpreter to compile into the smallest program size -possible at the cost of some speed. This loss is from the removal of -unrolled loops and inlined functionality, it also makes -use of some goto spaghetti which can play havok with branch-prediction -and actually slow down PC-class processors. -WRENCH_REALLY_COMPACT reduces size further by removing the jumptable -interpreter in favor of a giant switch(). This saves ~6k at the cost -of a chunk of speed so only use it if you need to. -*/ -//#define WRENCH_COMPACT // saves a lot, costs some speed -//#define WRENCH_REALLY_COMPACT // saves a little more, costs more speed -/***********************************************************************/ - -/*********************************************************************** -Some architectures (most embedded) really don't like reading more than -8 bits on an unaligned memory location. If your architecture -allows it (PC, Mac, linux, unix etc..) this is a good optimization -*/ -//#define WRENCH_UNALIGNED_READS -/***********************************************************************/ - -/*********************************************************************** -wrench automatically detect endian-ness and defines these three -macros for reading data from the code stream, if you have special -memory requirements, as some embedded systems to, you may re-defines -them here, but they MUST match the endian-ness of the target -architecture, see vm.h for the current definitions -*/ -//#define READ_32_FROM_PC( P ) -//#define READ_16_FROM_PC( P ) -//#define READ_8_FROM_PC( P ) -/***********************************************************************/ - -/************************************************************************ -how many stack locations the wrench state will pre-allocate. The stack -cannot be grown so this needs to be enough for the life of the process. -this does NOT include global/array space which is allocated separately, just -function calls. Unless you are using piles of local data AND recursing -like crazy a modest size should be more than enough. - -This will consume 8 bytes per stack entry -*/ -#define WRENCH_DEFAULT_STACK_SIZE 64 -/***********************************************************************/ - -/************************************************************************ -including debug functionality incurs NO performance penalty on the VM - -..BUT - -it does add a small size penalty. If you are trying to cram wrench -into the smallest possible space, this compiles out the debug functions. -You can still run debug-enabled code, the VM will just skip it. -*/ -//!!!!!!!!!!!!! EXPERIMENTAL DO NOT USE -//#define WRENCH_INCLUDE_DEBUG_CODE - -/************************************************************************ -if you WANT full sprintf support for floats (%f/%g) this adds it, at -the cost of using the standard c library for it, not for small embedded -systems but larger ones? who cares. -*/ -//#define WRENCH_FLOAT_SPRINTF - -/***********************************************************************/ - -/************************************************************************ -File operations: define ONE of these. If you use "Custom" then you -need to link in a source file with the functions defined in: - - /discrete_src/std_io_defs.h - -examples are in - - /discrete_src/std_io_linux.cpp - /discrete_src/std_io_win32.cpp - /discrete_src/std_io_spiffs.cpp - /discrete_src/std_io_littlefs.cpp - -*/ -//#define WRENCH_WIN32_FILE_IO -//#define WRENCH_LINUX_FILE_IO -//#define WRENCH_SPIFFS_FILE_IO // !!!!!!!!!!!! PRE-RELEASE DO NOT USE -//#define WRENCH_LITTLEFS_FILE_IO // !!!!!!!!!!!! PRE-RELEASE DO NOT USE -//#define WRENCH_CUSTOM_FILE_IO -/***********************************************************************/ - -#include -#include - -struct WRState; -struct WRValue; -struct WRContext; -struct WRFunction; - -// hashing function used inside wrench, it's a stripped down murmer, -// not academically fantastic but very good and very fast -uint32_t wr_hash( const void* dat, const int len ); -uint32_t wr_hashStr( const char* dat ); - -// in order to minimize text segment size, only a minimum of strings -// are embedded in the code. error messages are very verbose enums -enum WRError -{ - WR_ERR_None = 0, - - WR_ERR_compiler_not_loaded, - WR_ERR_function_hash_signature_not_found, - WR_ERR_library_constant_not_loaded, - WR_ERR_unknown_opcode, - WR_ERR_unexpected_EOF, - WR_ERR_unexpected_token, - WR_ERR_bad_expression, - WR_ERR_bad_label, - WR_ERR_statement_expected, - WR_ERR_unterminated_string_literal, - WR_ERR_newline_in_string_literal, - WR_ERR_bad_string_escape_sequence, - WR_ERR_tried_to_load_non_resolvable, - WR_ERR_break_keyword_not_in_looping_structure, - WR_ERR_continue_keyword_not_in_looping_structure, - WR_ERR_expected_while, - WR_ERR_compiler_panic, - WR_ERR_constant_redefined, - WR_ERR_struct_in_struct, - - WR_ERR_run_must_be_called_by_itself_first, - WR_ERR_hash_table_size_exceeded, - WR_ERR_wrench_function_not_found, - WR_ERR_array_must_be_indexed, - WR_ERR_context_not_found, - - WR_ERR_usr_data_template_already_exists, - WR_ERR_usr_data_already_exists, - WR_ERR_usr_data_template_not_found, - WR_ERR_usr_data_refernce_not_found, - - WR_ERR_bad_goto_label, - WR_ERR_bad_goto_location, - WR_ERR_goto_target_not_found, - - WR_ERR_switch_with_no_cases, - WR_ERR_switch_case_or_default_expected, - WR_ERR_switch_construction_error, - WR_ERR_switch_bad_case_hash, - WR_ERR_switch_duplicate_case, - - WR_ERR_bad_bytecode_CRC, - - WR_ERR_execute_function_zero_called_more_than_once, - - WR_warning_enums_follow, - - WR_WARN_c_function_not_found, - WR_WARN_lib_function_not_found, -}; - -/***************************************************************/ -/**************************************************************/ -// State Management - -// create/destroy a WRState object that can run multiple contexts/threads -WRState* wr_newState( int stackSize =WRENCH_DEFAULT_STACK_SIZE ); -void wr_destroyState( WRState* w ); - - -/***************************************************************/ -/**************************************************************/ -// Running Code - -// compile source code and return a new'ed block of bytecode ready -// for wr_run(), this memory must be delete[]'ed -// return value is a WRError -// optionally an "errMsg" buffer can be passed which will output a -// human-readable string of what went wrong and where. -// compilerOptionFlags : OR'ed mask of of WrenchCompilerOptionFlags: -enum WrenchCompilerOptionFlags -{ - WR_INCLUDE_GLOBALS = 1<<0, // include all globals NOTE: wr_getGlobalRef(...) - // will not function without this - - WR_EMBED_DEBUG_CODE = 1<<1, // include per-instruction NOTE: WrenchDebugInterface - // will not function without this - - WR_EMBED_SOURCE_CODE = 1<<2,// include a copy of the source code -}; - -WRError wr_compile( const char* source, - const int size, - unsigned char** out, - int* outLen, - char* errMsg =0, - const unsigned int compilerOptionFlags = WR_INCLUDE_GLOBALS ); - -// w: state (see wr_newState) -// block: location of bytecode -// blockSize: number of bytes in the block - -// RETURNS: an allocated WRContext -// NOTE: This Context is automatically destyroyed when -// wr_destroyState() is called, but can be manually deleted -// with wr_destroyContext(...) (see below) -WRContext* wr_run( WRState* w, const unsigned char* block, const int blockSize ); - -// macro for automatically freeing the WRContext -#define wr_runOnce( w, block, blockSize ) wr_destroyContext( wr_run((w),(block),(blockSize)) ) - -// After wr_run(...) or wr_callFunction(...) has run, there is always a -// return value (default 0) this function fetches it -WRValue* wr_returnValueFromLastCall( WRState* w ); - -// wr_run actually calls these two functions back-to back. If you want -// to separate the process you can call them on your own. -WRContext* wr_newContext( WRState* w, const unsigned char* block, const int blockSize ); -WRValue* wr_executeContext( WRContext* context ); - -// after wr_run() this allows any function in the script to be -// called with the given arguments, returning a single value -// -// contextId: the context in which the function was loaded -// functionName: plaintext name of the function to be called -// argv: array of WRValues to call the function with (optional) -// argn: how many arguments argv contains (optional, but if -// zero then argv will be ignored) -// RETURNS: WRValue returned by the function/program, or NULL for -// error (see w->err for the code) -WRValue* wr_callFunction( WRContext* context, const char* functionName, const WRValue* argv =0, const int argn =0 ); - -// exactly the same as the above but the hash is supplied directly to -// save compute time, us wr_hashStr(...) to obtain the hash of the -// functionName -WRValue* wr_callFunction( WRContext* context, const int32_t hash, const WRValue* argv =0, const int argn =0 ); - -// The raw function pointer can be pre-loaded with wr_getFunction() and -// passed directly -// This method is exposed so functions can be called with an absolute -// minimum of overhead -WRValue* wr_callFunction( WRContext* context, WRFunction* function, const WRValue* argv =0, const int argn =0 ); - -// after wrench executes it may have set an error code, this is how to -// retreive it. This sytems is coarse at the moment. Re-entering the -// interpreter clears the last error -WRError wr_getLastError( WRState* w ); - -// once wr_run() is called the returned context object can be used to -// pre-fetch a function pointer. This reduces the overhead of calling -// that function to almost nothing. -WRFunction* wr_getFunction( WRContext* context, const char* functionName ); - -// want direct access to a global? okay then, if the code was compiled -// with symbols (see compile(...) suite) then this will give you a -// reference to it -WRValue* wr_getGlobalRef( WRContext* context, const char* label ); - -// Destroy a context you no longer need and free up all the memory it -// was using -// NOTE: all contexts ARE AUTOMATICALLY FREED when wr_destroyState(...) -// is called, it it NOT necessary to call this on each context -void wr_destroyContext( WRContext* context ); - - -/***************************************************************/ -/***************************************************************/ -// Callbacks from wrench - -// register a function inside a state that can be called (by ALL -// contexts) -// callback will contain: -// c: context called from -// argv: pointer to arguments function was called -// with (may be null!) -// argn: how many arguments it was called with -// retVal: this value will be passed back, default: integer zero -// usr: opaque pointer function was registered with -typedef void (*WR_C_CALLBACK)(WRContext* c, const WRValue* argv, const int argn, WRValue& retVal, void* usr ); - -// IMPORTANT: The values passed may be references (keepin' it real) so -// always use the getters inside the WRValue class: - -// int: .asInt(); -// float: .asFloat(); -// binary: .array( unsigned int* size, char* type ); -// string: .c_str( unsigned int* len ); - -// tests: -// .isFloat() fast check if this is a float value -// .isInt() fast check if this is a float value - - -// this will do its pest to represent the value as a string to the -// supplied buffer, len is maximum size allowed -// string: .asString(char* string, size_t len ) - -// w: state to register with (will be available to all contexts) -// name: name of the function -// function: callback (see typdef above) -// usr: opaque pointer that will be passed to the callback (optional) -void wr_registerFunction( WRState* w, const char* name, WR_C_CALLBACK function, void* usr =0 ); - -// serialize WRValues to and from binary -// NOTE: on success, wr_serialize returns a malloc()'d buffer that must be freed! -bool wr_serialize( char** buf, int* len, const WRValue& value ); -bool wr_deserialize( WRContext* context, WRValue& value, const char* buf, const int len ); - -/***************************************************************/ -/***************************************************************/ -// wrench Library calls - -// Wrench allows libraries to be loaded in the form of :: -// these calls impose no overhead if not used - -// library calls do assume a deep knowledge of the internal workings of -// wrench so the callback is an absolute minimum of info: -// stackTop: current top of the stack, arguments have been pushed here, -// and any return value must be placed here, not pushed! see -// one of the many library examples -// argn: how many arguments this function was called with. no -// guarantees are made about matching a function signature, -// library calls validate if they care. -// stackTop : [return value] -// stackTop - 1 : [argN ] -// stackTop - 2 : [argN-1] -// ... -// stackTop - (N-1) : [arg2] -// stackTop - N : [arg1] -typedef void (*WR_LIB_CALLBACK)( WRValue* stackTop, const int argn, WRContext* context ); - -// w: state to register with (will be available to all contexts) -// signature: library signature must be in the form of :: -// ex: "math::cos" -// function: callback (see typdef above) -void wr_registerLibraryFunction( WRState* w, const char* signature, WR_LIB_CALLBACK function ); -void wr_registerLibraryConstant( WRState* w, const char* signature, const WRValue& value ); - -// you can write your own libraries (look at std.cpp for all the -// standard ones) but several have been provided: - -void wr_loadAllLibs( WRState* w ); // load all libraries in one call - -void wr_loadSysLib( WRState* w ); // system/internal functions -void wr_loadMathLib( WRState* w ); // provides most of the calls in math.h -void wr_loadStdLib( WRState* w ); // standard functions like sprintf/rand/ -void wr_loadIOLib( WRState* w ); // IO funcs (time/file/io) -void wr_loadStringLib( WRState* w ); // string functions -void wr_loadMessageLib( WRState* w ); // messaging between contexts -void wr_loadSerializeLib( WRState* w ); // serialize WRValues to and from binary - -// arduino-specific functions, be sure to add arduino_lib.cpp to your -// sketch. much thanks to Koepel for contributing -void wr_loadAllArduinoLibs( WRState* w ); - -void wr_loadArduinoSTDLib( WRState* w ); -void wr_loadArduinoIOLib( WRState* w ); -void wr_loadArduinoLCDLib( WRState* w ); - -/***************************************************************/ -/***************************************************************/ -// wrench types and how to make them! - -// The WRValue is the basic data unit inside wrench, it can be a -// float/int/array whatever... think of it as a thneed. It's 8 bytes on -// a 32 bit system and you can create one on the stack no problemo, no -// heap will be harmed - -// load a value up and make it ready for calling a function -WRValue& wr_makeInt( WRValue* val, int i ); -WRValue& wr_makeFloat( WRValue* val, float f ); - -// a string has to exist in a context so it can be worked with -WRValue& wr_makeString( WRContext* context, WRValue* val, const char* data, const int len =0 ); - -// turning a value into a container allocates a hash table which must -// be released with destroy! -void wr_makeContainer( WRValue* val, const uint16_t sizeHint =0 ); -void wr_destroyContainer( WRValue* val ); - -void wr_addValueToContainer( WRValue* container, const char* name, WRValue* value ); -void wr_addIntToContainer( WRValue* container, const char* name, const int32_t value ); -void wr_addFloatToContainer( WRValue* container, const char* name, const float value ); -void wr_addArrayToContainer( WRValue* container, const char* name, char* array, const uint32_t size ); - - - -/******************************************************************/ -// "standard" functions - -extern int32_t wr_Seed; - -// inside-baseball time. This has to be here so stuff that includes -// "wrench.h" can create a WRValue. - -/* - - wrench values have a 32-bit type vector - -type bits: XXXX XXXX ____ ____ ____ ____ __TT - [8-bit type] - [ 32-bit xtype ] - -the bottom two bits 'T' represent the main type: -int 00 -- integer, ui / i are the value -float 01 -- float, f is the value -reference 10 -- reference, *r is a pointer to the real value -extended 11 -- extended type, see upper bits. - -This is so operation tables can be constructed 4x4 for very quick -resolution of how to add/subtract/AND/etc... two values without a -time-consuming if-else-tree. single bit shift + OR and we're there: - - (typeA << 2) | typeB - -Always results in a value from 0-15 which can index into a function -table: - - FunctionTypedef wr_func[16] = - { - do_I_I, do_I_F, do_I_R, do_I_E, - do_F_I, do_F_F, do_F_R, do_F_E, - do_R_I, do_R_F, do_R_R, do_R_E, - do_E_I, do_E_F, do_E_R, do_E_E, - }; - -such that -wr_func[ (typeA << 2) | typeB ]( look ma, no ifs! ); - -In the case of "extended" types these are the ones that take extra -logic: arrays and hashes. - -The "extended" types are: - -(x is "don't care") - -0x20xxxxxx raw array: r->c points to a flat array, and - r->p2 holds the size of the array in the - middle 21 bits: - - 0x1FFFFF00 - - such that shifting them >>8 yields - the actual array size - This is done so the value is never garbage - collected, or matched to the wrong type, but - does limit the size of an array to 2megabytes - -0x40xxxxxx debug break: stop execution and do debugging work - -0x60xxxxxx iterator: p2 holds the element we are on - va holds the array to be iterated - -0x80xxxxxx reference array: refers to a particular element of an - array. - - r-> points to the array we are holding an - element of - - p2 holds the element in the middle 21 - bits: - - 0x1FFFFF00 - - such that shifting them >>8 yields - the actual element - - This is done so the value is never garbage - collected, or matched to the wrong type, but - does limit the size of an array to 2megabytes - -0xA0xxxxxx array/utility hash: - va-> holds a pointer to the actual array object (gc_object) - which has been allocated and is subject to - garbage collection - - there are 4 kinds of "array" objects: - - SV_VALUE 0x01 array of WRValues - SV_CHAR 0x02 array of chars (this - is how strings are represented) - SV_VOID_HASH_TABLE 0x04 hash table of void* - used for internal structures - (do not gc) - SV_HASH_TABLE 0x03 hash table of WRValues - (DO descend for gc) - -0xC0xxxxxx struct: This value is a constructed "struct" object with a - hash table of values - - va-> refers to the container of initialized objects - - -0xE0xxxxxx hash table: hash table of WRValues - - va-> refers to the hash table object - -*/ - -//------------------------------------------------------------------------------ -#if __cplusplus <= 199711L -enum WRValueType -#else -enum WRValueType : uint8_t -#endif -{ - WR_INT = 0x00, - WR_FLOAT = 0x01, - WR_REF = 0x02, - WR_EX = 0x03, -}; - -//------------------------------------------------------------------------------ -#if __cplusplus <= 199711L -enum WRExType -#else -enum WRExType : uint8_t -#endif -{ - WR_EX_NONE = 0x00, // 0000 - - WR_EX_RAW_ARRAY = 0x20, // 0010 - - // EX types have one of the upper two bits set - WR_EX_DEBUG_BREAK= 0x40, // 0100 - - WR_EX_ITERATOR = 0x60, // 0110 - WR_EX_ARRAY_MEMBER = 0x80, // 1000 - WR_EX_ARRAY = 0xA0, // 1010 - - // see EXPECTS_HASH_INDEX!! - WR_EX_STRUCT = 0xC0, // 1100 - WR_EX_HASH_TABLE = 0xE0, // 1110 -}; - -//------------------------------------------------------------------------------ -enum WRGCObjectType -{ - SV_VALUE = 0x01, // 0001 - SV_CHAR = 0x02, // 0010 - SV_HASH_TABLE = 0x03, // 0011 - SV_VOID_HASH_TABLE = 0x04, // 0100 // used for wrench internals, not meant for VM to care about -}; - -#define EX_TYPE_MASK 0xE0 -#define IS_DEBUG_BREAK(X) ((X)==WR_EX_DEBUG_BREAK) -#define IS_ARRAY_MEMBER(X) (((X)&EX_TYPE_MASK)==WR_EX_ARRAY_MEMBER) -#define IS_ARRAY(X) ((X)==WR_EX_ARRAY) -#define IS_ITERATOR(X) ((X)==WR_EX_ITERATOR) -#define IS_RAW_ARRAY(X) (((X)&EX_TYPE_MASK)==WR_EX_RAW_ARRAY) -#define IS_HASH_TABLE(X) ((X)==WR_EX_HASH_TABLE) - -#define EXPECTS_HASH_INDEX(X) ( ((X) == WR_EX_STRUCT) || ((X)==WR_EX_HASH_TABLE) ) - -#if !defined(ARDUINO) && (__arm__ || WIN32 || _WIN32 || __linux__ || __MINGW32__ || __APPLE__ || __MINGW64__ || __clang__ || __GNUC__) -#include -#include -#include -#include -#include -#include -#include -#endif - -#ifdef ARDUINO -#include -#ifdef BYTE_ORDER -#if BYTE_ORDER == LITTLE_ENDIAN -#define ARDUINO_LITTLE_ENDIAN -#elif BYTE_ORDER == BIG_ENDIAN -#define ARDUINO_BIG_ENDIAN -#else -#error -#endif - -#else - -#define TEST_LITTLE_ENDIAN (((union { unsigned x; unsigned char c; }){1}).c) -#ifdef TEST_LITTLE_ENDIAN -#define ARDUINO_LITTLE_ENDIAN -#else -#define ARDUINO_BIG_ENDIAN -#endif - -#endif -#endif - -#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \ - defined(__BIG_ENDIAN__) || \ - defined(__ARMEB__) || \ - defined(__THUMBEB__) || \ - defined(__AARCH64EB__) || \ - defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) || \ - defined(ARDUINO_BIG_ENDIAN) -#define WRENCH_BIG_ENDIAN -#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \ - defined(__LITTLE_ENDIAN__) || \ - defined(__ARMEL__) || \ - defined(_M_PPC) || \ - defined(__THUMBEL__) || \ - defined(__AARCH64EL__) || \ - defined(_MSC_VER) || \ - defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \ - defined(ARDUINO_LITTLE_ENDIAN) -#define WRENCH_LITTLE_ENDIAN -#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - #define WRENCH_LITTLE_ENDIAN -#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - #define WRENCH_BIG_ENDIAN - -#else - -// if you are reading this, please define the appropriate endian-ness -// here for your compiler(WRENCH_LITTLE_ENDIAN / WRENCH_BIG_ENDIAN) and: -#error "Endian-ness not detected! Please contact curt.hartung@gmail.com so it can be added" - -#endif - -class WRGCObject; - -//------------------------------------------------------------------------------ -struct WRValue -{ - // never reference the data members directly, they are unions and - // bad things will happen. Always access them with one of these - // methods - int asInt() const; - void setInt( const int val ); - - float asFloat() const; - void setFloat( const float val ); - - bool isFloat() const { return type == WR_FLOAT || (type == WR_REF && r->type == WR_FLOAT); } - bool isInt() const { return type == WR_INT || (type == WR_REF && r->type == WR_INT); } - bool isWrenchArray() const { return IS_ARRAY(xtype); } - bool isRawArray() const { return IS_RAW_ARRAY(xtype); } - bool isHashTable() const { return IS_HASH_TABLE(xtype); } - - // if this value is an array, return [or create] the 'index'-th element - // if create is true and this value is NOT an array, it will be converted into one - WRValue* indexArray( WRContext* context, const uint32_t index, const bool create ); - - // if this value is a hash table, return [or create] the 'index' hash item - // if create is true and this value is NOT a hash, it will be converted into one - WRValue* indexHash( WRContext* context, const uint32_t hash, const bool create ); - - // string: must point to a buffer long enough to contain at least maxLen bytes. - // the "string" pointer will be passed back, if maxLen is 0 (not - // reccomended) the string is assumed to be unlimited size - char* asString( char* string, size_t maxLen =0 ) const; - - // return a raw pointer to the data array if this is one, otherwise null - void* array( unsigned int* len =0, char arrayType =SV_CHAR ) const; - - inline void init() { p = 0; p2 = 0; } // call upon first create or when you're sure no memory is hanging from one - -//private: // is what this SHOULD be.. but that's impractical since the - // VM is not an object that can be friended. - - WRValue& singleValue() const; // return a single value for comparison - WRValue& deref() const; // if this value is a ARRAY_MEMBER, copy the referred value in - - uint32_t getHashEx() const; // harder - uint32_t getHash() const // for easy cases - { - if ( type <= WR_FLOAT ) - { - return ui; - } - - return getHashEx(); - } - - union // first 4 bytes (or 8 on a 64-bit system but.. what are you doing here?) - { - int32_t i; - uint32_t ui; - WRValue* frame; - float f; - const void* p; - void* r1; - char* c; - WRValue* r; - WRGCObject* va; - WR_C_CALLBACK ccb; - WR_LIB_CALLBACK lcb; - WRFunction* wrf; - }; - - union - { -#ifdef WRENCH_LITTLE_ENDIAN - struct - { -#if (__cplusplus <= 199711L) - uint8_t type; // carries the type - uint8_t padL; // pad carries an array pointer (up to 2megs using bottom 4 bits of xtype) - uint8_t padH; - uint8_t xtype; // carries the extra type (if it exists) -#else - WRValueType type; // carries the type - uint8_t padL; // pad carries an array pointer (up to 2megs using bottom 4 bits of xtype) - uint8_t padH; - WRExType xtype; // carries the extra type (if it exists) -#endif - }; -#else // WRENCH_BIG_ENDIAN - struct - { -#if (__cplusplus <= 199711L) - uint8_t xtype; // carries the extra type (if it exists) - uint8_t padH; - uint8_t padL; // pad carries an array pointer (up to 2megs using bottom 4 bits of xtype) - uint8_t type; // carries the type -#else - WRExType xtype; // carries the extra type (if it exists) - uint8_t padH; - uint8_t padL; // pad carries an array pointer (up to 2megs using bottom 4 bits of xtype) - WRValueType type; // carries the type -#endif - }; -#endif - - uint32_t p2; - union - { - int returnOffset; - WRValue* r2; - void* usr; - }; - }; - - inline WRValue& operator= (const WRValue& V) { r1 = V.r1; r2 = V.r2; return *this; } -}; - -//------------------------------------------------------------------------------ -// Helper class to represent a wrench value, in all cases it does NOT -// managerthe memory, but relies on a WRContext to do that -class WrenchValue -{ -public: - - WrenchValue( WRContext* context, const char* label ) : m_context(context), m_value( wr_getGlobalRef(context, label) ) {} - WrenchValue( WRContext* context, WRValue* value ) : m_context(context), m_value(value) {} - - bool isValid() const { return m_value ? true : false; } - - // if the value is not the correct type it will be converted to - // that type, preserving the value as best it can - operator int32_t* () { return asInt(); } - int32_t* asInt() { return (m_value->type == WR_INT) ? &(m_value->i) : makeInt(); } - - operator float* () { return asFloat(); } - float* asFloat() { return (m_value->type == WR_FLOAT) ? &(m_value->f) : makeFloat(); } - - // this will convert it to an array if it isn't one - WRValue& operator[] ( const int index ) { return *asArrayMember( index ); } - WRValue* asArrayMember( const int index ); - int arraySize(); // returns -1 if this is not an array - - // convert the value in-place and return a pointer to it - int32_t* makeInt(); - float* makeFloat(); - -private: - WRContext* m_context; - WRValue* m_value; -}; - -struct WrenchPacket; -struct WrenchSymbol; -struct WrenchFunction; - -template class SimpleLL; - -//------------------------------------------------------------------------------ -class WRDebugServerInterface -{ -public: - - WRDebugServerInterface( WRState* w, - const unsigned char* bytes =0, - const int len =0, - bool (*receiveFunction)( char* data, const size_t length, const int timeoutMilliseconds ) =0, - size_t (*writeFunction)( const char* data, const size_t length ) =0 ); - - ~WRDebugServerInterface(); - - WRContext* loadBytes( const unsigned char* bytes, const int len ); - char* m_localBytes; - int m_localBytesLen; - -//private: again.. TECHNICALLY.. but we are keeping things simple - - int m_steppingOverReturnVector; - int m_lineSteps; - int m_opcodeSteps; - - struct LineBreak - { - int line; - LineBreak* next; - }; - SimpleLL* m_lineBreaks; - SimpleLL* m_packetQ; - - void codewordEncountered( uint16_t codeword, WRValue* stackTop ); - - void processPacket( WrenchPacket* packet ); - - WRState* m_w; - bool (*m_receiveFunction)( char* data, const size_t length, const int timeoutMilliseconds ); - size_t (*m_writeFunction)( const char* data, const size_t length ); - - int m_onLine; - int m_onFunction; - bool m_firstCall; - - const unsigned char* m_embeddedSource; - int m_embeddedSourceSize; - int m_embeddedSourceHash; - - const unsigned char* m_symbolBlock; - int m_symbolBlockSize; - - WRContext* m_context; - const WRValue* m_argv; - int m_argn; - const unsigned char* m_pc; - WRValue* m_frameBase; - WRValue* m_stackTop; - - bool m_brk; -}; - -//------------------------------------------------------------------------------ -class WRDebugClientInterface -{ -public: - - WRDebugClientInterface( size_t (*receiveFunction)( char* data, const size_t length, const int timeoutMilliseconds ), - size_t (*writeFunction)( const char* data, const size_t length ) ); - - WRDebugClientInterface( WRDebugServerInterface* localServer ); - - ~WRDebugClientInterface(); - -// const WRDebugMessage& status(); - - // load and prepare a program for running - // if byteCode/size is null then it re-loads it's existing program - void load( const char* byteCode =0, const int size =0 ); - void run(); // run or continue - - bool getSourceCode( const char** data, int* len ); - uint32_t getSourceCodeHash(); - - const WRValue* getGlobalValue( const char* name ); - const char* getGlobalName( const int index ); - void setGlobalValue( const char* name, WRValue const& value ); - - const WRValue* getLocalValue( const char* name ); - const char* getLocalName( const int index ); - void setLocalValue( const char* name, WRValue const& value ); - - void stepOverLine( const int steps =1 ); - void stepOverOpcode( const int steps =1 ); - - void stepIntoLine( const int steps =1 ); - void stepIntoOpcode( const int steps =1 ); - - void setBreakpoint( const int lineNumber ); - void clearBreakpoint( const int lineNumber ); - -//private: - - bool transmit( WrenchPacket& packet );\ - WrenchPacket* receive( const int timeoutMilliseconds =0 ); - - void loadSourceBlock(); - void loadSymbols(); - - void init(); - - char* m_sourceBlock; - int m_sourceBlockLen; - uint32_t m_sourceBlockHash; - WrenchPacket* m_packet; - - SimpleLL* m_packetQ; - - void populateSymbols( const char* block, const int size ); - SimpleLL* m_globals; - SimpleLL* m_functions; - bool m_symbolsLoaded; - - WRDebugServerInterface* m_localServer; - size_t (*m_receiveFunction)( char* data, const size_t length, const int timeoutMilliseconds ); - size_t (*m_writeFunction)( const char* data, const size_t length ); -}; - - -#ifdef WRENCH_REALLY_COMPACT -#ifndef WRENCH_COMPACT -#define WRENCH_COMPACT -#endif -#endif - -#define WRENCH_JUMPTABLE_INTERPRETER -#ifdef WRENCH_REALLY_COMPACT - #undef WRENCH_JUMPTABLE_INTERPRETER -#elif !defined(__clang__) - #if _MSC_VER - #undef WRENCH_JUMPTABLE_INTERPRETER - #endif -#endif - -#ifndef WRENCH_COMBINED -#include "utils.h" -#include "simple_ll.h" -#include "gc_object.h" -#include "serializer.h" -#include "vm.h" -#include "opcode.h" -#include "str.h" -#include "opcode_stream.h" -#include "wrench_debug.h" -#include "cc.h" -#include "std_io_defs.h" -#endif - -#endif - diff --git a/test/README b/test/README new file mode 100644 index 0000000..43b5d13 --- /dev/null +++ b/test/README @@ -0,0 +1,37 @@ + +This directory contains the firmware's host-side regression suites and release +boundary checks. They are deliberately compiled outside PlatformIO so a pull +request fails on ordinary C++ warnings as well as test failures: + +```sh +mkdir -p /tmp/ambit-host-tests +c++ -std=c++11 -Wall -Wextra -Werror test/calibration/test_main.cpp \ + -o /tmp/ambit-host-tests/calibration +/tmp/ambit-host-tests/calibration +c++ -std=c++11 -Wall -Wextra -Werror \ + test/adpd6000/test_main.cpp src/src/adpd/jii_adpd6000.cpp \ + -o /tmp/ambit-host-tests/adpd6000 +/tmp/ambit-host-tests/adpd6000 +python3 -m unittest discover -s test/release_process -p 'test_*.py' -v +``` + +Install the archive-hash-pinned release tooling, then run the exact installed +GitHub release-plugin proof too: + +```sh +bash tools/install_release_tooling.sh +node test/release_process/semantic_release_github_draft.mjs +``` + +That mock-Octokit test proves `@semantic-release/github@11.0.6` creates a +draft, uploads its configured assets, returns without a publication PATCH, and +does not publish in its later success phase. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html diff --git a/test/adpd6000/test_main.cpp b/test/adpd6000/test_main.cpp new file mode 100644 index 0000000..0585aef --- /dev/null +++ b/test/adpd6000/test_main.cpp @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: CERN-OHL-S-2.0 +// Copyright (c) 2026 Jan IngenHousz Institute + +#include + +#include +#include +#include +#include +#include + +#include "../../src/src/adpd/jii_adpd6000.h" + +namespace { + +using jii::adpd6000::Driver; +using jii::adpd6000::Transport; + +struct Operation { + uint16_t command; + std::vector transmitted; + size_t received_length; + bool receive_buffer_word_aligned; +}; + +struct FakeTransport { + std::map registers; + std::deque fifo; + std::vector operations; + size_t fail_on_call = 0; + int32_t failure = -77; + bool emulate_dma_word_write = false; + + static int32_t transact(void *context, uint16_t command, + const uint8_t *tx_data, uint8_t *rx_data, + size_t data_length) { + FakeTransport &self = *static_cast(context); + Operation operation = { + command, + {}, + rx_data == nullptr ? 0 : data_length, + rx_data == nullptr || + (reinterpret_cast(rx_data) % sizeof(uint32_t)) == 0}; + if (tx_data != nullptr) { + operation.transmitted.assign(tx_data, tx_data + data_length); + } + self.operations.push_back(operation); + if (self.fail_on_call != 0 && self.operations.size() == self.fail_on_call) { + return self.failure; + } + + const bool write = (command & 1U) != 0; + const uint16_t address = command >> 1; + if (write) { + if (tx_data == nullptr || data_length != 2) { + return -88; + } + const uint16_t value = static_cast( + (static_cast(tx_data[0]) << 8) | tx_data[1]); + self.registers[address] = value; + if (address == jii::adpd6000::reg::kFifoStatus && + (value & 0x8000U) != 0) { + self.fifo.clear(); + } + return 0; + } + + if (rx_data == nullptr) { + return -89; + } + if (address == jii::adpd6000::reg::kFifoData) { + if (self.fifo.size() < data_length) { + return -90; + } + for (size_t i = 0; i < data_length; ++i) { + rx_data[i] = self.fifo.front(); + self.fifo.pop_front(); + } + return 0; + } + if (data_length != 2) { + return -91; + } + uint16_t value = self.registers[address]; + if (address == jii::adpd6000::reg::kFifoStatus) { + value = static_cast((value & 0xF800U) | self.fifo.size()); + } + rx_data[0] = static_cast(value >> 8); + rx_data[1] = static_cast(value & 0xFF); + if (self.emulate_dma_word_write) { + // ESP-IDF documents that SPI DMA writes RX buffers in four-byte units, + // even when the requested transfer length is only two bytes. + rx_data[2] = 0xA5; + rx_data[3] = 0x5A; + } + return 0; + } + + Driver driver() { + return Driver(Transport{this, &FakeTransport::transact}); + } +}; + +int failures = 0; + +void check(bool condition, const std::string &message) { + if (!condition) { + ++failures; + std::cerr << "FAIL: " << message << '\n'; + } +} + +void test_spi_framing_and_register_byte_order() { + uint8_t bytes[2] = {0xFF, 0xFF}; + check(Driver::encode_spi_command(jii::adpd6000::reg::kChipId, false, + bytes) == 0, + "read command encodes"); + check(bytes[0] == 0x00 && bytes[1] == 0x10, + "CHIP_ID read is address<<1 with R/W=0"); + check(Driver::encode_spi_command(jii::adpd6000::reg::kSystemControl, + true, bytes) == 0, + "write command encodes"); + check(bytes[0] == 0x00 && bytes[1] == 0x1F, + "SYS_CTL write is address<<1 with R/W=1"); + check(Driver::encode_spi_command(jii::adpd6000::kMaxRegisterAddress, + true, bytes) == 0 && + bytes[0] == 0xFF && bytes[1] == 0xFF, + "maximum 15-bit register address is accepted"); + check(Driver::encode_spi_command( + static_cast(jii::adpd6000::kMaxRegisterAddress + 1U), + false, bytes) == jii::adpd6000::kInvalidArgument, + "register address above 15 bits is rejected"); + + FakeTransport fake; + Driver driver = fake.driver(); + check(driver.write_register(0x0128, 0xBEEF) == 0, + "register write succeeds"); + check(fake.operations.back().command == 0x0251, + "write command reaches transport unchanged"); + check(fake.operations.back().transmitted == std::vector({0xBE, 0xEF}), + "register value is transmitted MSB first"); + + fake.registers[0x0128] = 0xCAFE; + fake.emulate_dma_word_write = true; + uint16_t value = 0; + check(driver.read_register(0x0128, &value) == 0 && value == 0xCAFE, + "register read reconstructs MSB-first value"); + check(fake.operations.back().received_length == 2, + "register read keeps the wire receive length at two bytes"); + check(fake.operations.back().receive_buffer_word_aligned, + "register read provides a four-byte-aligned DMA receive buffer"); +} + +void test_masking_reset_and_run_transitions() { + FakeTransport fake; + Driver driver = fake.driver(); + fake.registers[jii::adpd6000::reg::kOperatingMode] = 0xA5F8; + + check(driver.set_running(true) == 0, "run transition succeeds"); + check(fake.registers[jii::adpd6000::reg::kOperatingMode] == 0xA5F9, + "run changes only OP_MODE bits"); + check(driver.set_running(false) == 0, "stop transition succeeds"); + check(fake.registers[jii::adpd6000::reg::kOperatingMode] == 0xA5F8, + "stop preserves enabled-slot and unrelated bits"); + check(driver.set_optical_slot_count(3) == 0, + "optical slot count accepts three slots"); + check(fake.registers[jii::adpd6000::reg::kOperatingMode] == 0xA3F8, + "slot count changes only PPG_TIMESLOT_EN"); + check(driver.set_optical_slot_count(12) == 0, + "optical slot count accepts the twelve-slot boundary"); + check(fake.registers[jii::adpd6000::reg::kOperatingMode] == 0xACF8, + "twelve-slot boundary preserves unrelated OPMODE bits"); + check(driver.set_optical_slot_count(13) == jii::adpd6000::kOutOfRange, + "optical slot count rejects values above twelve"); + + check(driver.software_reset() == 0, "software reset write succeeds"); + check(fake.registers[jii::adpd6000::reg::kSystemControl] == 0x8000, + "software reset asserts SYS_CTL.SW_RESET"); +} + +void test_fifo_operations() { + FakeTransport fake; + Driver driver = fake.driver(); + fake.fifo = {0x01, 0x23, 0x45, 0x67, 0x89}; + + uint16_t count = 0; + check(driver.fifo_byte_count(&count) == 0 && count == 5, + "FIFO byte count is read from FIFO_STATUS"); + uint8_t data[3] = {}; + check(driver.read_fifo(data, sizeof(data)) == 0, + "byte-granular FIFO read succeeds"); + check(data[0] == 0x01 && data[1] == 0x23 && data[2] == 0x45, + "FIFO preserves byte stream order"); + check(fake.operations.back().command == 0x005E, + "FIFO read uses the fixed FIFO_DATA read command"); + + uint8_t over_capacity = 0; + const size_t before_invalid_read = fake.operations.size(); + check(driver.read_fifo(&over_capacity, + jii::adpd6000::kFifoCapacityBytes + 1U) == + jii::adpd6000::kInvalidArgument, + "FIFO read rejects lengths past the hardware capacity"); + check(fake.operations.size() == before_invalid_read, + "invalid FIFO length does not reach the transport"); + + const size_t before_clear = fake.operations.size(); + check(driver.clear_fifo() == 0, "FIFO clear succeeds"); + check(fake.fifo.empty(), "FIFO clear empties fake FIFO"); + check(fake.operations.size() == before_clear + 2, + "FIFO clear asserts then deasserts CLEAR_FIFO"); + check(fake.operations[before_clear].transmitted == + std::vector({0x80, 0x00}) && + fake.operations[before_clear + 1].transmitted == + std::vector({0x00, 0x00}), + "FIFO clear transition writes the specified command values"); +} + +void test_fifo_sample_failure_clears_stale_outputs() { + FakeTransport fake; + Driver driver = fake.driver(); + fake.fifo = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB}; + fake.fail_on_call = 2; + uint32_t samples[3] = {0xAAAAAAAAU, 0xBBBBBBBBU, 0xCCCCCCCCU}; + + check(driver.read_fifo_samples(3, 3, samples) == fake.failure, + "sample FIFO read propagates a mid-run transport failure"); + check(samples[0] == 0x012345U, + "samples completed before the failure remain available"); + check(samples[1] == 0 && samples[2] == 0, + "failed and remaining samples are cleared instead of staying stale"); + check(fake.operations.size() == 2, + "sample FIFO read stops transport work at the first failure"); +} + +void test_frequency_bounds_and_identity() { + FakeTransport fake; + Driver driver = fake.driver(); + + check(driver.set_slot_frequency(960000, 100) == 0, + "100 Hz slot frequency succeeds"); + check(fake.registers[jii::adpd6000::reg::kTimeslotFrequencyLow] == 0x2580 && + fake.registers[jii::adpd6000::reg::kTimeslotFrequencyHigh] == 0, + "slot period is split across low and high registers"); + check(driver.set_slot_frequency(960000, 0) == jii::adpd6000::kOutOfRange, + "zero frequency is rejected"); + check(driver.set_slot_frequency(960000, 960001) == + jii::adpd6000::kOutOfRange, + "frequency above timer clock is rejected"); + check(driver.set_slot_frequency(0xFFFFFFFFU, 1) == + jii::adpd6000::kOutOfRange, + "period wider than 23 bits is rejected"); + + fake.registers[jii::adpd6000::reg::kChipId] = 0x02C4; + uint8_t chip = 0; + uint8_t version = 0; + check(driver.identify(&chip, &version) == 0 && chip == 0xC4 && version == 2, + "identity separates chip ID and silicon version bytes"); +} + +void test_transport_error_propagation() { + FakeTransport fake; + Driver driver = fake.driver(); + fake.fail_on_call = 1; + + uint16_t value = 0; + check(driver.read_register(0x0008, &value) == fake.failure, + "register read propagates transport error"); + check(fake.operations.size() == 1, "failed read performs one transaction"); + + fake.operations.clear(); + fake.fail_on_call = 2; + fake.registers[jii::adpd6000::reg::kOperatingMode] = 0x1200; + check(driver.set_running(true) == fake.failure, + "read-modify-write propagates write error"); + check(fake.operations.size() == 2, + "read-modify-write stops after failed write"); + + fake.operations.clear(); + fake.fail_on_call = 1; + check(driver.clear_fifo() == fake.failure, + "FIFO clear propagates first transport error"); + check(fake.operations.size() == 1, + "FIFO clear does not deassert after failed assertion"); +} + +} // namespace + +int main() { + test_spi_framing_and_register_byte_order(); + test_masking_reset_and_run_transitions(); + test_fifo_operations(); + test_fifo_sample_failure_clears_stale_outputs(); + test_frequency_bounds_and_identity(); + test_transport_error_propagation(); + + if (failures != 0) { + std::cerr << failures << " test(s) failed\n"; + return 1; + } + std::cout << "ADPD6000 fake-transport tests passed\n"; + return 0; +} diff --git a/test/calibration/test_main.cpp b/test/calibration/test_main.cpp new file mode 100644 index 0000000..1859e61 --- /dev/null +++ b/test/calibration/test_main.cpp @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: CERN-OHL-S-2.0 +// Copyright (c) 2026 Jan IngenHousz Institute + +#include + +#include +#include +#include + +#include "../../src/calibration_math.h" + +namespace { + +int failures = 0; + +void check(bool condition, const std::string &message) { + if (!condition) { + ++failures; + std::cerr << "FAIL: " << message << '\n'; + } +} + +void test_legacy_defaults() { + const uint32_t offsets[ambit_calibration::CHANNEL_COUNT] = {0, 0, 0, 0, 0, 0}; + check(ambit_calibration::apply_adpd_offset(ambit_calibration::S630, 123, offsets) == 123, + "legacy fluorescence signal stays unchanged"); + check(ambit_calibration::apply_adpd_offset(ambit_calibration::R630, 456, offsets) == 456, + "legacy fluorescence reference stays unchanged"); + check(ambit_calibration::apply_adpd_offset(ambit_calibration::SUN, 65123, offsets) == 123, + "legacy sun correction remains 65000"); + check(ambit_calibration::apply_adpd_offset(ambit_calibration::LEAF, 64999, offsets) == 0, + "legacy leaf correction saturates at zero"); + check(ambit_calibration::apply_adpd_offset(ambit_calibration::S730, 789, offsets) == 789, + "legacy 730 signal stays unchanged"); +} + +void test_explicit_zero_offsets_are_not_legacy_defaults() { + const uint32_t offsets[ambit_calibration::CHANNEL_COUNT] = {0, 0, 0, 0, 0, 0}; + check(ambit_calibration::apply_adpd_offset( + ambit_calibration::SUN, 123, offsets, true) == 123, + "an explicitly stored zero sun baseline must be honored"); + check(ambit_calibration::apply_adpd_offset( + ambit_calibration::LEAF, 456, offsets, true) == 456, + "an explicitly stored zero leaf baseline must be honored"); +} + +void test_saved_six_channel_baseline() { + const uint32_t offsets[ambit_calibration::CHANNEL_COUNT] = {10, 20, 30, 40, 50, 60}; + check(ambit_calibration::apply_adpd_offset(ambit_calibration::S630, 110, offsets) == 100, + "saved s_630 baseline is applied"); + check(ambit_calibration::apply_adpd_offset(ambit_calibration::R630, 120, offsets) == 100, + "saved r_630 baseline is applied"); + check(ambit_calibration::apply_adpd_offset(ambit_calibration::SUN, 130, offsets) == 100, + "saved sun baseline replaces the legacy constant"); + check(ambit_calibration::apply_adpd_offset(ambit_calibration::LEAF, 140, offsets) == 100, + "saved leaf baseline replaces the legacy constant"); + check(ambit_calibration::apply_adpd_offset(ambit_calibration::S730, 150, offsets) == 100, + "saved s_730 baseline is applied"); + check(ambit_calibration::apply_adpd_offset(ambit_calibration::R730, 160, offsets) == 100, + "saved r_730 baseline is applied"); +} + +void test_saturation_and_invalid_channel() { + const uint32_t offsets[ambit_calibration::CHANNEL_COUNT] = {100, 100, 100, 100, 100, 100}; + check(ambit_calibration::apply_adpd_offset(ambit_calibration::S630, 99, offsets) == 0, + "under-baseline values saturate at zero"); + check(ambit_calibration::apply_adpd_offset(ambit_calibration::R730, 100, offsets) == 0, + "baseline equality returns zero"); + check(ambit_calibration::apply_adpd_offset(99, 123, offsets) == 123, + "unknown channels are not modified"); +} + +void test_coefficient_bounds() { + check(!ambit_calibration::valid_actinic_coefficient(0.01f), + "actinic lower bound is exclusive"); + check(ambit_calibration::valid_actinic_coefficient(0.0101f), + "actinic value above the lower bound is accepted"); + check(ambit_calibration::valid_actinic_coefficient(1.0f), + "actinic upper bound is inclusive"); + check(!ambit_calibration::valid_actinic_coefficient(1.0001f), + "actinic value above the upper bound is rejected"); + check(ambit_calibration::valid_spec_coefficient(0.05f), + "spectral lower bound is inclusive"); + check(ambit_calibration::valid_spec_coefficient(100.0f), + "spectral upper bound is inclusive"); + check(!ambit_calibration::valid_spec_coefficient(0.049f), + "spectral value below the lower bound is rejected"); + check(!ambit_calibration::valid_spec_coefficient(100.01f), + "spectral value above the upper bound is rejected"); + check(!ambit_calibration::valid_actinic_coefficient( + std::numeric_limits::quiet_NaN()) && + !ambit_calibration::valid_spec_coefficient( + std::numeric_limits::infinity()), + "non-finite coefficients are rejected"); +} + +void test_strict_baseline_parsing() { + uint32_t value = 777; + check(ambit_calibration::parse_adpd_baseline_value("0", &value) && value == 0, + "an explicit zero baseline parses successfully"); + check(ambit_calibration::parse_adpd_baseline_value(" 16777215 ", &value) && + value == ambit_calibration::MAX_ADPD_BASELINE, + "the maximum baseline and surrounding whitespace are accepted"); + + const char *invalid[] = { + "", " ", "not-a-number", "nan", "inf", "-1", "1.5", + "16777216", "12tail", + }; + for (const char *text : invalid) { + value = 777; + check(!ambit_calibration::parse_adpd_baseline_value(text, &value), + std::string("invalid baseline token is rejected: '") + text + "'"); + check(value == 777, "a rejected token does not mutate its output"); + } + check(!ambit_calibration::parse_adpd_baseline_value(nullptr, &value), + "a missing baseline token is rejected"); + check(!ambit_calibration::parse_adpd_baseline_value("1", nullptr), + "a missing baseline output is rejected"); +} + +void test_baseline_vector_validation() { + uint32_t values[ambit_calibration::CHANNEL_COUNT] = { + ambit_calibration::MAX_S630_BASELINE, 1, 2, 3, 4, + ambit_calibration::MAX_ADPD_BASELINE, + }; + check(ambit_calibration::valid_adpd_baseline(values), + "baseline vector accepts both inclusive boundaries"); + values[ambit_calibration::S630] = ambit_calibration::MAX_S630_BASELINE + 1U; + check(!ambit_calibration::valid_adpd_baseline(values), + "baseline vector rejects fluorescence baseline above 400"); + values[ambit_calibration::S630] = 0; + values[ambit_calibration::R730] = ambit_calibration::MAX_ADPD_BASELINE + 1U; + check(!ambit_calibration::valid_adpd_baseline(values), + "baseline vector rejects any channel above 24 bits"); + check(!ambit_calibration::valid_adpd_baseline(nullptr), + "missing baseline vector is rejected"); +} + +void test_spec_vector_bounds() { + float offsets[ambit_calibration::SPEC_CHANNEL_COUNT] = { + 0.0f, 0.001f, 0.002f, 0.003f, 0.004f, + 0.005f, 0.006f, 0.007f, 0.008f, 0.009f, + }; + check(ambit_calibration::valid_spec_offset(offsets), + "a plausible dark-offset vector is accepted"); + offsets[0] = -0.0001f; + check(!ambit_calibration::valid_spec_offset(offsets), + "a negative dark offset is rejected"); + offsets[0] = ambit_calibration::SPEC_OFFSET_MAX; + check(!ambit_calibration::valid_spec_offset(offsets), + "the dark-offset upper bound is exclusive"); + + float sens[ambit_calibration::SPEC_CHANNEL_COUNT] = { + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + }; + check(ambit_calibration::valid_spec_sens(sens), + "the shipped unity sensitivity vector is accepted"); + sens[9] = ambit_calibration::SPEC_SENS_MAX; + check(ambit_calibration::valid_spec_sens(sens), + "the sensitivity upper bound is inclusive"); + sens[9] = 0.0f; + check(!ambit_calibration::valid_spec_sens(sens), + "a zero sensitivity is rejected — it would erase a channel"); + + // The tier-2 fit legitimately produces negative NIR/Clear terms, so signs + // must survive validation while magnitudes and non-finites do not. + float weights[ambit_calibration::SPEC_CHANNEL_COUNT] = { + 1.0f, 0.9f, 0.8f, 0.7f, 0.6f, 0.5f, 0.4f, 0.3f, -0.2f, -0.1f, + }; + check(ambit_calibration::valid_par_weight(weights), + "a tier-2 weight vector with negative stray-light terms is accepted"); + weights[0] = std::numeric_limits::quiet_NaN(); + check(!ambit_calibration::valid_par_weight(weights), + "a NaN weight is rejected before it can reach the PAR sum"); + weights[0] = -(ambit_calibration::PAR_WEIGHT_ABS_MAX * 1.1f); + check(!ambit_calibration::valid_par_weight(weights), + "weight magnitude is bounded in both directions"); + + check(!ambit_calibration::valid_spec_offset(nullptr) && + !ambit_calibration::valid_spec_sens(nullptr) && + !ambit_calibration::valid_par_weight(nullptr), + "missing spectral vectors are rejected"); +} + +void test_par_scalar_bounds() { + check(ambit_calibration::valid_par_slope(1.0f), + "the shipped unity slope is accepted"); + check(!ambit_calibration::valid_par_slope(0.0f), + "a zero slope is rejected — it would zero every reading"); + check(!ambit_calibration::valid_par_slope(-1.0f), + "a negative slope is rejected"); + check(ambit_calibration::valid_par_slope(ambit_calibration::PAR_SLOPE_MAX), + "the slope upper bound is inclusive"); + check(!ambit_calibration::valid_par_slope( + std::numeric_limits::infinity()), + "a non-finite slope is rejected"); + + check(ambit_calibration::valid_par_intercept(0.0f), + "a zero intercept is accepted"); + check(ambit_calibration::valid_par_intercept( + -ambit_calibration::PAR_INTERCEPT_ABS_MAX), + "the intercept bound is symmetric and inclusive"); + check(!ambit_calibration::valid_par_intercept( + ambit_calibration::PAR_INTERCEPT_ABS_MAX * 1.1f), + "an out-of-range intercept is rejected"); + check(!ambit_calibration::valid_par_intercept( + std::numeric_limits::quiet_NaN()), + "a non-finite intercept is rejected"); +} + +void test_par_weight_unset_detection() { + float weights[ambit_calibration::SPEC_CHANNEL_COUNT] = {0.0f}; + check(ambit_calibration::par_weight_is_unset(weights), + "the shipped all-zero weight vector reads as uncalibrated"); + check(ambit_calibration::par_weight_is_unset(nullptr), + "a missing weight vector reads as uncalibrated"); + // Any single fitted element is enough: cmd 35 flags bit2 must clear as soon + // as the vector stops collapsing PAR to par_intercept. + weights[ambit_calibration::SPEC_CHANNEL_COUNT - 1] = -0.001f; + check(!ambit_calibration::par_weight_is_unset(weights), + "one non-zero element clears the uncalibrated flag"); +} + +void test_strict_calibration_float_parsing() { + float value = -1.0f; + check(ambit_calibration::parse_calibration_float("0", &value) && value == 0.0f, + "a deliberate zero parses — par_weight needs this distinguishable"); + check(ambit_calibration::parse_calibration_float(" -0.0075 ", &value) && + value < 0.0f, + "a negative fractional value and surrounding whitespace are accepted"); + check(ambit_calibration::parse_calibration_float("1.5", &value) && value == 1.5f, + "fractional values are accepted, unlike the baseline parser"); + + const char *invalid[] = {"", " ", "not-a-number", "nan", "inf", "1.0tail"}; + for (const char *text : invalid) { + value = 777.0f; + check(!ambit_calibration::parse_calibration_float(text, &value), + std::string("invalid calibration token is rejected: '") + text + "'"); + check(value == 777.0f, "a rejected token does not mutate its output"); + } + check(!ambit_calibration::parse_calibration_float(nullptr, &value), + "a missing calibration token is rejected"); + check(!ambit_calibration::parse_calibration_float("1", nullptr), + "a missing calibration output is rejected"); +} + +void test_wrap_safe_timeout() { + check(!ambit_calibration::wrap_safe_timeout_elapsed(100U, 149U, 50U), + "timeout remains pending one millisecond before its boundary"); + check(ambit_calibration::wrap_safe_timeout_elapsed(100U, 150U, 50U), + "timeout expires at its boundary"); + check(!ambit_calibration::wrap_safe_timeout_elapsed( + 0xFFFFFFF0U, 0x0000000FU, 32U), + "timeout remains pending across millis wrap"); + check(ambit_calibration::wrap_safe_timeout_elapsed( + 0xFFFFFFF0U, 0x00000010U, 32U), + "timeout expires correctly across millis wrap"); +} + +} // namespace + +int main() { + test_legacy_defaults(); + test_explicit_zero_offsets_are_not_legacy_defaults(); + test_saved_six_channel_baseline(); + test_saturation_and_invalid_channel(); + test_coefficient_bounds(); + test_strict_baseline_parsing(); + test_baseline_vector_validation(); + test_spec_vector_bounds(); + test_par_scalar_bounds(); + test_par_weight_unset_detection(); + test_strict_calibration_float_parsing(); + test_wrap_safe_timeout(); + if (failures != 0) return 1; + std::cout << "calibration tests passed\n"; + return 0; +} diff --git a/test/release_process/semantic_release_github_draft.mjs b/test/release_process/semantic_release_github_draft.mjs new file mode 100644 index 0000000..2894430 --- /dev/null +++ b/test/release_process/semantic_release_github_draft.mjs @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: CERN-OHL-S-2.0 +// Copyright (c) 2026 Jan IngenHousz Institute + +import assert from "node:assert/strict"; +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { pathToFileURL } from "node:url"; + +const packageJson = JSON.parse( + await (await import("node:fs/promises")).readFile( + "node_modules/@semantic-release/github/package.json", "utf8")); +assert.equal(packageJson.version, "11.0.6", "the tested plugin must be exactly 11.0.6"); + +const releaseConfig = JSON.parse(await readFile(".releaserc.json", "utf8")); +const githubPlugin = releaseConfig.plugins.find( + entry => Array.isArray(entry) && entry[0] === "@semantic-release/github"); +assert.ok(githubPlugin, "the production GitHub plugin config must exist"); +const githubConfig = githubPlugin[1]; +assert.equal(githubConfig.draftRelease, true, + "the production config must keep releases in draft"); + +const packageRoot = path.resolve("node_modules/@semantic-release/github"); +const publishGitHub = (await import(pathToFileURL( + path.join(packageRoot, "lib/publish.js")))).default; +const successGitHub = (await import(pathToFileURL( + path.join(packageRoot, "lib/success.js")))).default; + +const calls = []; +class MockOctokit { + constructor() {} + + async request(route, options) { + calls.push({ route, options }); + if (typeof route === "object") { + return { data: { browser_download_url: "https://example.invalid/asset.bin" } }; + } + if (route === "POST /repos/{owner}/{repo}/releases") { + return { data: { + id: 114, + upload_url: "https://uploads.github.invalid/{?name,label}", + html_url: "https://github.invalid/release/114", + } }; + } + if (route === "GET /repos/{owner}/{repo}") { + return { data: { full_name: "Jan-IngenHousz-Institute/ambit" } }; + } + throw new Error(`unexpected Octokit call: ${route}`); + } +} + +const temporary = await mkdtemp(path.join(os.tmpdir(), "ambit-draft-proof-")); +try { + await writeFile(path.join(temporary, "asset.bin"), Buffer.from("qualified bytes")); + const logger = { log() {}, warn() {}, error() {} }; + const context = { + cwd: temporary, + env: { GITHUB_TOKEN: "mock-token" }, + options: { repositoryUrl: "https://github.com/Jan-IngenHousz-Institute/ambit.git" }, + branch: { name: "main" }, + nextRelease: { + gitTag: "v1.1.4", + name: "v1.1.4", + notes: "qualified release", + version: "1.1.4", + }, + logger, + }; + + await publishGitHub( + { ...githubConfig, assets: [{ path: "asset.bin", label: "qualified" }] }, + context, + { Octokit: MockOctokit }); + + const create = calls.find(call => call.route === "POST /repos/{owner}/{repo}/releases"); + assert.ok(create, "publish must create one GitHub release"); + assert.equal(create.options.draft, true, "publish must create a draft"); + assert.equal(create.options.target_commitish, "main"); + assert.equal(calls.filter(call => typeof call.route === "object").length, 1, + "publish must upload the configured asset"); + assert.equal(calls.some(call => String(call.route).startsWith("PATCH ")), false, + "draft publish must return without publishing the release"); + + calls.length = 0; + await successGitHub( + githubConfig, + { ...context, commits: [], releases: [] }, + { Octokit: MockOctokit }); + assert.deepEqual(calls.map(call => call.route), ["GET /repos/{owner}/{repo}"], + "success phase must not PATCH or publish the draft"); +} finally { + await rm(temporary, { recursive: true, force: true }); +} + +console.log("@semantic-release/github 11.0.6 draft-only behavior proved"); diff --git a/test/release_process/test_release_guard.py b/test/release_process/test_release_guard.py new file mode 100644 index 0000000..52b32b7 --- /dev/null +++ b/test/release_process/test_release_guard.py @@ -0,0 +1,290 @@ +# SPDX-License-Identifier: CERN-OHL-S-2.0 +# Copyright (c) 2026 Jan IngenHousz Institute + +import copy +import importlib.util +import json +import shutil +import tempfile +import types +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +SPEC = importlib.util.spec_from_file_location( + "release_guard", ROOT / "tools" / "release_guard.py") +release_guard = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(release_guard) + + +class ReleaseGuardTests(unittest.TestCase): + def setUp(self): + self.temporary = Path(tempfile.mkdtemp(prefix="ambit-release-guard-")) + self.assets = self.temporary / "assets" + self.assets.mkdir() + self.version = "1.1.4" + self.tag = "v1.1.4" + self.repository = "Jan-IngenHousz-Institute/ambit" + self.target_sha = "a" * 40 + self.app = "ambit-fw-v1.1.4.bin" + payloads = { + "bootloader.bin": b"bootloader", + "partitions.bin": b"partitions", + "boot_app0.bin": b"boot-app-zero", + self.app: b"qualified-application", + } + for name, payload in payloads.items(): + (self.assets / name).write_bytes(payload) + flash = [] + offsets = { + "bootloader.bin": "0x0", + "partitions.bin": "0x8000", + "boot_app0.bin": "0xe000", + self.app: "0x10000", + } + for name in ("bootloader.bin", "partitions.bin", "boot_app0.bin", self.app): + path = self.assets / name + flash.append({ + "file": name, + "offset": offsets[name], + "size": path.stat().st_size, + "sha256": release_guard.sha256(path), + }) + manifest = { + "name": "ambit-iot", + "version": self.version, + "chip": "esp32c3", + "flash": flash, + "ota": {"file": self.app}, + } + (self.assets / "manifest.json").write_text( + json.dumps(manifest), encoding="utf-8") + self.candidate = release_guard.verify_candidate(self.assets, self.version) + + def tearDown(self): + shutil.rmtree(self.temporary) + + def write_json(self, name, value): + path = self.temporary / name + path.write_text(json.dumps(value), encoding="utf-8") + return path + + def release(self, phase="draft"): + labels = dict(release_guard.EXPECTED_LABELS) + labels[self.app] = "Application image (OTA / 0x10000)" + assets = [] + published = phase == "published" + release_slug = self.tag if published else "untagged-114-draft" + for index, name in enumerate(release_guard.expected_assets(self.version), 10): + expected = self.candidate[name] + assets.append({ + "id": index, + "name": name, + "label": labels[name], + "state": "uploaded", + "size": expected["size"], + "digest": "sha256:{}".format(expected["sha256"]), + "created_at": "2026-08-07T10:00:00Z", + "updated_at": "2026-08-07T10:00:01Z", + "browser_download_url": "https://github.com/{}/releases/download/{}/{}".format( + self.repository, release_slug, name), + }) + html_url = ("https://github.com/{}/releases/tag/{}".format( + self.repository, self.tag) if published else + "https://github.com/{}/releases/tag/untagged-114-draft".format( + self.repository)) + return { + "id": 114, + "node_id": "release-node-114", + "tag_name": self.tag, + "target_commitish": "main", + "name": self.tag, + "draft": not published, + "prerelease": False, + "immutable": published, + "created_at": "2026-08-07T09:59:59Z", + "published_at": "2026-08-07T10:01:00Z" if published else None, + "html_url": html_url, + "url": "https://api.github.com/repos/{}/releases/114".format(self.repository), + "assets": assets, + } + + def ref(self): + return {"ref": "refs/tags/{}".format(self.tag), + "object": {"type": "commit", "sha": self.target_sha}} + + def test_candidate_requires_exact_files_manifest_and_qualified_app_hash(self): + app_hash = self.candidate[self.app]["sha256"] + verified = release_guard.verify_candidate( + self.assets, self.version, app_hash) + self.assertEqual(verified[self.app]["sha256"], app_hash) + (self.assets / "release-preview.md").write_text("not a release asset", encoding="utf-8") + with self.assertRaises(release_guard.GuardError): + release_guard.verify_candidate(self.assets, self.version, app_hash) + + def test_manifest_hash_drift_fails(self): + manifest_path = self.assets / "manifest.json" + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + manifest["flash"][3]["sha256"] = "0" * 64 + manifest_path.write_text(json.dumps(manifest), encoding="utf-8") + with self.assertRaises(release_guard.GuardError): + release_guard.verify_candidate(self.assets, self.version) + + def test_draft_proof_survives_publish_only_when_assets_are_unchanged(self): + downloads = self.temporary / "downloads" + shutil.copytree(self.assets, downloads) + releases_path = self.write_json("draft-releases.json", [[self.release()]]) + ref_path = self.write_json("ref.json", self.ref()) + proof_path = self.temporary / "proof.json" + app_hash = self.candidate[self.app]["sha256"] + release_guard.make_proof(types.SimpleNamespace( + assets_dir=str(self.assets), downloads_dir=str(downloads), + releases_json=str(releases_path), ref_json=str(ref_path), + repository=self.repository, version=self.version, tag=self.tag, + target_sha=self.target_sha, branch="main", output=str(proof_path), + pr_number=4, pr_head_sha="b" * 40, run_id=400, artifact_id=401, + app_sha256=app_hash)) + + published = self.release("published") + published_path = self.write_json("published-releases.json", [[published]]) + latest_path = self.write_json("latest.json", published) + release_guard.verify_proof(types.SimpleNamespace( + proof=str(proof_path), releases_json=str(published_path), + ref_json=str(ref_path), phase="published", + latest_json=str(latest_path), downloads_dir=str(downloads))) + + drifted = copy.deepcopy(published) + drifted["assets"][0]["updated_at"] = "2026-08-07T10:02:00Z" + drifted_path = self.write_json("drifted.json", [[drifted]]) + with self.assertRaises(release_guard.GuardError): + release_guard.verify_proof(types.SimpleNamespace( + proof=str(proof_path), releases_json=str(drifted_path), + ref_json=str(ref_path), phase="published", + latest_json=str(latest_path), downloads_dir=str(downloads))) + + def test_existing_tag_or_release_fails_preflight(self): + releases = self.write_json("existing-release.json", [[self.release()]]) + refs = self.write_json("existing-ref.json", [self.ref()]) + with self.assertRaises(release_guard.GuardError): + release_guard.preflight(types.SimpleNamespace( + releases_json=str(releases), refs_json=str(refs), tag=self.tag)) + + def test_draft_accepts_repo_scoped_untagged_url_and_rejects_other_repo(self): + draft = self.release("draft") + release_guard.verify_release_metadata( + draft, self.repository, self.tag, "main", "draft") + draft["html_url"] = "https://github.com/other/project/releases/untagged/114-draft" + with self.assertRaises(release_guard.GuardError): + release_guard.verify_release_metadata( + draft, self.repository, self.tag, "main", "draft") + + def test_published_release_requires_canonical_tag_url(self): + published = self.release("published") + release_guard.verify_release_metadata( + published, self.repository, self.tag, "main", "published") + published["html_url"] = "https://github.com/{}/releases/untagged/114-draft".format( + self.repository) + with self.assertRaises(release_guard.GuardError): + release_guard.verify_release_metadata( + published, self.repository, self.tag, "main", "published") + + def test_draft_asset_urls_must_match_temporary_release_namespace(self): + draft = self.release("draft") + release_guard.release_assets( + draft, self.repository, self.tag, self.candidate, "draft") + draft["assets"][0]["browser_download_url"] = ( + "https://github.com/{}/releases/download/untagged-other/{}".format( + self.repository, draft["assets"][0]["name"])) + with self.assertRaises(release_guard.GuardError): + release_guard.release_assets( + draft, self.repository, self.tag, self.candidate, "draft") + + def test_anonymous_asset_lines_use_canonical_published_urls(self): + proof = { + "repository": self.repository, + "tag": self.tag, + "assets": {name: {"id": index, "browser_download_url": "draft-url"} + for index, name in enumerate( + release_guard.expected_assets(self.version), 10)}, + } + proof_path = self.write_json("asset-lines-proof.json", proof) + output = [] + original_stdout = release_guard.sys.stdout + release_guard.sys.stdout = types.SimpleNamespace(write=output.append) + try: + release_guard.asset_lines(types.SimpleNamespace( + proof=str(proof_path), mode="browser")) + finally: + release_guard.sys.stdout = original_stdout + text = "".join(output) + self.assertNotIn("draft-url", text) + self.assertIn("/releases/download/{}/{}".format(self.tag, self.app), text) + + def test_artifact_selection_is_by_exact_id_response_not_timestamp(self): + artifact = { + "id": 401, + "name": "firmware-{}".format("b" * 40), + "expired": False, + "workflow_run": {"id": 400}, + } + path = self.write_json("artifact.json", artifact) + # A single exact-ID API response is accepted. A list with a duplicate + # candidate is rejected instead of selecting the latest timestamp. + args = types.SimpleNamespace(artifacts_json=str(path), name=artifact["name"]) + release_guard.select_artifact(args) + duplicate = self.write_json("artifacts.json", {"artifacts": [artifact, artifact]}) + with self.assertRaises(release_guard.GuardError): + release_guard.select_artifact(types.SimpleNamespace( + artifacts_json=str(duplicate), name=artifact["name"])) + + +class WorkflowBoundaryTests(unittest.TestCase): + def test_release_workflow_has_one_draft_producer_and_one_protected_finalizer(self): + workflow = (ROOT / ".github/workflows/release.yml").read_text(encoding="utf-8") + stage, finalizer = workflow.split(" publish-finalizer:", 1) + self.assertIn("draftRelease", (ROOT / ".releaserc.json").read_text(encoding="utf-8")) + self.assertEqual(stage.count("run: npx semantic-release\n"), 1) + self.assertNotIn("draft=false", stage) + self.assertIn("environment: firmware-release", finalizer) + self.assertEqual(finalizer.count("-F draft=false"), 1) + self.assertNotIn("npx semantic-release", finalizer) + self.assertNotIn("actions/upload-artifact", finalizer) + self.assertIn("QUALIFIED_PR_RUN_ID", workflow) + self.assertIn("QUALIFIED_ARTIFACT_ID", workflow) + self.assertIn("QUALIFIED_APP_SHA256", workflow) + self.assertIn("QUALIFIED_PR_HEAD_SHA: ${{ vars.AMBIT_V114_PR_HEAD_SHA }}", workflow) + self.assertNotIn("actions/artifacts?name=", workflow) + self.assertIn("-F draft=false -f make_latest=true", finalizer) + preview = stage.index("npx semantic-release --dry-run") + preflight = stage.index("release_guard.py preflight") + publish = stage.index("run: npx semantic-release\n") + self.assertLess(preview, preflight) + self.assertLess(preflight, publish) + preflight_step = stage[stage.rfind(" - name:", 0, preflight):preflight] + self.assertIn("if: steps.preview.outputs.will-release == 'true'", preflight_step) + self.assertIn("attestations: read", finalizer) + self.assertIn("gh --version", finalizer) + + def test_plugin_and_host_compile_flags_are_pinned(self): + package = json.loads((ROOT / "package.json").read_text(encoding="utf-8")) + self.assertEqual(package["devDependencies"]["@semantic-release/github"], "11.0.6") + config = json.loads((ROOT / ".releaserc.json").read_text(encoding="utf-8")) + github_plugin = next(item for item in config["plugins"] + if isinstance(item, list) and item[0] == "@semantic-release/github") + self.assertIs(github_plugin[1]["draftRelease"], True) + self.assertIs(github_plugin[1]["addReleases"], False) + pr_workflow = (ROOT / ".github/workflows/pr.yml").read_text(encoding="utf-8") + self.assertGreaterEqual(pr_workflow.count("-std=c++11 -Wall -Wextra -Werror"), 2) + self.assertIn("semantic_release_github_draft.mjs", pr_workflow) + self.assertIn("install_release_tooling.sh", pr_workflow) + proof = (ROOT / "test/release_process/semantic_release_github_draft.mjs").read_text( + encoding="utf-8") + self.assertIn('readFile(".releaserc.json", "utf8")', proof) + installer = (ROOT / "tools/install_release_tooling.sh").read_text(encoding="utf-8") + self.assertIn("4bd998c6530867151587e99ca8abb7ede0d1e51904195f8945caf2d1eed22554", + installer) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/install_release_tooling.sh b/tools/install_release_tooling.sh new file mode 100755 index 0000000..92713e1 --- /dev/null +++ b/tools/install_release_tooling.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: CERN-OHL-S-2.0 +# Copyright (c) 2026 Jan IngenHousz Institute + +set -euo pipefail + +# npm validates registry integrity itself. This additional archive pin makes the +# exact plugin source qualified by semantic_release_github_draft.mjs explicit +# and reviewable even though this firmware repository does not commit a lockfile. +PLUGIN_ARCHIVE_SHA256="4bd998c6530867151587e99ca8abb7ede0d1e51904195f8945caf2d1eed22554" +RELEASE_TOOLING_TEMP=$(mktemp -d) +trap 'rm -rf "${RELEASE_TOOLING_TEMP}"' EXIT + +pushd "${RELEASE_TOOLING_TEMP}" >/dev/null +PLUGIN_ARCHIVE=$(npm pack @semantic-release/github@11.0.6 --silent) +test "${PLUGIN_ARCHIVE}" = "semantic-release-github-11.0.6.tgz" +echo "${PLUGIN_ARCHIVE_SHA256} ${PLUGIN_ARCHIVE}" | sha256sum --check --status +popd >/dev/null + +npm install --ignore-scripts --package-lock=false +node -e "const p=require('./node_modules/@semantic-release/github/package.json'); if(p.version!=='11.0.6') process.exit(1)" diff --git a/tools/release_guard.py b/tools/release_guard.py new file mode 100644 index 0000000..35f3133 --- /dev/null +++ b/tools/release_guard.py @@ -0,0 +1,499 @@ +#!/usr/bin/env python3 +"""Fail-closed validation for the AMBIT immutable release boundary. + +The workflow deliberately keeps GitHub API access in shell steps and all +policy/byte validation here. This makes the important checks deterministic, +unit-testable, and reusable on both sides of the protected environment gate. +""" + +import argparse +import hashlib +import json +import re +import sys +from pathlib import Path +from urllib.parse import urlparse + + +EXPECTED_OFFSETS = { + "bootloader.bin": "0x0", + "partitions.bin": "0x8000", + "boot_app0.bin": "0xe000", +} +EXPECTED_LABELS = { + "manifest.json": "Flash manifest (offsets + sha256)", + "bootloader.bin": "Bootloader (0x0)", + "partitions.bin": "Partition table (0x8000)", + "boot_app0.bin": "OTA data init (0xe000)", +} +SHA256_RE = re.compile(r"^[0-9a-f]{64}$") + + +class GuardError(RuntimeError): + pass + + +def require(condition, message): + if not condition: + raise GuardError(message) + + +def load(path): + with Path(path).open("r", encoding="utf-8") as handle: + return json.load(handle) + + +def dump(value, path=None): + text = json.dumps(value, indent=2, sort_keys=True) + "\n" + if path: + Path(path).write_text(text, encoding="utf-8") + else: + sys.stdout.write(text) + + +def flatten_pages(value): + if isinstance(value, dict): + return value["artifacts"] if "artifacts" in value else [value] + require(isinstance(value, list), "expected a JSON list") + if value and all(isinstance(page, list) for page in value): + return [item for page in value for item in page] + return value + + +def sha256(path): + digest = hashlib.sha256() + with Path(path).open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def expected_assets(version): + app = "ambit-fw-v{}.bin".format(version) + return ["manifest.json", app, "bootloader.bin", "partitions.bin", "boot_app0.bin"] + + +def verify_candidate(directory, version, expected_app_sha256=None): + root = Path(directory) + app = "ambit-fw-v{}.bin".format(version) + names = expected_assets(version) + require(root.is_dir(), "candidate asset directory is missing") + actual = sorted(path.name for path in root.iterdir() if path.is_file()) + require(actual == sorted(names), + "candidate must contain exactly five assets: expected {}; got {}".format( + sorted(names), actual)) + + manifest = load(root / "manifest.json") + require(set(manifest) == {"name", "version", "chip", "flash", "ota"}, + "manifest top-level keys do not match the release contract") + require(manifest["name"] == "ambit-iot", "manifest name must be ambit-iot") + require(manifest["version"] == version, "manifest version mismatch") + require(manifest["chip"] == "esp32c3", "manifest chip mismatch") + require(manifest["ota"] == {"file": app}, "manifest OTA target mismatch") + + expected_flash = ["bootloader.bin", "partitions.bin", "boot_app0.bin", app] + require(isinstance(manifest["flash"], list), "manifest flash field must be a list") + require([entry.get("file") for entry in manifest["flash"]] == expected_flash, + "manifest flash order/file allowlist mismatch") + + result = {} + for name in names: + path = root / name + result[name] = {"size": path.stat().st_size, "sha256": sha256(path)} + if expected_app_sha256 is not None: + require(SHA256_RE.match(expected_app_sha256) is not None, + "qualified application sha256 is malformed") + require(result[app]["sha256"] == expected_app_sha256, + "application image does not match the qualified sha256") + + for entry in manifest["flash"]: + require(set(entry) == {"file", "offset", "size", "sha256"}, + "manifest flash entry keys do not match the release contract") + name = entry["file"] + expected_offset = "0x10000" if name == app else EXPECTED_OFFSETS[name] + require(entry["offset"] == expected_offset, + "manifest offset mismatch for {}".format(name)) + require(entry["size"] == result[name]["size"], + "manifest size mismatch for {}".format(name)) + require(isinstance(entry["sha256"], str) and SHA256_RE.match(entry["sha256"]), + "manifest sha256 is malformed for {}".format(name)) + require(entry["sha256"] == result[name]["sha256"], + "manifest sha256 mismatch for {}".format(name)) + return result + + +def exact_release(releases, tag): + matches = [release for release in flatten_pages(releases) + if release.get("tag_name") == tag] + require(len(matches) == 1, + "expected exactly one release for {}; found {}".format(tag, len(matches))) + return matches[0] + + +def exact_ref(ref_json, tag, target_sha): + require(ref_json.get("ref") == "refs/tags/{}".format(tag), "tag ref name mismatch") + target = ref_json.get("object", {}) + require(target.get("type") == "commit", "release tag must be a lightweight commit tag") + require(target.get("sha") == target_sha, "release tag points at the wrong commit") + + +def verify_release_metadata(release, repository, tag, branch, phase): + require(isinstance(release.get("id"), int) and release["id"] > 0, + "release id is missing") + require(release.get("tag_name") == tag, "release tag metadata mismatch") + require(release.get("target_commitish") == branch, + "release target_commitish must be {}".format(branch)) + require(release.get("name") == tag, "release name must equal the tag") + require(release.get("prerelease") is False, "release must be stable, not a prerelease") + expected_api_url = "https://api.github.com/repos/{}/releases/{}".format( + repository, release["id"]) + require(release.get("url") == expected_api_url, "release API identity mismatch") + html_url = release.get("html_url", "") + if phase == "draft": + require(release.get("draft") is True, "staged release must remain a draft") + require(release.get("immutable") is False, "draft release must not claim immutability") + require(release.get("published_at") is None, "draft must not have a publication timestamp") + parsed = urlparse(html_url) + expected_prefix = "/{}/releases/".format(repository) + require(parsed.scheme == "https" and parsed.netloc == "github.com" + and parsed.path.startswith(expected_prefix) + and len(parsed.path) > len(expected_prefix) + and not parsed.params and not parsed.query and not parsed.fragment, + "draft HTML URL is not a repository-scoped HTTPS release URL") + elif phase == "published": + require(release.get("draft") is False, "final release must not be a draft") + require(release.get("immutable") is True, "published release is not immutable") + require(isinstance(release.get("published_at"), str) and release["published_at"], + "published release is missing published_at") + require(html_url == "https://github.com/{}/releases/tag/{}".format( + repository, tag), "published release HTML URL is not canonical") + else: + raise GuardError("unknown release phase") + + +def expected_asset_browser_url(release, repository, tag, name, phase): + if phase == "published": + return "https://github.com/{}/releases/download/{}/{}".format( + repository, tag, name) + if phase != "draft": + raise GuardError("unknown release phase") + + html = urlparse(release.get("html_url", "")) + expected_prefix = "/{}/releases/tag/".format(repository) + require(html.scheme == "https" and html.netloc == "github.com" + and html.path.startswith(expected_prefix), + "draft release URL cannot identify its temporary asset namespace") + draft_slug = html.path[len(expected_prefix):] + require(draft_slug.startswith("untagged-") and "/" not in draft_slug, + "draft release URL has an invalid temporary asset namespace") + return "https://github.com/{}/releases/download/{}/{}".format( + repository, draft_slug, name) + + +def release_assets(release, repository, tag, candidate, phase): + assets = release.get("assets") + require(isinstance(assets, list), "release assets are missing") + expected_names = set(candidate) + actual_names = [asset.get("name") for asset in assets] + require(len(actual_names) == len(set(actual_names)), "release contains duplicate asset names") + require(set(actual_names) == expected_names and len(actual_names) == 5, + "release must contain the exact five-asset allowlist") + + by_name = {} + ids = set() + app = next(name for name in expected_names if name.startswith("ambit-fw-v")) + labels = dict(EXPECTED_LABELS) + labels[app] = "Application image (OTA / 0x10000)" + for asset in assets: + name = asset["name"] + require(asset.get("state") == "uploaded", "asset {} is not uploaded".format(name)) + require(isinstance(asset.get("id"), int) and asset["id"] > 0, + "asset {} has no REST id".format(name)) + require(asset["id"] not in ids, "release contains duplicate asset ids") + ids.add(asset["id"]) + require(asset.get("size") == candidate[name]["size"], + "REST size mismatch for {}".format(name)) + require(asset.get("digest") == "sha256:{}".format(candidate[name]["sha256"]), + "REST digest mismatch for {}".format(name)) + require(asset.get("label") == labels[name], "asset label mismatch for {}".format(name)) + require(isinstance(asset.get("created_at"), str) and asset["created_at"], + "asset {} is missing created_at".format(name)) + require(isinstance(asset.get("updated_at"), str) and asset["updated_at"], + "asset {} is missing updated_at".format(name)) + expected_url = expected_asset_browser_url( + release, repository, tag, name, phase) + require(asset.get("browser_download_url") == expected_url, + "asset browser URL mismatch for {}".format(name)) + by_name[name] = { + "id": asset["id"], + "name": name, + "size": asset["size"], + "digest": asset["digest"], + "sha256": candidate[name]["sha256"], + "created_at": asset["created_at"], + "updated_at": asset["updated_at"], + "browser_download_url": asset["browser_download_url"], + } + return by_name + + +def verify_downloads(directory, assets): + root = Path(directory) + actual = sorted(path.name for path in root.iterdir() if path.is_file()) if root.is_dir() else [] + require(actual == sorted(assets), "download directory does not contain the exact asset set") + for name, expected in assets.items(): + path = root / name + require(path.stat().st_size == expected["size"], + "downloaded size mismatch for {}".format(name)) + require(sha256(path) == expected["sha256"], + "downloaded sha256 mismatch for {}".format(name)) + + +def make_proof(args): + candidate = verify_candidate(args.assets_dir, args.version, args.app_sha256) + releases = load(args.releases_json) + release = exact_release(releases, args.tag) + ref_json = load(args.ref_json) + exact_ref(ref_json, args.tag, args.target_sha) + verify_release_metadata(release, args.repository, args.tag, args.branch, "draft") + assets = release_assets(release, args.repository, args.tag, candidate, "draft") + verify_downloads(args.downloads_dir, assets) + proof = { + "schema": 1, + "repository": args.repository, + "version": args.version, + "tag": args.tag, + "target_sha": args.target_sha, + "target_commitish": args.branch, + "qualified_pr": { + "number": args.pr_number, + "head_sha": args.pr_head_sha, + "run_id": args.run_id, + "artifact_id": args.artifact_id, + "app_sha256": args.app_sha256, + }, + "release": { + key: release.get(key) for key in + ("id", "node_id", "tag_name", "target_commitish", "name", + "prerelease", "created_at", "html_url", "url") + }, + "assets": assets, + } + dump(proof, args.output) + + +def verify_proof(args): + proof = load(args.proof) + require(proof.get("schema") == 1, "unsupported proof schema") + require(proof.get("tag") == "v{}".format(proof.get("version")), + "proof version/tag mismatch") + qualified = proof.get("qualified_pr", {}) + require(isinstance(qualified.get("number"), int) and qualified["number"] > 0, + "proof qualified PR number is missing") + require(isinstance(qualified.get("run_id"), int) and qualified["run_id"] > 0, + "proof qualified run id is missing") + require(isinstance(qualified.get("artifact_id"), int) and qualified["artifact_id"] > 0, + "proof qualified artifact id is missing") + require(re.match(r"^[0-9a-f]{40}$", qualified.get("head_sha", "")) is not None, + "proof qualified PR head is malformed") + app = "ambit-fw-v{}.bin".format(proof["version"]) + require(qualified.get("app_sha256") == proof.get("assets", {}).get(app, {}).get("sha256"), + "proof application hash is not tied to the qualified hash") + releases = load(args.releases_json) + release = exact_release(releases, proof["tag"]) + exact_ref(load(args.ref_json), proof["tag"], proof["target_sha"]) + verify_release_metadata(release, proof["repository"], proof["tag"], + proof["target_commitish"], args.phase) + for key, value in proof["release"].items(): + # GitHub may expose a draft under an untagged URL and replace it with + # the canonical tag URL when the same release is published. + if args.phase == "published" and key == "html_url": + continue + require(release.get(key) == value, "release identity changed at {}".format(key)) + + candidate = {name: {"size": asset["size"], "sha256": asset["sha256"]} + for name, asset in proof["assets"].items()} + current_assets = release_assets( + release, proof["repository"], proof["tag"], candidate, args.phase) + if args.phase == "draft": + require(current_assets == proof["assets"], "release asset REST proof changed") + else: + # GitHub moves the same immutable asset records from a temporary + # untagged draft namespace to the canonical tag URL when publishing. + # Every identity, timestamp, size and digest must remain byte-for-byte + # stable; only browser_download_url may make that documented move. + for name, current in current_assets.items(): + recorded = proof["assets"].get(name, {}) + for key, value in current.items(): + if key == "browser_download_url": + continue + require(recorded.get(key) == value, + "release asset REST proof changed at {}:{}".format(name, key)) + if args.downloads_dir: + verify_downloads(args.downloads_dir, proof["assets"]) + if args.phase == "published": + require(args.latest_json, "published verification requires latest release metadata") + latest = load(args.latest_json) + require(latest.get("id") == release["id"] and latest.get("tag_name") == proof["tag"], + "published release is not the stable latest release") + + +def preflight(args): + releases = flatten_pages(load(args.releases_json)) + refs = flatten_pages(load(args.refs_json)) + release_count = sum(1 for release in releases if release.get("tag_name") == args.tag) + ref_count = sum(1 for ref in refs if ref.get("ref") == "refs/tags/{}".format(args.tag)) + require(release_count == 0, "release {} already exists".format(args.tag)) + require(ref_count == 0, "tag {} already exists".format(args.tag)) + + +def verify_qualified_pr(args): + pr = load(args.pr_json) + require(pr.get("number") == args.number, "qualified PR number mismatch") + require(pr.get("state") == "closed" and pr.get("merged_at"), + "qualified PR is not merged") + require(pr.get("base", {}).get("ref") == args.branch, + "qualified PR base branch mismatch") + require(pr.get("head", {}).get("sha") == args.head_sha, + "qualified PR head changed after qualification") + + +def select_artifact(args): + artifacts = flatten_pages(load(args.artifacts_json)) + matches = [artifact for artifact in artifacts + if artifact.get("name") == args.name and artifact.get("expired") is False] + require(len(matches) == 1, + "expected one live PR artifact named {}; found {}".format(args.name, len(matches))) + artifact = matches[0] + run_id = artifact.get("workflow_run", {}).get("id") + require(isinstance(run_id, int) and run_id > 0, "artifact workflow run id is missing") + dump({"artifact_id": artifact["id"], "run_id": run_id}) + + +def verify_run(args): + run = load(args.run_json) + require(run.get("id") == args.run_id, "workflow run id mismatch") + require(run.get("event") == "pull_request", "artifact was not produced by pull_request CI") + require(run.get("status") == "completed" and run.get("conclusion") == "success", + "artifact workflow run was not successful") + require(run.get("head_sha") == args.head_sha, "artifact workflow ran at the wrong PR head") + require(run.get("path") == ".github/workflows/pr.yml", "artifact came from the wrong workflow") + + +def asset_lines(args): + proof = load(args.proof) + for name in sorted(proof["assets"]): + asset = proof["assets"][name] + if args.mode == "api": + locator = str(asset["id"]) + else: + locator = "https://github.com/{}/releases/download/{}/{}".format( + proof["repository"], proof["tag"], name) + sys.stdout.write("{}\t{}\n".format(locator, name)) + + +def release_asset_lines(args): + release = exact_release(load(args.releases_json), args.tag) + assets = release.get("assets", []) + expected = set(expected_assets(args.version)) + require(len(assets) == 5 and {asset.get("name") for asset in assets} == expected, + "draft does not have the exact five-asset allowlist") + for asset in sorted(assets, key=lambda item: item["name"]): + require(isinstance(asset.get("id"), int) and asset["id"] > 0, + "release asset id is missing") + sys.stdout.write("{}\t{}\n".format(asset["id"], asset["name"])) + + +def parser(): + root = argparse.ArgumentParser() + commands = root.add_subparsers(dest="command", required=True) + + candidate = commands.add_parser("candidate") + candidate.add_argument("--assets-dir", required=True) + candidate.add_argument("--version", required=True) + candidate.add_argument("--expected-app-sha256") + + before = commands.add_parser("preflight") + before.add_argument("--releases-json", required=True) + before.add_argument("--refs-json", required=True) + before.add_argument("--tag", required=True) + + qualified_pr = commands.add_parser("verify-qualified-pr") + qualified_pr.add_argument("--pr-json", required=True) + qualified_pr.add_argument("--number", required=True, type=int) + qualified_pr.add_argument("--head-sha", required=True) + qualified_pr.add_argument("--branch", required=True) + + artifact = commands.add_parser("select-artifact") + artifact.add_argument("--artifacts-json", required=True) + artifact.add_argument("--name", required=True) + + run = commands.add_parser("verify-run") + run.add_argument("--run-json", required=True) + run.add_argument("--run-id", required=True, type=int) + run.add_argument("--head-sha", required=True) + + proof = commands.add_parser("make-proof") + proof.add_argument("--assets-dir", required=True) + proof.add_argument("--downloads-dir", required=True) + proof.add_argument("--releases-json", required=True) + proof.add_argument("--ref-json", required=True) + proof.add_argument("--repository", required=True) + proof.add_argument("--version", required=True) + proof.add_argument("--tag", required=True) + proof.add_argument("--target-sha", required=True) + proof.add_argument("--branch", required=True) + proof.add_argument("--pr-number", required=True, type=int) + proof.add_argument("--pr-head-sha", required=True) + proof.add_argument("--run-id", required=True, type=int) + proof.add_argument("--artifact-id", required=True, type=int) + proof.add_argument("--app-sha256", required=True) + proof.add_argument("--output", required=True) + + verify = commands.add_parser("verify-proof") + verify.add_argument("--proof", required=True) + verify.add_argument("--releases-json", required=True) + verify.add_argument("--ref-json", required=True) + verify.add_argument("--phase", choices=("draft", "published"), required=True) + verify.add_argument("--latest-json") + verify.add_argument("--downloads-dir") + + lines = commands.add_parser("asset-lines") + lines.add_argument("--proof", required=True) + lines.add_argument("--mode", choices=("api", "browser"), required=True) + + release_lines = commands.add_parser("release-asset-lines") + release_lines.add_argument("--releases-json", required=True) + release_lines.add_argument("--tag", required=True) + release_lines.add_argument("--version", required=True) + return root + + +def main(): + args = parser().parse_args() + if args.command == "candidate": + dump(verify_candidate(args.assets_dir, args.version, args.expected_app_sha256)) + elif args.command == "preflight": + preflight(args) + elif args.command == "verify-qualified-pr": + verify_qualified_pr(args) + elif args.command == "select-artifact": + select_artifact(args) + elif args.command == "verify-run": + verify_run(args) + elif args.command == "make-proof": + make_proof(args) + elif args.command == "verify-proof": + verify_proof(args) + elif args.command == "asset-lines": + asset_lines(args) + elif args.command == "release-asset-lines": + release_asset_lines(args) + + +if __name__ == "__main__": + try: + main() + except (GuardError, KeyError, OSError, ValueError, TypeError) as error: + sys.stderr.write("release guard: {}\n".format(error)) + sys.exit(1) diff --git a/tools/version.py b/tools/version.py new file mode 100644 index 0000000..4f0b50d --- /dev/null +++ b/tools/version.py @@ -0,0 +1,50 @@ +# version.py - PlatformIO pre-script: inject the firmware version at build time. +# +# The version has exactly one source, resolved in priority order: +# 1. $AMBIT_RELEASE_VERSION - set by CI from the semantic-release tag +# 2. git describe - dev builds ("v0.1.0-3-gabc123-dirty") +# 3. "0.0.0-dev" - no git / no tags +# +# It feeds every reporting surface: the cmd 33/2 binary struct (Major/Minor/Batch), +# the text `hello` reply, and the openJII envelope identity. Before this script the +# tree carried four hand-maintained copies (nvs1.h, two literals in the JSON +# frontend, plus the Calibratron's out-of-repo pin) that had already diverged +# (0.0.6 vs 0.0.3 vs 0.0.5). +Import("env") + +import os +import re +import subprocess + + +def _git_describe(): + try: + return ( + subprocess.check_output( + ["git", "describe", "--tags", "--always", "--dirty"], + cwd=env["PROJECT_DIR"], + stderr=subprocess.DEVNULL, + ) + .decode() + .strip() + ) + except Exception: + return "" + + +version = os.environ.get("AMBIT_RELEASE_VERSION", "").strip() or _git_describe() or "0.0.0-dev" +version = version.lstrip("vV") + +# Major/Minor/Batch feed the frozen 48-byte cmd 33/2 wire struct (uint8 each). +m = re.match(r"^(\d+)\.(\d+)\.(\d+)", version) +major, minor, batch = (int(g) for g in m.groups()) if m else (0, 0, 0) + +env.Append( + CPPDEFINES=[ + ("AMBIT_FW_VERSION", env.StringifyMacro(version)), + ("AMBIT_FW_MAJOR", major), + ("AMBIT_FW_MINOR", minor), + ("AMBIT_FW_BATCH", batch), + ] +) +print("ambit fw version: %s (%d.%d.%d)" % (version, major, minor, batch))