summaryrefslogtreecommitdiff
path: root/gr-comedi
diff options
context:
space:
mode:
authorjcorgan <jcorgan@221aa14e-8319-0410-a670-987f0aec2ac5>2006-08-03 04:51:51 +0000
committerjcorgan <jcorgan@221aa14e-8319-0410-a670-987f0aec2ac5>2006-08-03 04:51:51 +0000
commit5d69a524f81f234b3fbc41d49ba18d6f6886baba (patch)
treeb71312bf7f1e8d10fef0f3ac6f28784065e73e72 /gr-comedi
Houston, we have a trunk.
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@3122 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'gr-comedi')
-rw-r--r--gr-comedi/AUTHORS1
-rw-r--r--gr-comedi/ChangeLog25
-rw-r--r--gr-comedi/Makefile.am24
-rw-r--r--gr-comedi/src/Makefile.am91
-rw-r--r--gr-comedi/src/comedi.i76
-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
-rw-r--r--gr-comedi/src/run_tests.in47
13 files changed, 1003 insertions, 0 deletions
diff --git a/gr-comedi/AUTHORS b/gr-comedi/AUTHORS
new file mode 100644
index 0000000000..624606f051
--- /dev/null
+++ b/gr-comedi/AUTHORS
@@ -0,0 +1 @@
+Stephane Fillod <f8cfe@free.fr>
diff --git a/gr-comedi/ChangeLog b/gr-comedi/ChangeLog
new file mode 100644
index 0000000000..92ee15bc48
--- /dev/null
+++ b/gr-comedi/ChangeLog
@@ -0,0 +1,25 @@
+2005-10-10 Stephane Fillod <f8cfe@free.fr>
+
+ * src/comedi_source.{h,cc}: new.
+ * src/comedi_sink.{h,cc}: new.
+
+#
+# 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 2, 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., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
diff --git a/gr-comedi/Makefile.am b/gr-comedi/Makefile.am
new file mode 100644
index 0000000000..0b0234e364
--- /dev/null
+++ b/gr-comedi/Makefile.am
@@ -0,0 +1,24 @@
+#
+# 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 2, 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., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+SUBDIRS = src
diff --git a/gr-comedi/src/Makefile.am b/gr-comedi/src/Makefile.am
new file mode 100644
index 0000000000..3311cd569e
--- /dev/null
+++ b/gr-comedi/src/Makefile.am
@@ -0,0 +1,91 @@
+#
+# 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 2, 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., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+# Install this stuff so that it ends up as the gnuradio.comedi module
+# This usually ends up at:
+# ${prefix}/lib/python${python_version}/site-packages/gnuradio
+
+ourpythondir = $(grpythondir)
+ourlibdir = $(grpyexecdir)
+
+EXTRA_DIST = run_tests.in
+
+TESTS = run_tests
+
+LOCAL_IFILES = \
+ comedi.i
+
+NON_LOCAL_IFILES = \
+ $(top_srcdir)/gnuradio-core/src/lib/swig/gnuradio.i
+
+ALL_IFILES = \
+ $(LOCAL_IFILES) \
+ $(NON_LOCAL_IFILES)
+
+BUILT_SOURCES = \
+ comedi.cc \
+ comedi.py
+
+ourpython_PYTHON = \
+ comedi.py
+
+INCLUDES = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS)
+
+SWIGCPPPYTHONARGS = -c++ -python $(PYTHON_CPPFLAGS) $(STD_DEFINES_AND_INCLUDES)
+# -I$(swigincludedir) $(S-I$(grincludedir) -I$(comediincludedir)
+
+ourlib_LTLIBRARIES = _comedi.la
+
+_comedi_la_SOURCES = \
+ comedi.cc \
+ comedi_sink_s.cc \
+ comedi_source_s.cc \
+ gri_comedi.cc
+
+grinclude_HEADERS = \
+ comedi_sink_s.h \
+ comedi_source_s.h
+
+noinst_HEADERS = \
+ gri_comedi.h
+
+
+swiginclude_HEADERS = \
+ $(LOCAL_IFILES)
+
+_comedi_la_LIBADD = \
+ $(PYTHON_LDFLAGS) \
+ $(GNURADIO_CORE_LIBS) \
+ -lstdc++
+
+_comedi_la_LDFLAGS = $(NO_UNDEFINED) -module -avoid-version
+
+comedi.cc comedi.py: comedi.i
+ $(SWIG) $(SWIGCPPPYTHONARGS) -module comedi -o comedi.cc $<
+
+
+noinst_PYTHON = \
+ qa_comedi.py
+
+MOSTLYCLEANFILES = \
+ $(BUILT_SOURCES) *~ *.pyc
diff --git a/gr-comedi/src/comedi.i b/gr-comedi/src/comedi.i
new file mode 100644
index 0000000000..18f4f441fe
--- /dev/null
+++ b/gr-comedi/src/comedi.i
@@ -0,0 +1,76 @@
+/* -*- 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 2, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+%feature("autodoc","1");
+
+%include "exception.i"
+%import "gnuradio.i" // the common stuff
+
+%{
+#include "gnuradio_swig_bug_workaround.h" // mandatory bug fix
+#include "comedi_sink_s.h"
+#include "comedi_source_s.h"
+#include <stdexcept>
+%}
+
+// ----------------------------------------------------------------
+
+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 0000000000..2c1ffb7de9
--- /dev/null
+++ b/gr-comedi/src/comedi_sink_s.cc
@@ -0,0 +1,233 @@
+/* -*- 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 2, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, 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 comedi_sink_s_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,(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 0000000000..08fce25158
--- /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 2, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, 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 an 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 device_name);
+
+ // 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 0000000000..5041c9c30a
--- /dev/null
+++ b/gr-comedi/src/comedi_source_s.cc
@@ -0,0 +1,229 @@
+/* -*- 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 2, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, 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 comedi_source_s_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,(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 0000000000..e759c43a35
--- /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 2, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, 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 device_name);
+
+ // 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 0000000000..e456262938
--- /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 2, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, 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 0000000000..5e1f00e933
--- /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 2, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, 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 0000000000..98b2f91892
--- /dev/null
+++ b/gr-comedi/src/qa_comedi.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+#
+# 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 2, 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., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from gnuradio import gr, gr_unittest
+import comedi
+
+class qa_comedi (gr_unittest.TestCase):
+
+ def setUp (self):
+ self.fg = gr.flow_graph ()
+
+ def tearDown (self):
+ self.fg = 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 ()
diff --git a/gr-comedi/src/run_tests.in b/gr-comedi/src/run_tests.in
new file mode 100644
index 0000000000..6c0ba9d18c
--- /dev/null
+++ b/gr-comedi/src/run_tests.in
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+# All this strange PYTHONPATH manipulation is required to run our
+# tests using our just built shared library and swig-generated python
+# code prior to installation.
+
+# build tree == src tree unless you're doing a VPATH build.
+# If you don't know what a VPATH build is, you're not doing one. Relax...
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+# Where to look in the build tree for our shared library
+libbld=@abs_top_builddir@/gr-comedi/src
+# Where to look in the src tree for swig generated python code
+libsrc=@abs_top_srcdir@/gr-comedi/src
+# Where to look in the src tree for hand written python code
+py=@abs_top_srcdir@/gr-comedi/src
+
+# Where to look for GNU Radio python modules in current build tree
+# FIXME this is wrong on a distcheck. We really need to ask gnuradio-core
+# where it put its python files.
+grpythonbld=@abs_top_builddir@/gnuradio-core/src/python/:@abs_top_builddir@/gnuradio-core/src/lib/swig/:@abs_top_builddir@/gnuradio-core/src/lib/swig/.libs
+
+PYTHONPATH="$grpythonbld:$libbld:$libbld/.libs:$libsrc:$py:$PYTHONPATH"
+export PYTHONPATH
+
+#
+# This is the simple part...
+# Run everything that matches qa_*.py and return the final result.
+#
+
+ok=yes
+for file in @srcdir@/qa_*.py
+do
+ if ! $file
+ then
+ ok=no
+ fi
+done
+
+if [ $ok = yes ]
+then
+ exit 0
+else
+ exit 1
+fi