From 104cc997ab8038c295465f3fad466f74723b2c89 Mon Sep 17 00:00:00 2001 From: Dan Nechita Date: Thu, 18 Jan 2024 10:54:42 +0200 Subject: [PATCH 01/46] sdk: Add timestamp of current system clock right before frame Signed-off-by: Dan Nechita --- apps/server/server.cpp | 22 +++++++++++++------ sdk/src/cameras/itof-camera/camera_itof.cpp | 13 ++++++++++- sdk/src/cameras/itof-frame/frame_impl.cpp | 3 +++ .../connections/target/adsd3500_sensor.cpp | 14 +++++++++++- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/apps/server/server.cpp b/apps/server/server.cpp index 3951bb05f..b25c91de6 100644 --- a/apps/server/server.cpp +++ b/apps/server/server.cpp @@ -50,6 +50,7 @@ using namespace google::protobuf::io; +static const int FRAME_PREPADDING_BYTES = 8; static int interrupted = 0; /* Available sensors */ @@ -199,7 +200,8 @@ int Network::callback_function(struct lws *wsi, buff_frame_to_send[0] = '1'; n = lws_write(wsi, buff_frame_to_send + LWS_SEND_BUFFER_PRE_PADDING, - (buff_frame_length + 1), LWS_WRITE_TEXT); + (buff_frame_length + 1 + FRAME_PREPADDING_BYTES), + LWS_WRITE_TEXT); m_frame_ready = false; if (lws_partial_buffered(wsi)) { latest_sent_msg_is_was_buffered = true; @@ -502,12 +504,18 @@ void invoke_sdk_api(payload::ClientRequest buff_recv) { free(buff_frame_to_send); buff_frame_to_send = NULL; } - buff_frame_to_send = (uint8_t *)malloc( - (buff_frame_length + LWS_SEND_BUFFER_PRE_PADDING + 1) * - sizeof(uint8_t)); - - memcpy(buff_frame_to_send + (LWS_SEND_BUFFER_PRE_PADDING + 1), buffer, - buff_frame_length * sizeof(uint8_t)); + buff_frame_to_send = + (uint8_t *)malloc((buff_frame_length + LWS_SEND_BUFFER_PRE_PADDING + + 1 + FRAME_PREPADDING_BYTES) * + sizeof(uint8_t)); + int64_t *pTimestampLocation = reinterpret_cast( + &buff_frame_to_send[LWS_SEND_BUFFER_PRE_PADDING + 1]); + *pTimestampLocation = + std::chrono::system_clock::now().time_since_epoch().count(); + + memcpy(buff_frame_to_send + + (LWS_SEND_BUFFER_PRE_PADDING + 1 + FRAME_PREPADDING_BYTES), + buffer, buff_frame_length * sizeof(uint8_t)); m_frame_ready = true; diff --git a/sdk/src/cameras/itof-camera/camera_itof.cpp b/sdk/src/cameras/itof-camera/camera_itof.cpp index e99dda2d1..acf928cdf 100644 --- a/sdk/src/cameras/itof-camera/camera_itof.cpp +++ b/sdk/src/cameras/itof-camera/camera_itof.cpp @@ -664,6 +664,14 @@ aditof::Status CameraItof::setFrameType(const std::string &frameType) { m_details.frameType.totalCaptures = 1; } m_details.frameType.dataDetails.clear(); + FrameDataDetails timestampDetails; + timestampDetails.type = "timestamp"; + timestampDetails.width = sizeof(int64_t); + timestampDetails.height = 1; + timestampDetails.subelementSize = 1; + timestampDetails.subelementsPerElement = 1; + m_details.frameType.dataDetails.emplace_back(timestampDetails); + for (const auto &item : (*frameTypeIt).content) { if (item.type == "xyz" && !m_xyzEnabled) { continue; @@ -817,7 +825,7 @@ aditof::Status CameraItof::requestFrame(aditof::Frame *frame, LOG(ERROR) << "Frame type not found!"; return Status::INVALID_ARGUMENT; } else { - frame->getData("raw", &frameDataLocation); + frame->getData("frameData", &frameDataLocation); } } if (!frameDataLocation) { @@ -830,6 +838,9 @@ aditof::Status CameraItof::requestFrame(aditof::Frame *frame, LOG(WARNING) << "Failed to get frame from device"; return status; } + int64_t *timestamp; + frame->getData("timestamp", reinterpret_cast(×tamp)); + frame->getData("raw", &frameDataLocation); if (!m_adsd3500Enabled && !m_isOffline && (m_details.frameType.type != "pcm-native")) { diff --git a/sdk/src/cameras/itof-frame/frame_impl.cpp b/sdk/src/cameras/itof-frame/frame_impl.cpp index a8d2f7b9e..27fb9bc77 100644 --- a/sdk/src/cameras/itof-frame/frame_impl.cpp +++ b/sdk/src/cameras/itof-frame/frame_impl.cpp @@ -196,6 +196,9 @@ void FrameImpl::allocFrameData(const aditof::FrameDetails &details) { total_captures](FrameDataDetails frameDetail) { if (frameDetail.type == "header") { return (unsigned long int)(embed_hdr_length / 2) * total_captures; + } else if (frameDetail.type == "timestamp") { + return (unsigned long int)frameDetail.height * frameDetail.width / + 2; } else if (frameDetail.type == "xyz") { return (unsigned long int)(frameDetail.height * frameDetail.width * sizeof(Point3I)); diff --git a/sdk/src/connections/target/adsd3500_sensor.cpp b/sdk/src/connections/target/adsd3500_sensor.cpp index 43171273d..f0eb4e388 100644 --- a/sdk/src/connections/target/adsd3500_sensor.cpp +++ b/sdk/src/connections/target/adsd3500_sensor.cpp @@ -23,6 +23,7 @@ #include #include #endif +#include #include #include #include @@ -689,6 +690,8 @@ aditof::Status Adsd3500Sensor::getFrame(uint16_t *buffer) { uint8_t *pdata; dev = &m_implData->videoDevs[0]; m_capturesPerFrame = 1; + int64_t *pTimestampLocation = reinterpret_cast(buffer); + for (int idx = 0; idx < m_capturesPerFrame; idx++) { status = waitForBufferPrivate(dev); if (status != Status::OK) { @@ -700,12 +703,21 @@ aditof::Status Adsd3500Sensor::getFrame(uint16_t *buffer) { return status; } + // Get current time as soon as we get the frame from kernel space and + // write the timestamp right before the frame. + *pTimestampLocation = + std::chrono::system_clock::now().time_since_epoch().count(); + status = getInternalBufferPrivate(&pdata, buf_data_len, buf[idx], dev); if (status != Status::OK) { return status; } - memcpy(buffer, pdata, buf_data_len); + // The timestamp occupies 8 bytes. FramePos will be 4 (aka the 4th pair + // of 2 bytes, because buffer points at 2 bytes at a time) + size_t framePos = sizeof(*pTimestampLocation) / sizeof(*buffer); + + memcpy(buffer + framePos, pdata, buf_data_len); status = enqueueInternalBufferPrivate(buf[idx], dev); if (status != Status::OK) { From 7f550903a47837288cca5289c1621de33c13cac4 Mon Sep 17 00:00:00 2001 From: Dan Nechita Date: Thu, 18 Jan 2024 15:59:03 +0200 Subject: [PATCH 02/46] sdk: Don't crash when choosing pcm mode Signed-off-by: Dan Nechita --- sdk/src/cameras/itof-camera/camera_itof.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/sdk/src/cameras/itof-camera/camera_itof.cpp b/sdk/src/cameras/itof-camera/camera_itof.cpp index acf928cdf..5b9180f3d 100644 --- a/sdk/src/cameras/itof-camera/camera_itof.cpp +++ b/sdk/src/cameras/itof-camera/camera_itof.cpp @@ -816,18 +816,20 @@ aditof::Status CameraItof::requestFrame(aditof::Frame *frame, } uint16_t *frameDataLocation = nullptr; + std::string dataType; if (m_targetFramesAreComputed) { - frame->getData("frameData", &frameDataLocation); + dataType = "frameData"; } else { if ((m_details.frameType.type == "pcm-native")) { - frame->getData("ir", &frameDataLocation); + dataType = "ir"; } else if (m_details.frameType.type == "") { LOG(ERROR) << "Frame type not found!"; return Status::INVALID_ARGUMENT; } else { - frame->getData("frameData", &frameDataLocation); + dataType = "raw"; } } + frame->getData("frameData", &frameDataLocation); if (!frameDataLocation) { LOG(WARNING) << "getframe failed to allocated valid frame"; return status; @@ -840,7 +842,12 @@ aditof::Status CameraItof::requestFrame(aditof::Frame *frame, } int64_t *timestamp; frame->getData("timestamp", reinterpret_cast(×tamp)); - frame->getData("raw", &frameDataLocation); + + frame->getData(dataType, &frameDataLocation); + if (!frameDataLocation) { + LOG(WARNING) << "getframe failed to allocated valid frame"; + return status; + } if (!m_adsd3500Enabled && !m_isOffline && (m_details.frameType.type != "pcm-native")) { From 98a63ea445fbd79a69d39fdafc6eaba2aa117684 Mon Sep 17 00:00:00 2001 From: Dan Nechita Date: Mon, 22 Jan 2024 10:16:10 +0200 Subject: [PATCH 03/46] examples/first-frame: Show how to get the timestamp of a frame Signed-off-by: Dan Nechita --- examples/first-frame/main.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/examples/first-frame/main.cpp b/examples/first-frame/main.cpp index 476c07639..ef2153c76 100644 --- a/examples/first-frame/main.cpp +++ b/examples/first-frame/main.cpp @@ -40,6 +40,7 @@ #else #include #endif +#include #include #include #include @@ -254,6 +255,22 @@ int main(int argc, char *argv[]) { save_frame(frame, "ir"); save_frame(frame, "depth"); + // Get the timestamp of the frame + int64_t *timestamp; + frame.getData("timestamp", reinterpret_cast(×tamp)); + if (timestamp) { + LOG(INFO) << "frame timestamp is: " << *timestamp; + + // Display amount of time between the moment the frame was captured and this moment + auto timePoint = std::chrono::system_clock::time_point( + std::chrono::duration(*timestamp)); + const auto now = std::chrono::system_clock::now(); + auto duration = std::chrono::duration_cast( + now - timePoint); + LOG(INFO) << "milliseconds have passed since the frame was captured: " + << duration.count(); + } + // Example of reading temperature from hardware uint16_t sensorTmp = 0; uint16_t laserTmp = 0; From 0c0a936af368ca7c38f9520c6a9355f426dd84fc Mon Sep 17 00:00:00 2001 From: Dan Nechita Date: Mon, 22 Jan 2024 11:57:23 +0200 Subject: [PATCH 04/46] Use milliseconds resolution Signed-off-by: Dan Nechita --- apps/server/server.cpp | 5 ++++- examples/first-frame/main.cpp | 16 +++++++++------- sdk/src/connections/target/adsd3500_sensor.cpp | 7 +++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/apps/server/server.cpp b/apps/server/server.cpp index b25c91de6..f0015ef21 100644 --- a/apps/server/server.cpp +++ b/apps/server/server.cpp @@ -511,7 +511,10 @@ void invoke_sdk_api(payload::ClientRequest buff_recv) { int64_t *pTimestampLocation = reinterpret_cast( &buff_frame_to_send[LWS_SEND_BUFFER_PRE_PADDING + 1]); *pTimestampLocation = - std::chrono::system_clock::now().time_since_epoch().count(); + std::chrono::time_point_cast( + std::chrono::system_clock::now()) + .time_since_epoch() + .count(); memcpy(buff_frame_to_send + (LWS_SEND_BUFFER_PRE_PADDING + 1 + FRAME_PREPADDING_BYTES), diff --git a/examples/first-frame/main.cpp b/examples/first-frame/main.cpp index ef2153c76..dab918227 100644 --- a/examples/first-frame/main.cpp +++ b/examples/first-frame/main.cpp @@ -262,13 +262,15 @@ int main(int argc, char *argv[]) { LOG(INFO) << "frame timestamp is: " << *timestamp; // Display amount of time between the moment the frame was captured and this moment - auto timePoint = std::chrono::system_clock::time_point( - std::chrono::duration(*timestamp)); - const auto now = std::chrono::system_clock::now(); - auto duration = std::chrono::duration_cast( - now - timePoint); - LOG(INFO) << "milliseconds have passed since the frame was captured: " - << duration.count(); + auto frameCapturedTimepointMs = + std::chrono::time_point_cast( + std::chrono::system_clock::time_point( + std::chrono::milliseconds(*timestamp))); + const auto nowMs = + std::chrono::time_point_cast( + std::chrono::system_clock::now()); + LOG(INFO) << "time passed since the frame was captured: " + << (nowMs - frameCapturedTimepointMs).count() << "ms"; } // Example of reading temperature from hardware diff --git a/sdk/src/connections/target/adsd3500_sensor.cpp b/sdk/src/connections/target/adsd3500_sensor.cpp index f0eb4e388..0413f5a81 100644 --- a/sdk/src/connections/target/adsd3500_sensor.cpp +++ b/sdk/src/connections/target/adsd3500_sensor.cpp @@ -683,6 +683,8 @@ aditof::Status Adsd3500Sensor::program(const uint8_t *firmware, size_t size) { aditof::Status Adsd3500Sensor::getFrame(uint16_t *buffer) { using namespace aditof; + using namespace std::chrono; + struct v4l2_buffer buf[MAX_SUBFRAMES_COUNT]; struct VideoDev *dev; Status status; @@ -705,8 +707,9 @@ aditof::Status Adsd3500Sensor::getFrame(uint16_t *buffer) { // Get current time as soon as we get the frame from kernel space and // write the timestamp right before the frame. - *pTimestampLocation = - std::chrono::system_clock::now().time_since_epoch().count(); + *pTimestampLocation = time_point_cast(system_clock::now()) + .time_since_epoch() + .count(); status = getInternalBufferPrivate(&pdata, buf_data_len, buf[idx], dev); if (status != Status::OK) { From bd1cdde06c5ba3e40f53f35ab293adb61423dc83 Mon Sep 17 00:00:00 2001 From: SeptimiuVana Date: Mon, 8 Jan 2024 12:26:20 +0200 Subject: [PATCH 05/46] ci/windows-build/install_deps.ps1: update OpenSSL path Signed-off-by: SeptimiuVana --- ci/azure/windows-build/install_deps.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/azure/windows-build/install_deps.ps1 b/ci/azure/windows-build/install_deps.ps1 index c909cd002..a971e2653 100644 --- a/ci/azure/windows-build/install_deps.ps1 +++ b/ci/azure/windows-build/install_deps.ps1 @@ -15,7 +15,7 @@ mkdir -p deps_installed/Debug/protobuf mkdir -p deps_installed/Release/websockets mkdir -p deps_installed/Debug/websockets -cp -r C:/'Program Files'/OpenSSL-Win64 deps_installed +cp -r C:/'Program Files'/OpenSSL deps_installed #Install glog git clone --branch v0.6.0 --depth 1 https://github.com/google/glog @@ -39,11 +39,11 @@ mkdir build_3_2_3_Release mkdir build_3_2_3_Debug cd build_3_2_3_Release -cmake -DOPENSSL_ROOT_DIR="$local_path/deps_installed/OpenSSL-Win64" -DCMAKE_INSTALL_PREFIX="$local_path/deps_installed/Release/websockets" -G $GENERATOR .. +cmake -DOPENSSL_ROOT_DIR="$local_path/deps_installed/OpenSSL" -DCMAKE_INSTALL_PREFIX="$local_path/deps_installed/Release/websockets" -G $GENERATOR .. cmake --build . --target install --config Release -j 4 cd ../build_3_2_3_Debug -cmake -DOPENSSL_ROOT_DIR="$local_path/deps_installed/OpenSSL-Win64" -DCMAKE_INSTALL_PREFIX="$local_path/deps_installed/Debug/websockets" -G $GENERATOR .. +cmake -DOPENSSL_ROOT_DIR="$local_path/deps_installed/OpenSSL" -DCMAKE_INSTALL_PREFIX="$local_path/deps_installed/Debug/websockets" -G $GENERATOR .. cmake --build . --target install --config Debug -j 4 cd $local_path From b9126fa389dadfea5ec987bcd9f2aa137e7791ae Mon Sep 17 00:00:00 2001 From: SeptimiuVana Date: Mon, 8 Jan 2024 13:51:42 +0200 Subject: [PATCH 06/46] ci/azure/windows-build/build_sdk.ps1: update openssl paths Signed-off-by: SeptimiuVana --- ci/azure/windows-build/build_sdk.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/azure/windows-build/build_sdk.ps1 b/ci/azure/windows-build/build_sdk.ps1 index 10ff2ab8e..962f01014 100644 --- a/ci/azure/windows-build/build_sdk.ps1 +++ b/ci/azure/windows-build/build_sdk.ps1 @@ -7,10 +7,10 @@ mkdir build_Debug mkdir ../libs cd build_Release -cmake -DUSE_DEPTH_COMPUTE_STUBS=1 -DWITH_OPENCV=on -DWITH_PYTHON=on -DCMAKE_PREFIX_PATH="../deps_installed/Release/glog;../deps_installed/Release/protobuf;../deps_installed/Release/websockets;..\deps_installed\OpenSSL-Win64" -DOpenCV_DIR="C:/tools/opencv/build/x64/vc15/lib" -DOPENSSL_INCLUDE_DIRS="..\deps_installed\OpenSSL-Win64\include" .. +cmake -DUSE_DEPTH_COMPUTE_STUBS=1 -DWITH_OPENCV=on -DWITH_PYTHON=on -DCMAKE_PREFIX_PATH="../deps_installed/Release/glog;../deps_installed/Release/protobuf;../deps_installed/Release/websockets;..\deps_installed\OpenSSL" -DOpenCV_DIR="C:/tools/opencv/build/x64/vc15/lib" -DOPENSSL_INCLUDE_DIRS="..\deps_installed\OpenSSL\include" .. cmake --build . --target install --config Release -j 4 cd ../build_Debug -cmake -DUSE_DEPTH_COMPUTE_STUBS=1 -DWITH_OPENCV=on -DWITH_PYTHON=on -DCMAKE_PREFIX_PATH="../deps_installed/Debug/glog;../deps_installed/Debug/protobuf;../deps_installed/Debug/websockets;..\deps_installed\OpenSSL-Win64" -DOpenCV_DIR="C:/tools/opencv/build/x64/vc15/lib" -DOPENSSL_INCLUDE_DIRS="..\deps_installed\OpenSSL-Win64\include" .. +cmake -DUSE_DEPTH_COMPUTE_STUBS=1 -DWITH_OPENCV=on -DWITH_PYTHON=on -DCMAKE_PREFIX_PATH="../deps_installed/Debug/glog;../deps_installed/Debug/protobuf;../deps_installed/Debug/websockets;..\deps_installed\OpenSSL" -DOpenCV_DIR="C:/tools/opencv/build/x64/vc15/lib" -DOPENSSL_INCLUDE_DIRS="..\deps_installed\OpenSSL\include" .. cmake --build . --target install --config Debug -j 4 From 36c374c57bb19cae53b2c27a39a677ac49dfe77b Mon Sep 17 00:00:00 2001 From: SeptimiuVana Date: Mon, 8 Jan 2024 15:31:04 +0200 Subject: [PATCH 07/46] ci: remove openssl from windows build Signed-off-by: SeptimiuVana --- ci/azure/windows-build/build_sdk.ps1 | 4 ++-- ci/azure/windows-build/install_deps.ps1 | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/ci/azure/windows-build/build_sdk.ps1 b/ci/azure/windows-build/build_sdk.ps1 index 962f01014..de85d89cf 100644 --- a/ci/azure/windows-build/build_sdk.ps1 +++ b/ci/azure/windows-build/build_sdk.ps1 @@ -7,10 +7,10 @@ mkdir build_Debug mkdir ../libs cd build_Release -cmake -DUSE_DEPTH_COMPUTE_STUBS=1 -DWITH_OPENCV=on -DWITH_PYTHON=on -DCMAKE_PREFIX_PATH="../deps_installed/Release/glog;../deps_installed/Release/protobuf;../deps_installed/Release/websockets;..\deps_installed\OpenSSL" -DOpenCV_DIR="C:/tools/opencv/build/x64/vc15/lib" -DOPENSSL_INCLUDE_DIRS="..\deps_installed\OpenSSL\include" .. +cmake -DUSE_DEPTH_COMPUTE_STUBS=1 -DWITH_OPENCV=on -DWITH_PYTHON=on -DCMAKE_PREFIX_PATH="../deps_installed/Release/glog;../deps_installed/Release/protobuf;../deps_installed/Release/websockets" -DOpenCV_DIR="C:/tools/opencv/build/x64/vc15/lib" .. cmake --build . --target install --config Release -j 4 cd ../build_Debug -cmake -DUSE_DEPTH_COMPUTE_STUBS=1 -DWITH_OPENCV=on -DWITH_PYTHON=on -DCMAKE_PREFIX_PATH="../deps_installed/Debug/glog;../deps_installed/Debug/protobuf;../deps_installed/Debug/websockets;..\deps_installed\OpenSSL" -DOpenCV_DIR="C:/tools/opencv/build/x64/vc15/lib" -DOPENSSL_INCLUDE_DIRS="..\deps_installed\OpenSSL\include" .. +cmake -DUSE_DEPTH_COMPUTE_STUBS=1 -DWITH_OPENCV=on -DWITH_PYTHON=on -DCMAKE_PREFIX_PATH="../deps_installed/Debug/glog;../deps_installed/Debug/protobuf;../deps_installed/Debug/websockets" -DOpenCV_DIR="C:/tools/opencv/build/x64/vc15/lib" .. cmake --build . --target install --config Debug -j 4 diff --git a/ci/azure/windows-build/install_deps.ps1 b/ci/azure/windows-build/install_deps.ps1 index a971e2653..6a4cbb3b3 100644 --- a/ci/azure/windows-build/install_deps.ps1 +++ b/ci/azure/windows-build/install_deps.ps1 @@ -1,8 +1,6 @@ $ARCH=$Env:ARCH $GENERATOR=$Env:COMPILER -choco install openssl - $local_path=$pwd mkdir deps_installed @@ -15,8 +13,6 @@ mkdir -p deps_installed/Debug/protobuf mkdir -p deps_installed/Release/websockets mkdir -p deps_installed/Debug/websockets -cp -r C:/'Program Files'/OpenSSL deps_installed - #Install glog git clone --branch v0.6.0 --depth 1 https://github.com/google/glog cd glog @@ -39,11 +35,11 @@ mkdir build_3_2_3_Release mkdir build_3_2_3_Debug cd build_3_2_3_Release -cmake -DOPENSSL_ROOT_DIR="$local_path/deps_installed/OpenSSL" -DCMAKE_INSTALL_PREFIX="$local_path/deps_installed/Release/websockets" -G $GENERATOR .. +cmake -DLWS_WITH_SSL=OFF -DCMAKE_INSTALL_PREFIX="$local_path/deps_installed/Release/websockets" -G $GENERATOR .. cmake --build . --target install --config Release -j 4 cd ../build_3_2_3_Debug -cmake -DOPENSSL_ROOT_DIR="$local_path/deps_installed/OpenSSL" -DCMAKE_INSTALL_PREFIX="$local_path/deps_installed/Debug/websockets" -G $GENERATOR .. +cmake -DLWS_WITH_SSL=OFF -DCMAKE_INSTALL_PREFIX="$local_path/deps_installed/Debug/websockets" -G $GENERATOR .. cmake --build . --target install --config Debug -j 4 cd $local_path From 96a5c152ac7c8b1930194f75770c8e6dade0aa2a Mon Sep 17 00:00:00 2001 From: SeptimiuVana Date: Mon, 8 Jan 2024 15:41:12 +0200 Subject: [PATCH 08/46] azure-pipelines: update keys for cache Signed-off-by: SeptimiuVana --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 734dc71ca..c021d6494 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -115,8 +115,8 @@ jobs: versionSpec: '3.9' - task: Cache@2 inputs: - key: deps_windowsZ | "$(imageName)" - restoreKeys: deps_windowsZ | "$(imageName)" + key: deps_windows | "$(imageName)" + restoreKeys: deps_windows | "$(imageName)" path: $(Agent.BuildDirectory)/s/deps_installed cacheHitVar: CACHE_RESTORED - task: PowerShell@2 From c17fe4204f842d9dc2cb161ec5675f782c936ee7 Mon Sep 17 00:00:00 2001 From: SeptimiuVana Date: Tue, 30 Jan 2024 14:47:42 +0200 Subject: [PATCH 09/46] Fix to use uvc gadget with nvidia orin as host Signed-off-by: SeptimiuVana --- apps/uvc-app/tof-sdk-interface.cpp | 1 + .../step1/usr/share/systemd/usb-gadget.sh | 4 ++-- .../usb/linux/usb_depth_sensor_linux.cpp | 14 ++++++++++++++ sdk/src/connections/usb/linux/usb_linux_utils.cpp | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/apps/uvc-app/tof-sdk-interface.cpp b/apps/uvc-app/tof-sdk-interface.cpp index 3d51d508b..cc33489e9 100644 --- a/apps/uvc-app/tof-sdk-interface.cpp +++ b/apps/uvc-app/tof-sdk-interface.cpp @@ -427,6 +427,7 @@ void handleClientRequest(const char *in_buf, const size_t in_len, } } // switch + response.add_int32_payload(10); response.SerializeToString(&serverResponseBlob); *out_buf = (char *)malloc(serverResponseBlob.size()); diff --git a/sdcard-images-utils/nxp/patches/ubuntu_overlay/step1/usr/share/systemd/usb-gadget.sh b/sdcard-images-utils/nxp/patches/ubuntu_overlay/step1/usr/share/systemd/usb-gadget.sh index 0f4add9b9..e73770d2c 100755 --- a/sdcard-images-utils/nxp/patches/ubuntu_overlay/step1/usr/share/systemd/usb-gadget.sh +++ b/sdcard-images-utils/nxp/patches/ubuntu_overlay/step1/usr/share/systemd/usb-gadget.sh @@ -176,8 +176,8 @@ create_uvc() { #echo 2048 > functions/$FUNCTION/streaming_maxpacket #echo 1024 > functions/$FUNCTION/streaming_maxpacket - echo 15 > functions/$FUNCTION/streaming_maxburst - echo 3 > functions/$FUNCTION/streaming_interval + echo 1 > functions/$FUNCTION/streaming_maxburst + echo 1 > functions/$FUNCTION/streaming_interval ln -s functions/$FUNCTION $CONFIG } diff --git a/sdk/src/connections/usb/linux/usb_depth_sensor_linux.cpp b/sdk/src/connections/usb/linux/usb_depth_sensor_linux.cpp index 12fbfc2b6..db3a2cf47 100644 --- a/sdk/src/connections/usb/linux/usb_depth_sensor_linux.cpp +++ b/sdk/src/connections/usb/linux/usb_depth_sensor_linux.cpp @@ -145,6 +145,7 @@ aditof::Status UsbDepthSensor::open() { usb_payload::ClientRequest requestMsg; requestMsg.set_func_name( usb_payload::FunctionName::GET_AVAILABLE_FRAME_TYPES); + requestMsg.add_func_int32_param(10); std::string requestStr; requestMsg.SerializeToString(&requestStr); status = UsbLinuxUtils::uvcExUnitSendRequest(m_implData->fd, requestStr); @@ -262,6 +263,7 @@ UsbDepthSensor::setFrameType(const aditof::DepthSensorFrameType &type) { UsbUtils::depthSensorFrameTypeToProtoMsg(type, frameTypeMsg); // Send request requestMsg.set_func_name(usb_payload::FunctionName::SET_FRAME_TYPE); + requestMsg.add_func_int32_param(10); std::string requestStr; requestMsg.SerializeToString(&requestStr); status = UsbLinuxUtils::uvcExUnitSendRequest(m_implData->fd, requestStr); @@ -565,6 +567,7 @@ aditof::Status UsbDepthSensor::readRegisters(const uint16_t *address, requestMsg.add_func_int32_param(static_cast<::google::int32>(burst)); requestMsg.add_func_bytes_param(address, burst ? 2 : length * sizeof(uint16_t)); + requestMsg.add_func_int32_param(10); // Send request std::string requestStr; @@ -616,6 +619,7 @@ aditof::Status UsbDepthSensor::writeRegisters(const uint16_t *address, requestMsg.add_func_bytes_param(address, burst ? 2 : length * sizeof(uint16_t)); requestMsg.add_func_bytes_param(data, length * sizeof(uint16_t)); + requestMsg.add_func_int32_param(10); // Send request std::string requestStr; @@ -658,6 +662,7 @@ UsbDepthSensor::getAvailableControls(std::vector &controls) const { // Construct request message usb_payload::ClientRequest requestMsg; requestMsg.set_func_name(usb_payload::FunctionName::GET_AVAILABLE_CONTROLS); + requestMsg.add_func_int32_param(10); // Send request std::string requestStr; @@ -713,6 +718,7 @@ aditof::Status UsbDepthSensor::setControl(const std::string &control, requestMsg.set_func_name(usb_payload::FunctionName::SET_CONTROL); requestMsg.add_func_strings_param(control); requestMsg.add_func_strings_param(value); + requestMsg.add_func_int32_param(10); // Send request std::string requestStr; @@ -757,6 +763,7 @@ aditof::Status UsbDepthSensor::getControl(const std::string &control, usb_payload::ClientRequest requestMsg; requestMsg.set_func_name(usb_payload::FunctionName::GET_CONTROL); requestMsg.add_func_strings_param(control); + requestMsg.add_func_int32_param(10); // Send request std::string requestStr; @@ -832,6 +839,7 @@ aditof::Status UsbDepthSensor::adsd3500_read_cmd(uint16_t cmd, uint16_t *data, requestMsg.set_func_name(usb_payload::FunctionName::ADSD3500_READ_CMD); requestMsg.add_func_int32_param(static_cast<::google::int32>(cmd)); requestMsg.add_func_int32_param(static_cast<::google::int32>(usDelay)); + requestMsg.add_func_int32_param(10); // Send request std::string requestStr; @@ -878,6 +886,7 @@ aditof::Status UsbDepthSensor::adsd3500_write_cmd(uint16_t cmd, uint16_t data) { requestMsg.set_func_name(usb_payload::FunctionName::ADSD3500_WRITE_CMD); requestMsg.add_func_int32_param(static_cast<::google::int32>(cmd)); requestMsg.add_func_bytes_param(&data, sizeof(uint16_t)); + requestMsg.add_func_int32_param(10); // Send request std::string requestStr; @@ -925,6 +934,7 @@ aditof::Status UsbDepthSensor::adsd3500_read_payload_cmd(uint32_t cmd, requestMsg.add_func_int32_param(static_cast<::google::int32>(cmd)); requestMsg.add_func_int32_param(static_cast<::google::int32>(payload_len)); requestMsg.add_func_bytes_param(readback_data, 4 * sizeof(uint8_t)); + requestMsg.add_func_int32_param(10); // Send request std::string requestStr; @@ -973,6 +983,7 @@ aditof::Status UsbDepthSensor::adsd3500_read_payload(uint8_t *payload, usb_payload::ClientRequest requestMsg; requestMsg.set_func_name(usb_payload::FunctionName::ADSD3500_WRITE_PAYLOAD); requestMsg.add_func_int32_param(static_cast<::google::int32>(payload_len)); + requestMsg.add_func_int32_param(10); // Send request std::string requestStr; @@ -1022,6 +1033,7 @@ UsbDepthSensor::adsd3500_write_payload_cmd(uint32_t cmd, uint8_t *payload, requestMsg.add_func_int32_param(static_cast<::google::int32>(cmd)); requestMsg.add_func_int32_param(static_cast<::google::int32>(payload_len)); requestMsg.add_func_bytes_param(payload, payload_len); + requestMsg.add_func_int32_param(10); // Send request std::string requestStr; @@ -1068,6 +1080,7 @@ aditof::Status UsbDepthSensor::adsd3500_write_payload(uint8_t *payload, requestMsg.set_func_name(usb_payload::FunctionName::ADSD3500_WRITE_PAYLOAD); requestMsg.add_func_int32_param(static_cast<::google::int32>(payload_len)); requestMsg.add_func_bytes_param(payload, payload_len); + requestMsg.add_func_int32_param(10); // Send request std::string requestStr; @@ -1109,6 +1122,7 @@ aditof::Status UsbDepthSensor::adsd3500_reset() { // Construct request message usb_payload::ClientRequest requestMsg; requestMsg.set_func_name(usb_payload::FunctionName::ADSD3500_RESET); + requestMsg.add_func_int32_param(10); // Send request std::string requestStr; diff --git a/sdk/src/connections/usb/linux/usb_linux_utils.cpp b/sdk/src/connections/usb/linux/usb_linux_utils.cpp index f52e6e1bb..3bf857742 100644 --- a/sdk/src/connections/usb/linux/usb_linux_utils.cpp +++ b/sdk/src/connections/usb/linux/usb_linux_utils.cpp @@ -355,7 +355,7 @@ aditof::Status UsbLinuxUtils::uvcExUnitGetResponse(int fd, << ret << "(" << strerror(ret) << ")"; return aditof::Status::GENERIC_ERROR; } - size_t stringLength = reinterpret_cast(packet)[0]; + size_t stringLength = *(int16_t *)packet; responseStr.reserve(stringLength); From 57f9c345168c855240d899e4d51afd81279d7426 Mon Sep 17 00:00:00 2001 From: srivatsan Date: Wed, 21 Feb 2024 17:23:43 -0500 Subject: [PATCH 10/46] lws_callback_on_writeable() call must be called twice --- sdk/src/connections/network/network.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/connections/network/network.cpp b/sdk/src/connections/network/network.cpp index deba23511..c8f004474 100644 --- a/sdk/src/connections/network/network.cpp +++ b/sdk/src/connections/network/network.cpp @@ -242,6 +242,7 @@ int Network::SendCommand() { while (numRetry++ < MAX_RETRY_CNT && Server_Connected[m_connectionId] != false) { + lws_callback_on_writable(web_socket.at(m_connectionId)); lws_callback_on_writable(web_socket.at(m_connectionId)); /*Acquire the lock*/ std::unique_lock mlock(m_mutex[m_connectionId]); From c657d0a696993c97cbdfb1937cd4f26a2b602177 Mon Sep 17 00:00:00 2001 From: Srivatsan Ravichandran Date: Thu, 22 Feb 2024 20:39:25 +0000 Subject: [PATCH 11/46] Modified the example to get frames for 30 minutes and log timestamp difference and requestFrame() call time to a file --- examples/first-frame/main.cpp | 81 ++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/examples/first-frame/main.cpp b/examples/first-frame/main.cpp index dab918227..2097b0284 100644 --- a/examples/first-frame/main.cpp +++ b/examples/first-frame/main.cpp @@ -44,6 +44,7 @@ #include #include #include +#include using namespace aditof; @@ -99,6 +100,12 @@ Status save_frame(aditof::Frame &frame, std::string frameType) { } int main(int argc, char *argv[]) { + + // Start time + auto start_time = std::chrono::steady_clock::now(); + // Duration of 30 minutes + auto duration = std::chrono::minutes(30); + std::map command_map = { {"-h", {"--help", false, "", ""}}, {"-ip", {"--ip", false, "", ""}}, @@ -242,35 +249,51 @@ int main(int argc, char *argv[]) { LOG(ERROR) << "Could not start the camera!"; return 0; } - aditof::Frame frame; - status = camera->requestFrame(&frame); - if (status != Status::OK) { - LOG(ERROR) << "Could not request frame!"; - return 0; - } else { - LOG(INFO) << "succesfully requested frame!"; - } + std::ofstream file("timestampDifference_and_requestFrameExecutionTime_values.txt", std::ios_base::trunc); + int64_t *timestamp; + + while(true) { + aditof::Frame frame; - save_frame(frame, "ir"); - save_frame(frame, "depth"); + // Check if 30 minutes have elapsed + if (std::chrono::steady_clock::now() - start_time >= duration) { + break; // Exit the loop if 30 minutes have elapsed + } + + auto start = std::chrono::high_resolution_clock::now(); + status = camera->requestFrame(&frame); + auto end = std::chrono::high_resolution_clock::now(); + + // Calculate duration in milliseconds + std::chrono::duration requestFrameDuration = end - start; + // Print duration + LOG(INFO) << "requestFrame Execution time: " << requestFrameDuration.count() << " ms" << std::endl; + + if (status != Status::OK) { + LOG(ERROR) << "Could not request frame!"; + file << "Unable to get the frame" << "\n"; + } else { + LOG(INFO) << "succesfully requested frame!"; + + frame.getData("timestamp", reinterpret_cast(×tamp)); + if (timestamp) { + LOG(INFO) << "frame timestamp is: " << *timestamp; + + // Display amount of time between the moment the frame was captured and this moment + auto frameCapturedTimepointMs = + std::chrono::time_point_cast( + std::chrono::system_clock::time_point( + std::chrono::milliseconds(*timestamp))); + const auto nowMs = + std::chrono::time_point_cast( + std::chrono::system_clock::now()); + LOG(INFO) << "time passed since the frame was captured: " + << (nowMs - frameCapturedTimepointMs).count() << "ms"; + file << "requestFrameDuration: " << requestFrameDuration.count() << ", timestamp_diff: " << (nowMs - frameCapturedTimepointMs).count() << "\n"; + } + } - // Get the timestamp of the frame - int64_t *timestamp; - frame.getData("timestamp", reinterpret_cast(×tamp)); - if (timestamp) { - LOG(INFO) << "frame timestamp is: " << *timestamp; - - // Display amount of time between the moment the frame was captured and this moment - auto frameCapturedTimepointMs = - std::chrono::time_point_cast( - std::chrono::system_clock::time_point( - std::chrono::milliseconds(*timestamp))); - const auto nowMs = - std::chrono::time_point_cast( - std::chrono::system_clock::now()); - LOG(INFO) << "time passed since the frame was captured: " - << (nowMs - frameCapturedTimepointMs).count() << "ms"; } // Example of reading temperature from hardware @@ -280,11 +303,12 @@ int main(int argc, char *argv[]) { if (status != Status::OK) { LOG(ERROR) << "Could not read sensor temperature!"; } - status = camera->adsd3500GetLaserTemperature(laserTmp); if (status != Status::OK) { LOG(ERROR) << "Could not read laser temperature!"; } + LOG(INFO) << "Sensor temperature: " << sensorTmp; + LOG(INFO) << "Laser temperature: " << laserTmp; status = camera->stop(); if (status != Status::OK) { @@ -292,8 +316,7 @@ int main(int argc, char *argv[]) { return 0; } - LOG(INFO) << "Sensor temperature: " << sensorTmp; - LOG(INFO) << "Laser temperature: " << laserTmp; + file.close(); return 0; } From 469e38d81c61f6e28afe119a6fdcda1d0e194d79 Mon Sep 17 00:00:00 2001 From: Srivatsan Ravichandran Date: Tue, 2 Apr 2024 14:01:53 -0500 Subject: [PATCH 12/46] Example program showing ADSD3500, device driver and depth-compute library --- examples/adsd3500_sample_program/Makefile | 61 + examples/adsd3500_sample_program/README.txt | 41 + .../bin/reset_adsd3500 | Bin 0 -> 89088 bytes .../adsd3500_sample_program/bin/run_adsd3500 | Bin 0 -> 89088 bytes .../adsd3500_sample_program/bin/stop_stream | Bin 0 -> 89088 bytes .../config/RawToDepthAdsd3030_lr-mixed.ini | 23 + .../config/RawToDepthAdsd3030_lr-native.ini | 23 + .../config/RawToDepthAdsd3030_lr-qnative.ini | 23 + .../config/RawToDepthAdsd3030_sr-mixed.ini | 23 + .../config/RawToDepthAdsd3030_sr-native.ini | 23 + .../config/RawToDepthAdsd3030_sr-qnative.ini | 23 + .../config/RawToDepthAdsd3500_lr-mixed.ini | 23 + .../config/RawToDepthAdsd3500_lr-native.ini | 23 + .../config/RawToDepthAdsd3500_lr-qnative.ini | 23 + .../config/RawToDepthAdsd3500_sr-mixed.ini | 23 + .../config/RawToDepthAdsd3500_sr-native.ini | 23 + .../config/RawToDepthAdsd3500_sr-qnative.ini | 23 + .../config/RawToDepthAdsd_pcm-native.ini | 23 + .../config/config_adsd3500_adsd3030.json | 4 + .../config/config_adsd3500_adsd3100.json | 4 + .../adsd3500_sample_program/config/logo.png | Bin 0 -> 7755 bytes examples/adsd3500_sample_program/crc32/crc.c | 110 ++ examples/adsd3500_sample_program/crc32/crc.h | 26 + .../TOF_Calibration_Types.h | 390 ++++ .../depthComputeLibrary/tofiCompute.cpp | 155 ++ .../depthComputeLibrary/tofiConfig.cpp | 105 ++ .../include/adsd3500_util.h | 199 ++ .../src/GetDealiasAndIntrinsic.cpp | 33 + .../adsd3500_sample_program/src/GetFps.cpp | 39 + .../src/GetImageMode.cpp | 38 + .../adsd3500_sample_program/src/ReadCCB.cpp | 43 + .../src/ReceiveFrames.cpp | 33 + .../src/ResetAdsd3500.cpp | 33 + .../adsd3500_sample_program/src/SetFps.cpp | 58 + .../src/SetImageMode.cpp | 56 + .../src/StartStream.cpp | 33 + .../src/StopStream.cpp | 33 + .../src/adsd3500_util.cpp | 1670 +++++++++++++++++ .../src/run_adsd3500.cpp | 103 + .../src/test_program.cpp | 110 ++ 40 files changed, 3676 insertions(+) create mode 100644 examples/adsd3500_sample_program/Makefile create mode 100644 examples/adsd3500_sample_program/README.txt create mode 100755 examples/adsd3500_sample_program/bin/reset_adsd3500 create mode 100755 examples/adsd3500_sample_program/bin/run_adsd3500 create mode 100755 examples/adsd3500_sample_program/bin/stop_stream create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd3030_lr-mixed.ini create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd3030_lr-native.ini create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd3030_lr-qnative.ini create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd3030_sr-mixed.ini create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd3030_sr-native.ini create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd3030_sr-qnative.ini create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd3500_lr-mixed.ini create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd3500_lr-native.ini create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd3500_lr-qnative.ini create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd3500_sr-mixed.ini create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd3500_sr-native.ini create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd3500_sr-qnative.ini create mode 100644 examples/adsd3500_sample_program/config/RawToDepthAdsd_pcm-native.ini create mode 100644 examples/adsd3500_sample_program/config/config_adsd3500_adsd3030.json create mode 100644 examples/adsd3500_sample_program/config/config_adsd3500_adsd3100.json create mode 100644 examples/adsd3500_sample_program/config/logo.png create mode 100644 examples/adsd3500_sample_program/crc32/crc.c create mode 100644 examples/adsd3500_sample_program/crc32/crc.h create mode 100644 examples/adsd3500_sample_program/depthComputeLibrary/TOF_Calibration_Types.h create mode 100644 examples/adsd3500_sample_program/depthComputeLibrary/tofiCompute.cpp create mode 100644 examples/adsd3500_sample_program/depthComputeLibrary/tofiConfig.cpp create mode 100644 examples/adsd3500_sample_program/include/adsd3500_util.h create mode 100644 examples/adsd3500_sample_program/src/GetDealiasAndIntrinsic.cpp create mode 100644 examples/adsd3500_sample_program/src/GetFps.cpp create mode 100644 examples/adsd3500_sample_program/src/GetImageMode.cpp create mode 100644 examples/adsd3500_sample_program/src/ReadCCB.cpp create mode 100644 examples/adsd3500_sample_program/src/ReceiveFrames.cpp create mode 100644 examples/adsd3500_sample_program/src/ResetAdsd3500.cpp create mode 100644 examples/adsd3500_sample_program/src/SetFps.cpp create mode 100644 examples/adsd3500_sample_program/src/SetImageMode.cpp create mode 100644 examples/adsd3500_sample_program/src/StartStream.cpp create mode 100644 examples/adsd3500_sample_program/src/StopStream.cpp create mode 100644 examples/adsd3500_sample_program/src/adsd3500_util.cpp create mode 100644 examples/adsd3500_sample_program/src/run_adsd3500.cpp create mode 100644 examples/adsd3500_sample_program/src/test_program.cpp diff --git a/examples/adsd3500_sample_program/Makefile b/examples/adsd3500_sample_program/Makefile new file mode 100644 index 000000000..ae6d01248 --- /dev/null +++ b/examples/adsd3500_sample_program/Makefile @@ -0,0 +1,61 @@ +# the compiler: g++ for C++ program +#CXX = g++ +CXX = aarch64-linux-gnu-g++ +SRC_DIR = src +INCLUDE_DIR = include +BIN_DIR = bin +CRC_DIR = crc32 +CXXCFLAGS = -I$(INCLUDE_DIR) -std=c++11 +CRCS = $(CRC_DIR)/*.c + +#all: clean reset_adsd3500 set_image_mode get_image_mode start_stream stop_stream receive_frames set_fps get_fps read_ccb + +all: clean run_adsd3500 start_stream set_image_mode get_image_mode stop_stream + +first_frame: $(SRC_DIR)/firstFrame.cpp $(SRC_DIR)/adsd3500_util.cpp $(CRCS) + $(CXX) $^ -o $@ + mv $@ $(BIN_DIR) + +reset_adsd3500: $(SRC_DIR)/ResetAdsd3500.cpp $(SRC_DIR)/adsd3500_util.cpp $(CRCS) + $(CXX) $^ -o $@ + mv $@ $(BIN_DIR) + +set_fps: $(SRC_DIR)/SetFps.cpp $(SRC_DIR)/adsd3500_util.cpp $(CRCS) + $(CXX) $^ -o $@ + mv $@ $(BIN_DIR) + +get_fps: $(SRC_DIR)/GetFps.cpp $(SRC_DIR)/adsd3500_util.cpp $(CRCS) + $(CXX) $^ -o $@ + mv $@ $(BIN_DIR) + +set_image_mode: $(SRC_DIR)/SetImageMode.cpp $(SRC_DIR)/adsd3500_util.cpp $(CRCS) + $(CXX) $^ -o $@ + mv $@ $(BIN_DIR) + +get_image_mode: $(SRC_DIR)/GetImageMode.cpp $(SRC_DIR)/adsd3500_util.cpp $(CRCS) + $(CXX) $^ -o $@ + mv $@ $(BIN_DIR) + +start_stream: $(SRC_DIR)/StartStream.cpp $(SRC_DIR)/adsd3500_util.cpp $(CRCS) + $(CXX) $^ -o $@ + mv $@ $(BIN_DIR) + +stop_stream: $(SRC_DIR)/StopStream.cpp $(SRC_DIR)/adsd3500_util.cpp $(CRCS) + $(CXX) $^ -o $@ + mv $@ $(BIN_DIR) + +receive_frames: $(SRC_DIR)/ReceiveFrames.cpp $(SRC_DIR)/adsd3500_util.cpp $(CRCS) + $(CXX) $^ -o $@ + mv $@ $(BIN_DIR) + +run_adsd3500: $(SRC_DIR)/run_adsd3500.cpp $(SRC_DIR)/adsd3500_util.cpp $(CRCS) + $(CXX) $^ -o $@ + mv $@ $(BIN_DIR) + +read_ccb: $(SRC_DIR)/ReadCCB.cpp $(SRC_DIR)/adsd3500_util.cpp $(CRCS) + $(CXX) $^ -o $@ + mv $@ $(BIN_DIR) + +clean: + mkdir -p $(BIN_DIR) + $(RM) -rf $(BIN_DIR)/* diff --git a/examples/adsd3500_sample_program/README.txt b/examples/adsd3500_sample_program/README.txt new file mode 100644 index 000000000..55da01a1d --- /dev/null +++ b/examples/adsd3500_sample_program/README.txt @@ -0,0 +1,41 @@ +/**************************************************************************** +* Copyright (c) 2023 - Analog Devices Inc. All Rights Reserved. +* This software is proprietary & confidential to Analog Devices, Inc. +* and its licensors. +* ***************************************************************************** +* ***************************************************************************** +* 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.*/ + +/* Available executable tools */ +1. set_fps - Sets the FPS of the ADSD3500 device. +2. get_dealias_and_intrinsic - Gets Dealias and Camera Intrinsic parameters from the imager. +3. stop_stream - Stops streaming in the ADSD3500 device. +4. update_fw - Updates ADSD3500 firmware. +5. nvm_write - Writes NVM image in the ToF camera module. + +/* Steps to create and run the executable that sets FPS value in the ADSD3500 device. */ + +1. In the terminal run the 'make' command to generate the executable. + $ make (for example, to build 'set_fps' executable, run the command: $ make set_fps) + +2. Copy the executable to Amba board. + +3. Run the executables in the board as shown below: + $ ./set_fps + $ ./get_dealias_and_intrinsic + $ ./stop_stream + $ ./update_fw + $ ./nvm_write + + + diff --git a/examples/adsd3500_sample_program/bin/reset_adsd3500 b/examples/adsd3500_sample_program/bin/reset_adsd3500 new file mode 100755 index 0000000000000000000000000000000000000000..90c050a569f97e304e90bbb67a1b7d5860cafe0e GIT binary patch literal 89088 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 I`}5`h2VSnxg8%>k literal 0 HcmV?d00001 diff --git a/examples/adsd3500_sample_program/bin/run_adsd3500 b/examples/adsd3500_sample_program/bin/run_adsd3500 new file mode 100755 index 0000000000000000000000000000000000000000..44ca6240b56fa46ac2484df18bde6c5d14cc0bcf GIT binary patch literal 89088 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