Code Monkey home page Code Monkey logo

comedilib's People

Contributors

andersblomdell avatar dschleef avatar foleyj2 avatar ian-abbott avatar olsonse avatar pieleric avatar stv0g avatar tousley-ni avatar wking avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

comedilib's Issues

Can't do synchronous read from `comedi_test` device

I'm trying to set up a testing environment for a data acquisition system. I had hoped to use a comedi_test device to simulate analog input which is then piped into a custom UI. I'm targeting slow varying instruments (i.e. temperature, pressure), hence my approach using synchronous, rather than asynch.

System specs:
Ubuntu 20.04
libcomedi0 v(0.11.0-1.1build2) - installed from apt
libcomedi-dev v(0.11.0-1.1build2) - installed from apt

To recreate the problem:

  • Setup my comedi_test device on /dev/comedi0 with 2.5V amplitude and 10s period
sudo modprobe comedi comedi_num_legacy_minors=1
sudo modprobe comedi_test amplitude=2500000 period=10000000
sudo comedi_config /dev/comedi0 comedi_test
  • Verify that the test device is working using xoscope.
  • Run my comedi_read test program based on the "first demo program" in the comedi.org documentation, source below.

comedi_read.c:

#include <stdio.h>
#include <comedilib.h>

int subdev = 0;
int chan = 0;
int range = 0;
int aref = AREF_GROUND;

int main(int argc, char *argv[]) {
    comedi_t *it;
    int chan = 0;
    lsampl_t data;
    int retval;

    it = comedi_open("/dev/comedi0");
    if(it == NULL) {
        comedi_perror("comedi_open");
        return 1;
    }

    for (int i=0; i<10; i++) {
        retval = comedi_data_read(it, subdev, chan, range, aref, &data);
        if(retval < 0) {
            comedi_perror("comedi_data_read");
            return 1;
        }
        printf("%03d (%d): %d\n", i, retval, data);
        sleep(1);
    }

    return 0;
}

The resulting output is:

000 (1): 32767
001 (1): 32767
002 (1): 32767
003 (1): 32767
004 (1): 32767
005 (1): 32767
006 (1): 32767
007 (1): 32767
008 (1): 32767
009 (1): 32767

Does the comedi_test device support the comedi_data_read instruction? or am I doing something else wrong?

When I do an acquisition in asynch mode, or viewing in xoscope I'm able to see the expected output (values changing).

Important latency spikes during asynchronous reads

In my current application I am using comedi along with an NI PCIe-6259 card to perform measurements on a force/torque sensor.

The sensor has a bandwidth of 10kHz so I sample it at 25kHz in order to be safe from aliasing.
I configure an asynchronous acquisition to sample all 6 channels and buffer a small amount of samples to retrieve them at a lower frequency (typically 1kHz).

When I do that, most of my reads are very fast (~1µs) but sometimes I get quite important latencies (~14ms) to perform a single buffer acquisition. I observed that this is happening when not all the data is available during the first call to read and that a second one is necessary to get the remaining bytes. The lag occurs on this second read.

I don't know if this is due to misconfiguration, the hardware or a bug somewhere.

I extracted the bulk of my code into this example:

Code highlighting the issue
#include <comedilib.h>

#include <iostream>
#include <vector>
#include <chrono>
#include <cstring>
#include <unistd.h>
#include <signal.h>

uint32_t getMaximumSamplingFrequency(comedi_t* dev, int sub_dev);

bool stop{};

void sigint_handler(int) {
    stop = true;
}

int main() {
    const auto dev = comedi_open("/dev/comedi0");
    const auto sub_dev = 0;

    if (comedi_get_n_subdevices(dev) <= sub_dev) {
        std::cerr << "subdevice " << sub_dev << " does not exist" << std::endl;
        return 1;
    }

    int ret = comedi_get_subdevice_flags(dev, sub_dev);
    if (ret < 0 || !(ret & SDF_CMD_READ)) {
        std::cerr << "subdevice " << sub_dev
                  << " does not support 'read' commands" << std::endl;
        return 2;
    }

    comedi_set_global_oor_behavior(COMEDI_OOR_NUMBER);

    std::vector<uint32_t> chanlist(6);
    for (uint32_t i = 0; i < 6; ++i) {
        chanlist[i] = CR_PACK(i, 0, AREF_DIFF);
    }

    // max bandwidth = 10k -> fs > 2*10k -> fs = 25k
    uint32_t sampling_freq = 25000;
    const uint32_t daq_sampling_freq =
        getMaximumSamplingFrequency(dev, sub_dev);
    const uint32_t max_sampling_freq = daq_sampling_freq / 6;
    if (sampling_freq > max_sampling_freq) {
        sampling_freq = max_sampling_freq;
        std::cout << "Lowering the per channel sampling frequency to "
                  << sampling_freq << " to be able to sample " << 6
                  << " channels at " << daq_sampling_freq << "Hz" << std::endl;
    }

    // number of scans to perform during one acquisition
    const uint32_t read_frequency = 1000;
    const auto scan_count = sampling_freq / read_frequency;

    comedi_cmd command;
    memset(&command, 0, sizeof(command));

    command.subdev = sub_dev;
    command.start_src = TRIG_NOW; // Start the acquisition when the command
                                  // is sent and after start_arg ns
    command.start_arg = 0;        // no delay
    command.scan_begin_src =
        TRIG_TIMER; // one acquisition every scan_begin_arg ns
    command.scan_begin_arg = 1'000'000'000 / sampling_freq;
    command.convert_src = TRIG_TIMER; // Sampling timer
    command.convert_arg =
        1'000'000'000 /
        daq_sampling_freq; // Sample all channels as fast as possible
    command.scan_end_src =
        TRIG_COUNT; // Stop the scan after scan_end_arg convertions
    command.scan_end_arg = 6;
    command.stop_src = TRIG_NONE; // Continuous acquisition, stopped by
                                  // a call to comedi_cancel
    command.stop_arg = scan_count;
    command.chanlist = chanlist.data(); // List of channels to sample
    command.chanlist_len = 6;           // Number of channels to sample

    const auto sample_count = scan_count * 6;
    const auto buffer_size = sample_count * sizeof(sampl_t);
    std::vector<char> reading_buffer(buffer_size);

    const std::string cmdtest_messages[] = {
        "success",          "invalid source",    "source conflict",
        "invalid argument", "argument conflict", "invalid chanlist",
    };

    ret = comedi_command_test(dev, &command);
    if (ret < 0) {
        comedi_perror("comedi_command_test");
        if (errno == EIO) {
            fprintf(stderr,
                    "Ummm... this subdevice doesn't support commands\n");
        }
        return 3;
    }
    ret = comedi_command_test(dev, &command);
    if (ret < 0) {
        comedi_perror("comedi_command_test");
        return 4;
    }
    if (ret != 0) {
        std::cerr << "Error preparing command, second test returned " << ret
                  << "(" << cmdtest_messages[ret] << ")" << std::endl;
        return 5;
    }

    comedi_set_read_subdevice(dev, sub_dev);
    ret = comedi_get_read_subdevice(dev);
    if (ret < 0 || ret != sub_dev) {
        std::cerr << "failed to change 'read' subdevice from " << ret << " to "
                  << sub_dev << std::endl;
        return 6;
    }

    comedi_cancel(dev, sub_dev);
    ret = comedi_command(dev, &command);
    if (ret < 0) {
        comedi_perror("comedi_command");
        return 7;
    } else {
        signal(SIGINT, sigint_handler);
        while (not stop) {
            std::cout << "Reading a new packet of data\n";
            auto buffer = reading_buffer.data();

            ssize_t bytes_read = 0;
            auto bytes_to_read = buffer_size;
            do {
                const auto before = std::chrono::steady_clock::now();

                ret = read(comedi_fileno(dev), buffer + bytes_read,
                           bytes_to_read);

                const auto after = std::chrono::steady_clock::now();

                std::cout
                    << "\tgot " << ret << " bytes in "
                    << std::chrono::duration_cast<std::chrono::microseconds>(
                           after - before)
                           .count()
                    << "µs\n";

                if (ret >= 0) {
                    bytes_read += ret;
                    bytes_to_read -= static_cast<size_t>(ret);
                }
            } while (ret >= 0 and bytes_to_read > 0);
        }
        signal(SIGINT, nullptr);
    }

    comedi_cancel(dev, sub_dev);
    comedi_close(dev);
}

uint32_t getMaximumSamplingFrequency(comedi_t* dev, int sub_dev) {
    comedi_cmd cmd;
    if (comedi_get_cmd_generic_timed(dev, sub_dev, &cmd, 6, 1) < 0) {
        throw std::runtime_error(
            "Cannot get the maximum sampling frequency of the DAQ");
    } else {
        return 1'000'000'000 / cmd.convert_arg;
    }
}

A typical run produces the following output:

Reading a new packet of data
        got 300 bytes in 14159µs
Reading a new packet of data
        got 300 bytes in 1µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 36 bytes in 0µs
        got 264 bytes in 13994µs
Reading a new packet of data
        got 300 bytes in 14µs
Reading a new packet of data
        got 300 bytes in 1µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 1µs
Reading a new packet of data
        got 300 bytes in 1µs
Reading a new packet of data
        got 300 bytes in 1µs
Reading a new packet of data
        got 300 bytes in 1µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 1µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 300 bytes in 1µs
Reading a new packet of data
        got 300 bytes in 0µs
Reading a new packet of data
        got 72 bytes in 0µs
        got 228 bytes in 13975µs                                               
...

We can see that the lag occurs every 14 acquisitions, which is too regular to be an OS issue.

Is there something wrong in my way of doing the acquisition or is there a real underlying problem?

For info, I run my tests on a Linux 5.9.1 RT kernel but I've seen the problem on another computer with a different card so I'm not sure if it's relevant.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.