From 5681c97b4e44a75d3cabe81b5704e9f50f750ac2 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Mon, 7 Jul 2014 16:43:39 +0000 Subject: [PATCH] Add experimental Python bindings This commit adds some experimental Python bindings. These likely need some further refinement in the building/deployment area, but should serve as a useful testbed for experimentation and interactive analysis of filters and the library's behaviour. --- python/.gitignore | 5 ++ python/README | 25 +++++++++ python/iir++.h | 1 + python/pyiir.i | 130 ++++++++++++++++++++++++++++++++++++++++++++++ python/setup.py | 16 ++++++ 5 files changed, 177 insertions(+) create mode 100644 python/.gitignore create mode 100644 python/README create mode 120000 python/iir++.h create mode 100644 python/pyiir.i create mode 100644 python/setup.py diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 0000000..7c28a64 --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,5 @@ +.ipynb_checkpoints/ +__pycache__/ +build/ +iir.py +pyiir_wrap.cxx diff --git a/python/README b/python/README new file mode 100644 index 0000000..0b933c6 --- /dev/null +++ b/python/README @@ -0,0 +1,25 @@ +Experimental Python bindings +============================ + +These bindings are currently experimental, as it is not yet clear how best +to create and maintain them. They are built upon the C++ interface as the +object oriented nature of it sits much better with Python. + +At present, it is necessary to compile and install the C++ library before +working with the bindings. + +To generate the bindings, swig 3.0.2 has been used, but earlier versions +may work too: + + swig -c++ -python -py3 pyiir.i + +To build the extension, distutils is used: + + python3 setup.py build_ext + +Note that the -L ${LIBDIR} option may be handy if libiir++ is not installed +into the system library path. + +To install the extension: + + python3 setup.py install diff --git a/python/iir++.h b/python/iir++.h new file mode 120000 index 0000000..85f0bef --- /dev/null +++ b/python/iir++.h @@ -0,0 +1 @@ +../obj/iir++.h \ No newline at end of file diff --git a/python/pyiir.i b/python/pyiir.i new file mode 100644 index 0000000..fbf2dd6 --- /dev/null +++ b/python/pyiir.i @@ -0,0 +1,130 @@ +%module iir +%include + +%{ +#include "iir++.h" +%} + + + +/* + * Add bounds checking + * + * The Python library is targetted at experimentation. It is therefore + * reasonable to protect its object query and creation interfaces with code + * that performs bounds checking on arguments. + */ +%exception IIR::Coeff::c { + if(arg2 < 0 || arg2 >= arg1->nc()) { + char buf[80]; + snprintf(buf, sizeof(buf), "Index of c coefficient (%d) out of bounds " + "(0,%d).", arg2, arg1->nc() - 1); + PyErr_SetString(PyExc_RuntimeError, buf); + return NULL; + } + $action +} + +%exception IIR::Coeff::d { + if(arg2 < 0 || arg2 >= arg1->nd()) { + char buf[80]; + snprintf(buf, sizeof(buf), "Index of d coefficient (%d) out of bounds " + "(0,%d).", arg2, arg1->nd() - 1); + PyErr_SetString(PyExc_RuntimeError, buf); + return NULL; + } + $action +} + +%exception IIR::Filter::getCoeffSet { + if(arg2 < 0 || arg2 >= arg1->numCoeffSet()) { + char buf[80]; + snprintf(buf, sizeof(buf), "Index of coefficient set (%d) out of bounds " + "(0,%d).", arg2, arg1->numCoeffSet() - 1); + PyErr_SetString(PyExc_RuntimeError, buf); + return NULL; + } + $action +} + +%exception IIR::ButterworthLowPass { + if(arg1 < 1) { + char buf[80]; + snprintf(buf, sizeof(buf), "Invalid filter order (%d). Must be > 0.", + arg1); + PyErr_SetString(PyExc_RuntimeError, buf); + return NULL; + } + $action +} + +%exception IIR::ButterworthHighPass { + if(arg1 < 1) { + char buf[80]; + snprintf(buf, sizeof(buf), "Invalid filter order (%d). Must be > 0.", + arg1); + PyErr_SetString(PyExc_RuntimeError, buf); + return NULL; + } + $action +} + +%exception IIR::ButterworthBandPass { + if(arg1 < 1) { + char buf[80]; + snprintf(buf, sizeof(buf), "Invalid filter order (%d). Must be > 0.", + arg1); + PyErr_SetString(PyExc_RuntimeError, buf); + return NULL; + } + if(arg3 > arg4) { + PyErr_SetString(PyExc_RuntimeError, "Low corner frequency exceeds " + "high corner frequency."); + return NULL; + } + $action +} + +%exception IIR::ButterworthBandStop { + if(arg1 < 1) { + char buf[80]; + snprintf(buf, sizeof(buf), "Invalid filter order (%d). Must be > 0.", + arg1); + PyErr_SetString(PyExc_RuntimeError, buf); + return NULL; + } + if(arg3 > arg4) { + PyErr_SetString(PyExc_RuntimeError, "Low corner frequency exceeds " + "high corner frequency."); + return NULL; + } + $action +} + + + +/* + * Invalid filter string handling + * + * The C++ library uses a "valid()" member in IIR::Filter to avoid throwing an + * exception if the constructor is passed an invalid filter string. Python's + * exceptions are a much nicer fit, however, so use those instead. + * + * Notes: + * 1. Doesn't match if we prepend it with the namespace. + * 2. Applies to all constructors. + */ +%exception Filter { + $action + if(!result->valid()) { + PyErr_SetString(PyExc_RuntimeError, "Invalid filter description string."); + delete result; + return NULL; + } +} + + + +%include "iir++.h" + +// vim: ts=4:sw=4 diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 0000000..1b36f11 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +from distutils.core import setup, Extension + +iir_module = Extension('_iir', + sources=['pyiir_wrap.cxx'], + libraries=['iir++'], + ) + +setup(name = 'iir', + version = '1.0.3', + author = 'Laurence Withers', + description = 'IIR filter', + ext_modules = [iir_module], + py_modules = ['iir'], + )