summaryrefslogtreecommitdiff
path: root/gr-comedi/src
diff options
context:
space:
mode:
Diffstat (limited to 'gr-comedi/src')
-rw-r--r--gr-comedi/src/CMakeLists.txt117
-rw-r--r--gr-comedi/src/comedi.i74
-rw-r--r--gr-comedi/src/comedi_sink_s.cc233
-rw-r--r--gr-comedi/src/comedi_sink_s.h89
-rw-r--r--gr-comedi/src/comedi_source_s.cc229
-rw-r--r--gr-comedi/src/comedi_source_s.h90
-rw-r--r--gr-comedi/src/gri_comedi.cc30
-rw-r--r--gr-comedi/src/gri_comedi.h28
-rwxr-xr-xgr-comedi/src/qa_comedi.py40
9 files changed, 930 insertions, 0 deletions
diff --git a/gr-comedi/src/CMakeLists.txt b/gr-comedi/src/CMakeLists.txt
new file mode 100644
index 000000000..1d9dac2c4
--- /dev/null
+++ b/gr-comedi/src/CMakeLists.txt
@@ -0,0 +1,117 @@
+# Copyright 2011 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+
+########################################################################
+# Setup the include and linker paths
+########################################################################
+include_directories(
+ ${GR_COMEDI_INCLUDE_DIRS}
+ ${GNURADIO_CORE_INCLUDE_DIRS}
+ ${GRUEL_INCLUDE_DIRS}
+ ${Boost_INCLUDE_DIRS}
+ ${COMEDI_INCLUDE_DIRS}
+)
+
+link_directories(
+ ${Boost_LIBRARY_DIRS}
+ ${COMEDI_LIBRARY_DIRS}
+)
+
+########################################################################
+# Setup library
+########################################################################
+list(APPEND gr_comedi_sources
+ comedi_sink_s.cc
+ comedi_source_s.cc
+ gri_comedi.cc
+)
+
+list(APPEND comedi_libs
+ gnuradio-core
+ ${Boost_LIBRARIES}
+ ${COMEDI_LIBRARIES}
+)
+
+add_library(gnuradio-comedi SHARED ${gr_comedi_sources})
+target_link_libraries(gnuradio-comedi ${comedi_libs})
+GR_LIBRARY_FOO(gnuradio-comedi RUNTIME_COMPONENT "comedi_runtime" DEVEL_COMPONENT "comedi_devel")
+
+########################################################################
+# Install public header files
+########################################################################
+install(FILES
+ comedi_sink_s.h
+ comedi_source_s.h
+ DESTINATION ${GR_INCLUDE_DIR}/gnuradio
+ COMPONENT "comedi_devel"
+)
+
+########################################################################
+# Setup swig generation
+########################################################################
+if(ENABLE_PYTHON)
+include(GrPython)
+include(GrSwig)
+
+set(GR_SWIG_INCLUDE_DIRS
+ ${GR_COMEDI_INCLUDE_DIRS}
+ ${GNURADIO_CORE_SWIG_INCLUDE_DIRS}
+ ${GRUEL_INCLUDE_DIRS}
+ ${Boost_INCLUDE_DIRS}
+)
+
+set(GR_SWIG_DOC_FILE ${CMAKE_CURRENT_BINARY_DIR}/comedi_swig_doc.i)
+set(GR_SWIG_DOC_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
+
+set(GR_SWIG_LIBRARIES gnuradio-comedi)
+
+GR_SWIG_MAKE(comedi comedi.i)
+
+GR_SWIG_INSTALL(
+ TARGETS comedi
+ DESTINATION ${GR_PYTHON_DIR}/gnuradio
+ COMPONENT "comedi_python"
+)
+
+install(
+ FILES comedi.i
+ ${CMAKE_CURRENT_BINARY_DIR}/comedi_swig_doc.i
+ DESTINATION ${GR_INCLUDE_DIR}/gnuradio/swig
+ COMPONENT "comedi_swig"
+)
+
+endif(ENABLE_PYTHON)
+
+########################################################################
+# Handle the unit tests
+########################################################################
+if(ENABLE_TESTING AND ENABLE_PYTHON)
+
+list(APPEND GR_TEST_PYTHON_DIRS
+ ${CMAKE_BINARY_DIR}/gr-comedi/src
+)
+list(APPEND GR_TEST_TARGET_DEPS gnuradio-comedi)
+
+include(GrTest)
+file(GLOB py_qa_test_files "qa_*.py")
+foreach(py_qa_test_file ${py_qa_test_files})
+ get_filename_component(py_qa_test_name ${py_qa_test_file} NAME_WE)
+ GR_ADD_TEST(${py_qa_test_name} ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} ${py_qa_test_file})
+endforeach(py_qa_test_file)
+endif(ENABLE_TESTING AND ENABLE_PYTHON)
diff --git a/gr-comedi/src/comedi.i b/gr-comedi/src/comedi.i
new file mode 100644
index 000000000..d3660580a
--- /dev/null
+++ b/gr-comedi/src/comedi.i
@@ -0,0 +1,74 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005,2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+%include "gnuradio.i" // the common stuff
+
+//load generated python docstrings
+%include "comedi_swig_doc.i"
+
+%{
+#include "comedi_sink_s.h"
+#include "comedi_source_s.h"
+%}
+
+// ----------------------------------------------------------------
+
+GR_SWIG_BLOCK_MAGIC(comedi,source_s)
+
+comedi_source_s_sptr
+comedi_make_source_s (int sampling_freq,
+ const std::string dev = ""
+ ) throw (std::runtime_error);
+
+class comedi_source_s : public gr_sync_block {
+
+ protected:
+ comedi_source_s (int sampling_freq,
+ const std::string device_name
+ ) throw (std::runtime_error);
+
+ public:
+ ~comedi_source_s ();
+
+ bool start();
+ bool stop();
+};
+
+// ----------------------------------------------------------------
+
+GR_SWIG_BLOCK_MAGIC(comedi,sink_s)
+
+comedi_sink_s_sptr
+comedi_make_sink_s (int sampling_freq,
+ const std::string dev = ""
+ ) throw (std::runtime_error);
+
+class comedi_sink_s : public gr_sync_block {
+
+ protected:
+ comedi_sink_s (int sampling_freq,
+ const std::string device_name
+ ) throw (std::runtime_error);
+
+ public:
+ ~comedi_sink_s ();
+};
diff --git a/gr-comedi/src/comedi_sink_s.cc b/gr-comedi/src/comedi_sink_s.cc
new file mode 100644
index 000000000..de1decfb6
--- /dev/null
+++ b/gr-comedi/src/comedi_sink_s.cc
@@ -0,0 +1,233 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005,2010 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/mman.h>
+
+#include <comedi_sink_s.h>
+#include <gr_io_signature.h>
+#include <stdio.h>
+#include <errno.h>
+#include <iostream>
+#include <stdexcept>
+#include <gri_comedi.h>
+
+
+/*
+ * comedi_sink_s is untested because I don't own appropriate hardware.
+ * Feedback is welcome! --SF
+ */
+
+static std::string
+default_device_name ()
+{
+ return "/dev/comedi0";
+}
+
+
+// ----------------------------------------------------------------
+
+comedi_sink_s_sptr
+comedi_make_sink_s (int sampling_freq, const std::string dev)
+{
+ return gnuradio::get_initial_sptr(new comedi_sink_s (sampling_freq, dev));
+}
+
+comedi_sink_s::comedi_sink_s (int sampling_freq,
+ const std::string device_name)
+ : gr_sync_block ("comedi_sink_s",
+ gr_make_io_signature (0, 0, 0),
+ gr_make_io_signature (0, 0, 0)),
+ d_sampling_freq (sampling_freq),
+ d_device_name (device_name.empty() ? default_device_name() : device_name),
+ d_dev (0),
+ d_subdevice (COMEDI_SUBD_AO),
+ d_n_chan (1), // number of input channels
+ d_map (0),
+ d_buffer_size (0),
+ d_buf_front (0),
+ d_buf_back (0)
+{
+ int aref=AREF_GROUND;
+ int range=0;
+
+ d_dev = comedi_open(d_device_name.c_str());
+ if (d_dev == 0){
+ comedi_perror(d_device_name.c_str());
+ throw std::runtime_error ("comedi_sink_s");
+ }
+
+ unsigned int chanlist[256];
+
+ for(int i=0; i<d_n_chan; i++){
+ chanlist[i]=CR_PACK(i,range,aref);
+ }
+
+ comedi_cmd cmd;
+ int ret;
+
+ ret = comedi_get_cmd_generic_timed(d_dev,d_subdevice,&cmd,d_n_chan,(unsigned int)(1e9/sampling_freq));
+ if(ret<0)
+ bail ("comedi_get_cmd_generic_timed", comedi_errno());
+
+ // TODO: check period_ns is not to far off sampling_freq
+
+ d_buffer_size = comedi_get_buffer_size(d_dev, d_subdevice);
+ if (d_buffer_size <= 0)
+ bail ("comedi_get_buffer_size", comedi_errno());
+
+ d_map = mmap(NULL,d_buffer_size,PROT_WRITE,MAP_SHARED,comedi_fileno(d_dev),0);
+ if (d_map == MAP_FAILED)
+ bail ("mmap", errno);
+
+ cmd.chanlist = chanlist;
+ cmd.chanlist_len = d_n_chan;
+ cmd.scan_end_arg = d_n_chan;
+
+ cmd.stop_src=TRIG_NONE;
+ cmd.stop_arg=0;
+
+ /* comedi_command_test() tests a command to see if the
+ * trigger sources and arguments are valid for the subdevice.
+ * If a trigger source is invalid, it will be logically ANDed
+ * with valid values (trigger sources are actually bitmasks),
+ * which may or may not result in a valid trigger source.
+ * If an argument is invalid, it will be adjusted to the
+ * nearest valid value. In this way, for many commands, you
+ * can test it multiple times until it passes. Typically,
+ * if you can't get a valid command in two tests, the original
+ * command wasn't specified very well. */
+ ret = comedi_command_test(d_dev,&cmd);
+
+ if(ret<0)
+ bail ("comedi_command_test", comedi_errno());
+
+ ret = comedi_command_test(d_dev,&cmd);
+
+ if(ret<0)
+ bail ("comedi_command_test", comedi_errno());
+
+ /* start the command */
+ ret = comedi_command(d_dev,&cmd);
+
+ if(ret<0)
+ bail ("comedi_command", comedi_errno());
+
+ set_output_multiple (d_n_chan*sizeof(sampl_t));
+
+ assert(sizeof(sampl_t) == sizeof(short));
+ set_output_signature (gr_make_io_signature (1, 1, sizeof (sampl_t)));
+}
+
+bool
+comedi_sink_s::check_topology (int ninputs, int noutputs)
+{
+ if (ninputs > d_n_chan)
+ throw std::runtime_error ("comedi_sink_s");
+
+ return true;
+}
+
+comedi_sink_s::~comedi_sink_s ()
+{
+ if (d_map) {
+ munmap(d_map, d_buffer_size);
+ d_map = 0;
+ }
+
+ comedi_close(d_dev);
+}
+
+int
+comedi_sink_s::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ int ret;
+
+ int work_left = noutput_items * sizeof(sampl_t) * d_n_chan;
+ sampl_t *pbuf = (sampl_t*)d_map;
+
+ do {
+
+ do {
+ ret = comedi_get_buffer_contents(d_dev,d_subdevice);
+ if (ret < 0)
+ bail ("comedi_get_buffer_contents", comedi_errno());
+
+ assert(ret % sizeof(sampl_t) == 0);
+ assert(work_left % sizeof(sampl_t) == 0);
+
+ ret = std::min(ret, work_left);
+ d_buf_front += ret;
+
+ assert(d_buffer_size%d_n_chan == 0);
+ if (d_buf_front-d_buf_back > (unsigned)d_buffer_size) {
+ d_buf_front+=d_buffer_size;
+ d_buf_back +=d_buffer_size;
+ }
+
+ if(d_buf_front==d_buf_back){
+ usleep(1000000*std::min(work_left,d_buffer_size/2)/(d_sampling_freq*sizeof(sampl_t)*d_n_chan));
+ continue;
+ }
+ } while (d_buf_front==d_buf_back);
+
+ for(unsigned i=d_buf_back/sizeof(sampl_t);i<d_buf_front/sizeof(sampl_t);i++){
+ int chan = i%d_n_chan;
+ int i_idx = noutput_items-work_left/d_n_chan/sizeof(sampl_t)+(i-d_buf_back/sizeof(sampl_t))/d_n_chan;
+
+ pbuf[i%(d_buffer_size/sizeof(sampl_t))] = input_items[chan]==0 ? 0 :
+ (int)((short*)(input_items[chan]))[i_idx] + 32767;
+ }
+
+ // FIXME: how to tell comedi the buffer is *written* ?
+ ret = comedi_mark_buffer_read(d_dev,d_subdevice,d_buf_front-d_buf_back);
+ if(ret<0)
+ bail ("comedi_mark_buffer_read", comedi_errno());
+
+ work_left -= d_buf_front-d_buf_back;
+
+ d_buf_back = d_buf_front;
+
+ } while(work_left>0);
+
+ return noutput_items;
+}
+
+
+void
+comedi_sink_s::output_error_msg (const char *msg, int err)
+{
+ fprintf (stderr, "comedi_sink_s[%s]: %s: %s\n",
+ d_device_name.c_str(), msg, comedi_strerror(err));
+}
+
+void
+comedi_sink_s::bail (const char *msg, int err) throw (std::runtime_error)
+{
+ output_error_msg (msg, err);
+ throw std::runtime_error ("comedi_sink_s");
+}
diff --git a/gr-comedi/src/comedi_sink_s.h b/gr-comedi/src/comedi_sink_s.h
new file mode 100644
index 000000000..94d39710c
--- /dev/null
+++ b/gr-comedi/src/comedi_sink_s.h
@@ -0,0 +1,89 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef INCLUDED_COMEDI_SINK_H
+#define INCLUDED_COMEDI_SINK_H
+
+#include <gr_sync_block.h>
+#include <string>
+#include <comedilib.h>
+#include <stdexcept>
+
+class comedi_sink_s;
+typedef boost::shared_ptr<comedi_sink_s> comedi_sink_s_sptr;
+
+/*!
+ * \brief make a COMEDI sink.
+ *
+ * \param sampling_freq sampling rate in Hz
+ * \param dev COMEDI device name, e.g., "/dev/comedi0"
+ */
+comedi_sink_s_sptr
+comedi_make_sink_s (int sampling_freq,
+ const std::string dev = "/dev/comedi0");
+
+/*!
+ * \brief sink using COMEDI
+ *
+ * The sink has one input stream of signed short integers.
+ *
+ * Input samples must be in the range [-32768,32767].
+ */
+class comedi_sink_s : public gr_sync_block {
+ friend comedi_sink_s_sptr
+ comedi_make_sink_s (int sampling_freq, const std::string dev);
+
+ // typedef for pointer to class work method
+ typedef int (comedi_sink_s::*work_t)(int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+
+ unsigned int d_sampling_freq;
+ std::string d_device_name;
+
+ comedi_t *d_dev;
+ int d_subdevice;
+ int d_n_chan;
+ void *d_map;
+ int d_buffer_size;
+ unsigned d_buf_front;
+ unsigned d_buf_back;
+
+ // random stats
+ int d_nunderuns; // count of underruns
+
+ void output_error_msg (const char *msg, int err);
+ void bail (const char *msg, int err) throw (std::runtime_error);
+
+ protected:
+ comedi_sink_s (int sampling_freq, const std::string device_name);
+
+ public:
+ ~comedi_sink_s ();
+
+ bool check_topology (int ninputs, int noutputs);
+
+ int work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+};
+
+#endif /* INCLUDED_COMEDI_SINK_H */
diff --git a/gr-comedi/src/comedi_source_s.cc b/gr-comedi/src/comedi_source_s.cc
new file mode 100644
index 000000000..517f1440b
--- /dev/null
+++ b/gr-comedi/src/comedi_source_s.cc
@@ -0,0 +1,229 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005,2010 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/mman.h>
+
+#include <comedi_source_s.h>
+#include <gr_io_signature.h>
+#include <stdio.h>
+#include <errno.h>
+#include <iostream>
+#include <stdexcept>
+#include <gri_comedi.h>
+
+
+// FIXME these should query some kind of user preference
+
+
+static std::string
+default_device_name ()
+{
+ return "/dev/comedi0";
+}
+
+// ----------------------------------------------------------------
+
+comedi_source_s_sptr
+comedi_make_source_s (int sampling_freq, const std::string dev)
+{
+ return gnuradio::get_initial_sptr(new comedi_source_s (sampling_freq, dev));
+}
+
+comedi_source_s::comedi_source_s (int sampling_freq,
+ const std::string device_name)
+ : gr_sync_block ("comedi_source_s",
+ gr_make_io_signature (0, 0, 0),
+ gr_make_io_signature (0, 0, 0)),
+ d_sampling_freq (sampling_freq),
+ d_device_name (device_name.empty() ? default_device_name() : device_name),
+ d_dev (0),
+ d_subdevice (0/*COMEDI_SUBD_AI*/),
+ d_n_chan (1), // number of input channels
+ d_map (0),
+ d_buffer_size (0),
+ d_buf_front (0),
+ d_buf_back (0)
+{
+ int aref=AREF_GROUND;
+ int range=0;
+
+ d_dev = comedi_open(d_device_name.c_str());
+ if (d_dev == 0){
+ comedi_perror(d_device_name.c_str());
+ throw std::runtime_error ("comedi_source_s");
+ }
+
+ unsigned int chanlist[256];
+
+ for(int i=0; i<d_n_chan; i++){
+ chanlist[i]=CR_PACK(i,range,aref);
+ }
+
+ comedi_cmd cmd;
+ int ret;
+
+ ret = comedi_get_cmd_generic_timed(d_dev,d_subdevice,&cmd,d_n_chan,(unsigned int)(1e9/sampling_freq));
+ if(ret<0)
+ bail ("comedi_get_cmd_generic_timed", comedi_errno());
+
+ // TODO: check period_ns is not to far off sampling_freq
+
+ d_buffer_size = comedi_get_buffer_size(d_dev, d_subdevice);
+ if (d_buffer_size <= 0)
+ bail ("comedi_get_buffer_size", comedi_errno());
+
+ d_map = mmap(NULL,d_buffer_size,PROT_READ,MAP_SHARED,comedi_fileno(d_dev),0);
+ if (d_map == MAP_FAILED)
+ bail ("mmap", errno);
+
+ cmd.chanlist = chanlist;
+ cmd.chanlist_len = d_n_chan;
+ cmd.scan_end_arg = d_n_chan;
+
+ cmd.stop_src=TRIG_NONE;
+ cmd.stop_arg=0;
+
+ /* comedi_command_test() tests a command to see if the
+ * trigger sources and arguments are valid for the subdevice.
+ * If a trigger source is invalid, it will be logically ANDed
+ * with valid values (trigger sources are actually bitmasks),
+ * which may or may not result in a valid trigger source.
+ * If an argument is invalid, it will be adjusted to the
+ * nearest valid value. In this way, for many commands, you
+ * can test it multiple times until it passes. Typically,
+ * if you can't get a valid command in two tests, the original
+ * command wasn't specified very well. */
+ ret = comedi_command_test(d_dev,&cmd);
+
+ if(ret<0)
+ bail ("comedi_command_test", comedi_errno());
+
+ ret = comedi_command_test(d_dev,&cmd);
+
+ if(ret<0)
+ bail ("comedi_command_test", comedi_errno());
+
+ /* start the command */
+ ret = comedi_command(d_dev,&cmd);
+
+ if(ret<0)
+ bail ("comedi_command", comedi_errno());
+
+ set_output_multiple (d_n_chan*sizeof(sampl_t));
+
+ assert(sizeof(sampl_t) == sizeof(short));
+ set_output_signature (gr_make_io_signature (1, 1, sizeof (sampl_t)));
+}
+
+bool
+comedi_source_s::check_topology (int ninputs, int noutputs)
+{
+ if (noutputs > d_n_chan)
+ throw std::runtime_error ("comedi_source_s");
+
+ return true;
+}
+
+comedi_source_s::~comedi_source_s ()
+{
+ if (d_map) {
+ munmap(d_map, d_buffer_size);
+ d_map = 0;
+ }
+
+ comedi_close(d_dev);
+}
+
+int
+comedi_source_s::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ int ret;
+
+ int work_left = noutput_items * sizeof(sampl_t) * d_n_chan;
+ sampl_t *pbuf = (sampl_t*)d_map;
+
+ do {
+
+ do {
+ ret = comedi_get_buffer_contents(d_dev,d_subdevice);
+ if (ret < 0)
+ bail ("comedi_get_buffer_contents", comedi_errno());
+
+ assert(ret % sizeof(sampl_t) == 0);
+ assert(work_left % sizeof(sampl_t) == 0);
+
+ ret = std::min(ret, work_left);
+ d_buf_front += ret;
+
+ assert(d_buffer_size%d_n_chan == 0);
+ if (d_buf_front-d_buf_back > (unsigned)d_buffer_size) {
+ d_buf_front+=d_buffer_size;
+ d_buf_back +=d_buffer_size;
+ }
+
+ if(d_buf_front==d_buf_back){
+ usleep(1000000*std::min(work_left,d_buffer_size/2)/(d_sampling_freq*sizeof(sampl_t)*d_n_chan));
+ continue;
+ }
+ } while (d_buf_front==d_buf_back);
+
+ for(unsigned i=d_buf_back/sizeof(sampl_t);i<d_buf_front/sizeof(sampl_t);i++){
+ int chan = i%d_n_chan;
+ int o_idx = noutput_items-work_left/d_n_chan/sizeof(sampl_t)+(i-d_buf_back/sizeof(sampl_t))/d_n_chan;
+
+ if (output_items[chan])
+ ((short*)(output_items[chan]))[o_idx] =
+ (int)pbuf[i%(d_buffer_size/sizeof(sampl_t))] - 32767;
+ }
+
+ ret = comedi_mark_buffer_read(d_dev,d_subdevice,d_buf_front-d_buf_back);
+ if(ret<0)
+ bail ("comedi_mark_buffer_read", comedi_errno());
+
+ work_left -= d_buf_front-d_buf_back;
+
+ d_buf_back = d_buf_front;
+
+ } while(work_left>0);
+
+ return noutput_items;
+}
+
+void
+comedi_source_s::output_error_msg (const char *msg, int err)
+{
+ fprintf (stderr, "comedi_source_s[%s]: %s: %s\n",
+ d_device_name.c_str(), msg, comedi_strerror(err));
+}
+
+void
+comedi_source_s::bail (const char *msg, int err) throw (std::runtime_error)
+{
+ output_error_msg (msg, err);
+ throw std::runtime_error ("comedi_source_s");
+}
diff --git a/gr-comedi/src/comedi_source_s.h b/gr-comedi/src/comedi_source_s.h
new file mode 100644
index 000000000..b9fde4c1e
--- /dev/null
+++ b/gr-comedi/src/comedi_source_s.h
@@ -0,0 +1,90 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef INCLUDED_COMEDI_SOURCE_S_H
+#define INCLUDED_COMEDI_SOURCE_S_H
+
+#include <gr_sync_block.h>
+#include <string>
+#include <comedilib.h>
+#include <stdexcept>
+
+class comedi_source_s;
+typedef boost::shared_ptr<comedi_source_s> comedi_source_s_sptr;
+
+/*!
+ * \brief make a COMEDI source.
+ *
+ * \param sampling_freq sampling rate in Hz
+ * \param dev COMEDI device name, e.g., "/dev/comedi0"
+ */
+comedi_source_s_sptr
+comedi_make_source_s (int sampling_freq,
+ const std::string dev = "/dev/comedi0");
+
+/*!
+ * \brief source using COMEDI
+ *
+ * The source has one to many input stream of signed short integers.
+ *
+ * Output samples will be in the range [-32768,32767].
+ */
+class comedi_source_s : public gr_sync_block {
+ friend comedi_source_s_sptr
+ comedi_make_source_s (int sampling_freq, const std::string dev);
+
+ // typedef for pointer to class work method
+ typedef int (comedi_source_s::*work_t)(int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+
+ unsigned int d_sampling_freq;
+ std::string d_device_name;
+
+ comedi_t *d_dev;
+ int d_subdevice;
+ int d_n_chan;
+ void *d_map;
+ int d_buffer_size;
+ unsigned d_buf_front;
+ unsigned d_buf_back;
+
+ // random stats
+ int d_noverruns; // count of overruns
+
+ void output_error_msg (const char *msg, int err);
+ void bail (const char *msg, int err) throw (std::runtime_error);
+
+
+ protected:
+ comedi_source_s (int sampling_freq, const std::string device_name);
+
+ public:
+ ~comedi_source_s ();
+
+ bool check_topology (int ninputs, int noutputs);
+
+ int work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+};
+
+#endif /* INCLUDED_COMEDI_SOURCE_S_H */
diff --git a/gr-comedi/src/gri_comedi.cc b/gr-comedi/src/gri_comedi.cc
new file mode 100644
index 000000000..97b3ccd3e
--- /dev/null
+++ b/gr-comedi/src/gri_comedi.cc
@@ -0,0 +1,30 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gri_comedi.h>
+#include <algorithm>
+
+
diff --git a/gr-comedi/src/gri_comedi.h b/gr-comedi/src/gri_comedi.h
new file mode 100644
index 000000000..571a41180
--- /dev/null
+++ b/gr-comedi/src/gri_comedi.h
@@ -0,0 +1,28 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INCLUDED_GRI_COMEDI_H
+#define INCLUDED_GRI_COMEDI_H
+
+#include <stdio.h>
+
+#endif /* INCLUDED_GRI_COMEDI_H */
diff --git a/gr-comedi/src/qa_comedi.py b/gr-comedi/src/qa_comedi.py
new file mode 100755
index 000000000..a407ab62b
--- /dev/null
+++ b/gr-comedi/src/qa_comedi.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+#
+# Copyright 2005,2007 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import comedi
+
+class qa_comedi (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.tb = gr.top_block ()
+
+ def tearDown (self):
+ self.tb = None
+
+ def test_000_nop (self):
+ """Just see if we can import the module...
+ They may not have COMEDI library, etc. Don't try to run anything"""
+ pass
+
+if __name__ == '__main__':
+ gr_unittest.main ()