PlusToolkit / ndicapi

A common C API for communicating with NDI Polaris and Aurora devices
MIT License
48 stars 32 forks source link

The quaternion value different from that obtained by the NDI official application. #28

Closed AustinYuAo closed 12 months ago

AustinYuAo commented 2 years ago

Thanks very much for your reply. After reading this example, I have written a test program. However, when I placed tool in a fixed position, I found a problem. The quaternion value of tool obtained by the test application was different from that obtained by the NDI official application. I took two photos, one is the result of NDI and the other is the result of test program. You can find that the quaternion values in the test program seem to be in a different order, comparing to the quaternion values in the official NDI application, and the x、y、z values are also different. test NDI

busyyang commented 12 months ago

Did you find any reason for this? I also suffer this issue by now.

lassoan commented 12 months ago

The position values are indeed different, which suggest that you use a different coordinate system. For example, NDI prints StylusTipToTracker, while you are displaying StylusToTracker position in your Plus test application.

There is no standard order for quaternion components, so a different component order may be be fine (as long as it is used consistently).

If you post a link to your Plus configuration and source code of your test application then we can tell why you see different values.

AustinYuAo commented 12 months ago

@busyyang In fact, I didn't solve this problem at that time; instead, I used the official NDI code. @lassoan Thank you very much for your response. At that time, based on the example from the link[https://github.com/PlusToolkit/ndicapi/blob/fb6213a4a40de44bee62785397f890b261e02a38/Applications/ndiBasicExample.cxx#L48] , I wrote the test code as follows.

// A simple program that connects to an NDI tracker

include

include

include "API.h"

include

if _MSC_VER >= 1700

include

include

include

endif

if _MSC_VER >= 1700

//---------------------------------------------------------------------------- bool ParallelProbe(ndicapi& outDevice, bool checkDSR) { const int MAX_SERIAL_PORT_NUMBER = 20; // the serial port is almost surely less than this number std::vector deviceExists(MAX_SERIAL_PORT_NUMBER); std::fill(begin(deviceExists), end(deviceExists), false); std::vector<std::future> tasks; std::mutex deviceNameMutex; for (int i = 0; i < MAX_SERIAL_PORT_NUMBER; i++) { std::future result = std::async([i, &deviceNameMutex, &deviceExists, checkDSR]() { std::string devName; { std::lock_guard guard(deviceNameMutex); devName = std::string(ndiSerialDeviceName(i)); } int errnum = ndiSerialProbe(devName.c_str(), checkDSR); if (errnum == NDI_OKAY) { deviceExists[i] = true; } }); tasks.push_back(std::move(result)); } for (int i = 0; i < MAX_SERIAL_PORT_NUMBER; i++) { tasks[i].wait(); } for (int i = 0; i < MAX_SERIAL_PORT_NUMBER; i++) { // use first device found if (deviceExists[i] == true) { char devicename = (char*)ndiSerialDeviceName(i); outDevice = ndiOpenSerial(devicename); return true; } }

return false;

}

endif

struct ndicapi;

int main() { bool checkDSR = false; ndicapi device(nullptr); const char name(nullptr);

if (2 > 1)
    name = "C:/Users/Au/Desktop/NDIPolaris/measuringTool.rom";
else
{

if _MSC_VER >= 1700

    ParallelProbe(device, checkDSR);
    /* ParallelProbe(device,argc > 1 ? argv[1]: 0, checkDSR);*/

else

    {
        const int MAX_SERIAL_PORTS = 20;
        for (int i = 0; i < MAX_SERIAL_PORTS; ++i)
        {
            name = ndiSerialDeviceName(i);
            int result = ndiSerialProbe(name, checkDSR);
            if (result == NDI_OKAY)
            {
                break;
            }
        }
    }

endif

}

if (name != nullptr)
{
    device = ndiOpenSerial(name);
}

if (device != nullptr)
{
    const char* reply = ndiCommand(device, "INIT:");
    if (strncmp(reply, "ERROR", strlen(reply)) == 0 || ndiGetError(device) != NDI_OKAY)
    {
        std::cerr << "Error when sending command: " << ndiErrorString(ndiGetError(device)) << std::endl;
        return EXIT_FAILURE;
    }

    reply = ndiCommand(device, "COMM:%d%03d%d", NDI_115200, NDI_8N1, NDI_NOHANDSHAKE);

    // Add your own commands here!!!
    LoadToolFromFile(0, "C:/Users/Au/Desktop/NDIPolaris/measuringTool.rom");

    std::cout << "Starting tracking..." << std::endl;
    StartTracking(true);

    std::cout << "Tracking..." << std::endl;
    QuaternionTransformStruct* toolTransform[2];

    while (1)
    {
        Sleep(250);

        if (UpdateTransforms())
        {
            toolTransform[0] = GetQuaternionTransform(0);
            if (toolTransform[0] != NULL && toolTransform[0]->status == 0)
            {
                printf("q0: %lf\nq1: %lf\nq2: %lf\nq3: %lf\nx: %lf\ny: %lf\nz: %lf\n\n", toolTransform[0]->q0,
                    toolTransform[0]->q1,
                    toolTransform[0]->q2,
                    toolTransform[0]->q3,
                    toolTransform[0]->x,
                    toolTransform[0]->y,
                    toolTransform[0]->z);
            }

            toolTransform[1] = GetQuaternionTransform(1);

            delete toolTransform[0];
            delete toolTransform[1];
        }
    }

    printf("Stoping tracking...\n");
    StopTracking();

    ndiCloseSerial(device);
}

return EXIT_SUCCESS;

}

I would greatly appreciate it if you could help me solve this problem.

busyyang commented 12 months ago

Hi @AustinYuAo, I have solved this problem, the reason is that I mixed the rom files in my test code and NDI official tool. :) When I use the same rom file, the result the same with NDI official tool. And I use the NDI Capture from NDI Toolbox 5.002.022 if you would like to know.

Actually, the NDI Vega is used in my project, so I modified the ndiBasicExample.cxx to apply the tcpip communication. Here is the code if it can help you:

// A simple program that connects to an NDI tracker
#include <ndicapi.h>
#include <cstring>
#include <vector>
#include <iostream>

void _check_for_ndi_errors(ndicapi* device, const char* command)
{
    int code = ndiGetError(device);
    if (code != NDI_OKAY)
    {
        ndiCloseNetwork(device);
        std::cout << "Error when " << command << " The Error was: " << ndiErrorString(code) << std::endl;
        exit(-2);
    }

}

int main()
{
    std::vector<const char*> sroms = {
        "data/8700339.rom"
    };

    std::vector<int> port_handles;

    const char* hostname = "169.254.190.4";
    int  port = 8765;
    ndicapi* device = ndiOpenNetwork(hostname, port); 
    if (!device)
    {
        std::cout << "No NDI Device fond!" << std::endl;
    }
    ndiCommand(device, "INIT:");
    _check_for_ndi_errors(device, "Sending INIT command");
    // free ports that are waiting to be freed
    ndiCommand(device, "PHSR:01");
    int number_of_tools = ndiGetPHSRNumberOfHandles(device);
    for (int i = 0; i < number_of_tools; i++)
    {
        int port_handle = ndiGetPHSRHandle(device, i);
        ndiCommand(device, "PHF:%02X", port_handle);
        _check_for_ndi_errors(device, "free port handle");
    }

    for (size_t i = 0; i < sroms.size(); i++)
    {
        ndiCommand(device, "PHRQ:*********1****");
        _check_for_ndi_errors(device, "PHRQ");
        int port_handle = ndiGetPHRQHandle(device);
        _check_for_ndi_errors(device, "ndiGetPHRQHandle");
        port_handles.push_back(port_handle);
        ndiPVWRFromFile(device, port_handle, const_cast<char*>(sroms[i]));
        _check_for_ndi_errors(device, "ndiPVWRFromFile");
    }
    ndiCommand(device, "PHSR:01");
    _check_for_ndi_errors(device, "PHSR:01");
    // init ports
    //ndiCommand(device, "PHSR:02");
    for (size_t i = 0; i < sroms.size(); i++)
    {
        ndiCommand(device, "PINIT:%02X", port_handles[i]);
        _check_for_ndi_errors(device, "PINIT");
        ndiCommand(device, "PENA:%02XD", port_handles[i]);
        _check_for_ndi_errors(device, "PENA");
    }

    // start tracking
    ndiCommand(device, "TSTART:");
    _check_for_ndi_errors(device, "start tracking");

    // get 100 transforms
    int x = 100;
    while (x--)
    {
        for (auto port_handle:port_handles)
        {
            ndiCommand(device, "TX:0801");
            _check_for_ndi_errors(device, "TX:0801");
            unsigned long frame_number = ndiGetTXFrame(device, port_handle);
            _check_for_ndi_errors(device, "ndiGetTXFrame");
            double transform[8] = { 0 };
            ndiGetTXTransform(device, port_handle, transform);
            _check_for_ndi_errors(device, "ndiGetTXTransform");
            printf("Transform: %.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f\n",
                transform[0], transform[1], transform[2], transform[3],
                transform[4], transform[5], transform[6], transform[7]);
        }

        Sleep(50);
    }
}

I fond the scikit-surgerynditracker used the ndicapi as backend. The code is very clear to use NDI device. If you are familar with python, this repo is your friend too.

lassoan commented 12 months ago

The huge advantage of Plus over scikit-surgerynditracker is that Plus can acquire data from multiple sources (e.g., tracking and imaging) in a single process, tightly synchronized, at high frame rate, utilizing several CPU cores.