linux-comedi / comedilib Goto Github PK
View Code? Open in Web Editor NEWComedilib (libcomedi) library
License: GNU Lesser General Public License v2.1
Comedilib (libcomedi) library
License: GNU Lesser General Public License v2.1
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:
/dev/comedi0
with 2.5V amplitude and 10s periodsudo modprobe comedi comedi_num_legacy_minors=1
sudo modprobe comedi_test amplitude=2500000 period=10000000
sudo comedi_config /dev/comedi0 comedi_test
xoscope
.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).
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:
#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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.