diff options
Diffstat (limited to 'gcell/src/lib/wrapper')
-rw-r--r-- | gcell/src/lib/wrapper/Makefile.am | 50 | ||||
-rw-r--r-- | gcell/src/lib/wrapper/gcp_fft_1d_r2.cc | 116 | ||||
-rw-r--r-- | gcell/src/lib/wrapper/gcp_fft_1d_r2.h | 66 | ||||
-rw-r--r-- | gcell/src/lib/wrapper/qa_gcell_wrapper.cc | 39 | ||||
-rw-r--r-- | gcell/src/lib/wrapper/qa_gcell_wrapper.h | 35 | ||||
-rw-r--r-- | gcell/src/lib/wrapper/qa_gcp_fft_1d_r2.cc | 211 | ||||
-rw-r--r-- | gcell/src/lib/wrapper/qa_gcp_fft_1d_r2.h | 48 | ||||
-rw-r--r-- | gcell/src/lib/wrapper/spu/gcs_fft_1d_r2.c | 39 |
8 files changed, 604 insertions, 0 deletions
diff --git a/gcell/src/lib/wrapper/Makefile.am b/gcell/src/lib/wrapper/Makefile.am new file mode 100644 index 0000000000..03ffa54b32 --- /dev/null +++ b/gcell/src/lib/wrapper/Makefile.am @@ -0,0 +1,50 @@ +# +# Copyright 2008 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 this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = $(DEFINES) $(GCELL_INCLUDES) $(FFTW3F_CFLAGS) $(WITH_INCLUDES) + +noinst_LTLIBRARIES = libwrapper.la libwrapper-qa.la + +# generate a libtool.lo that contains an embeded SPU executable +gcell_all.lo: ../spu/gcell_all + $(GCELL_EMBEDSPU_LIBTOOL) $@ $< + +libwrapper_la_SOURCES = \ + gcp_fft_1d_r2.cc + +libwrapper_la_LIBADD = \ + gcell_all.lo + +libwrapper_qa_la_SOURCES = \ + qa_gcell_wrapper.cc \ + qa_gcp_fft_1d_r2.cc + +libwrapper_qa_la_LIBADD = \ + -lfftw3f + +gcellinclude_HEADERS = \ + gcp_fft_1d_r2.h + +noinst_HEADERS = \ + qa_gcell_wrapper.h + +CLEANFILES = gcell_all.lo diff --git a/gcell/src/lib/wrapper/gcp_fft_1d_r2.cc b/gcell/src/lib/wrapper/gcp_fft_1d_r2.cc new file mode 100644 index 0000000000..d639dad452 --- /dev/null +++ b/gcell/src/lib/wrapper/gcp_fft_1d_r2.cc @@ -0,0 +1,116 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <gcp_fft_1d_r2.h> +#include <stdint.h> +#include <stdexcept> +#include <math.h> + +static void +init_jd(gc_job_desc *jd, + gc_proc_id_t proc_id, + unsigned log2_fft_length, + bool forward, + std::complex<float> *out, + const std::complex<float> *in, + const std::complex<float> *W) +{ + jd->proc_id = proc_id; + jd->input.nargs = 2; + jd->output.nargs = 0; + jd->eaa.nargs = 3; + + jd->input.arg[0].u32 = log2_fft_length; + jd->input.arg[1].u32 = forward; + unsigned int fft_length = 1 << log2_fft_length; + + jd->eaa.arg[0].ea_addr = ptr_to_ea(out); + jd->eaa.arg[0].direction = GCJD_DMA_PUT; + jd->eaa.arg[0].put_size = sizeof(std::complex<float>) * fft_length; + + jd->eaa.arg[1].ea_addr = ptr_to_ea(const_cast<std::complex<float>*>(in)); + jd->eaa.arg[1].direction = GCJD_DMA_GET; + jd->eaa.arg[1].get_size = sizeof(std::complex<float>) * fft_length; + + jd->eaa.arg[2].ea_addr = ptr_to_ea(const_cast<std::complex<float>*>(W)); + jd->eaa.arg[2].direction = GCJD_DMA_GET; + jd->eaa.arg[2].get_size = sizeof(std::complex<float>) * fft_length / 4; +} + + +gc_job_desc * +gcp_fft_1d_r2_submit(gc_job_manager_sptr mgr, + unsigned int log2_fft_length, + bool forward, + std::complex<float> *out, + const std::complex<float> *in, + const std::complex<float> *W) +{ + unsigned int fft_length = 1 << log2_fft_length; + if (fft_length > 4096) + throw std::invalid_argument("fft_length > 4096"); + + if ((intptr_t)out & 0xf) + throw gc_bad_align("out"); + if ((intptr_t)in & 0xf) + throw gc_bad_align("in"); + if ((intptr_t)W & 0xf) + throw gc_bad_align("W"); + + gc_proc_id_t fft_id = mgr->lookup_proc("fft_1d_r2"); + gc_job_desc *jd = mgr->alloc_job_desc(); + init_jd(jd, fft_id, log2_fft_length, forward, out, in, W); + if (!mgr->submit_job(jd)){ + gc_job_status_t s = jd->status; + mgr->free_job_desc(jd); + throw gc_bad_submit("fft_1d_r2", s); + } + return jd; +} + +void +gcp_fft_1d_r2_forward_twiddle(unsigned int log2_fft_length, std::complex<float> *W) +{ + unsigned int n = 1 << log2_fft_length; + + W[0].real() = 1.0; + W[0].imag() = 0.0; + for (unsigned i=1; i < n/4; i++){ + W[i].real() = cos(i * 2*M_PI/n); + W[n/4 - i].imag() = -W[i].real(); + } +} + + +void +gcp_fft_1d_r2_reverse_twiddle(unsigned int log2_fft_length, std::complex<float> *W) +{ + // FIXME this is wrong/insufficient. inverse is still incorrect + + // reverse factors are the conjugate of the forward factors + gcp_fft_1d_r2_forward_twiddle(log2_fft_length, W); + + unsigned int n = 1 << log2_fft_length; + for (unsigned i=0; i < n/4; i++) + W[i] = conj(W[i]); +} diff --git a/gcell/src/lib/wrapper/gcp_fft_1d_r2.h b/gcell/src/lib/wrapper/gcp_fft_1d_r2.h new file mode 100644 index 0000000000..be1440fd43 --- /dev/null +++ b/gcell/src/lib/wrapper/gcp_fft_1d_r2.h @@ -0,0 +1,66 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_GCP_FFT_1D_R2_H +#define INCLUDED_GCP_FFT_1D_R2_H + +#include <gc_job_manager.h> +#include <complex> + +/*! + * \brief Submit a job that computes the forward or reverse FFT. + * + * \param mgr is the job manager instance + * \param log2_fft_length is the log2 of the fft_length (4 <= x <= 13). + * \param forward is true to compute the forward xform + * \param out is the fft_length output from FFT (must be 16-byte aligned). + * \param in is the fft_length input to FFT (must be 16-byte aligned). + * \param W is fft_length/4 twiddle factor input to FFT (must be 16-byte aligned). + * + * Returns a job descriptor which should be passed to wait_job*. + * Throws an exception in the event of a problem. + */ +gc_job_desc * +gcp_fft_1d_r2_submit(gc_job_manager_sptr mgr, + unsigned int log2_fft_length, + bool forward, + std::complex<float> *out, + const std::complex<float> *in, + const std::complex<float> *W); + +/*! + * \brief Compute twiddle factors for forward transform. + * + * \param log2_fft_length is the log2 of the fft_length. + * \param W is fft_length/4 twiddle factor output (must be 16-byte aligned). + */ +void +gcp_fft_1d_r2_forward_twiddle(unsigned int log2_fft_length, std::complex<float> *W); + +/*! + * \brief Compute twiddle factors for reverse transform. + * + * \param log2_fft_length is the log2 of the fft_length. + * \param W is fft_length/4 twiddle factor output (must be 16-byte aligned). + */ +void +gcp_fft_1d_r2_reverse_twiddle(unsigned int log2_fft_length, std::complex<float> *W); + +#endif /* INCLUDED_GCP_FFT_1D_R2_H */ diff --git a/gcell/src/lib/wrapper/qa_gcell_wrapper.cc b/gcell/src/lib/wrapper/qa_gcell_wrapper.cc new file mode 100644 index 0000000000..029dfbc580 --- /dev/null +++ b/gcell/src/lib/wrapper/qa_gcell_wrapper.cc @@ -0,0 +1,39 @@ +/* -*- c++ -*- */ +/* + * Copyright 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * This class gathers together all the test cases for the lib + * directory into a single test suite. As you create new test cases, + * add them here. + */ + +#include <qa_gcell_wrapper.h> +#include <qa_gcp_fft_1d_r2.h> + +CppUnit::TestSuite * +qa_gcell_wrapper::suite() +{ + CppUnit::TestSuite *s = new CppUnit::TestSuite("wrapper"); + + s->addTest(qa_gcp_fft_1d_r2::suite()); + + return s; +} diff --git a/gcell/src/lib/wrapper/qa_gcell_wrapper.h b/gcell/src/lib/wrapper/qa_gcell_wrapper.h new file mode 100644 index 0000000000..cb29db8836 --- /dev/null +++ b/gcell/src/lib/wrapper/qa_gcell_wrapper.h @@ -0,0 +1,35 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_QA_GCELL_WRAPPER_H +#define INCLUDED_QA_GCELL_WRAPPER_H + +#include <cppunit/TestSuite.h> + +//! collect all the tests for the wrapper directory + +class qa_gcell_wrapper { +public: + //! return suite of tests + static CppUnit::TestSuite *suite(); +}; + + +#endif /* INCLUDED_QA_GCELL_WRAPPER_H */ diff --git a/gcell/src/lib/wrapper/qa_gcp_fft_1d_r2.cc b/gcell/src/lib/wrapper/qa_gcp_fft_1d_r2.cc new file mode 100644 index 0000000000..1bb676ac2f --- /dev/null +++ b/gcell/src/lib/wrapper/qa_gcp_fft_1d_r2.cc @@ -0,0 +1,211 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "qa_gcp_fft_1d_r2.h" +#include <cppunit/TestAssert.h> +#include <gcp_fft_1d_r2.h> +#include <fftw3.h> +#include <stdio.h> +#include <stdlib.h> // random, posix_memalign +#include <algorithm> + +typedef boost::shared_ptr<void> void_sptr; + +// handle to embedded SPU executable +extern spe_program_handle_t gcell_all; + +/* + * Return pointer to cache-aligned chunk of storage of size size bytes. + * Throw if can't allocate memory. The storage should be freed + * with "free" when done. The memory is initialized to zero. + */ +static void * +aligned_alloc(size_t size, size_t alignment = 128) +{ + void *p = 0; + if (posix_memalign(&p, alignment, size) != 0){ + perror("posix_memalign"); + throw std::runtime_error("memory"); + } + memset(p, 0, size); // zero the memory + return p; +} + +class free_deleter { +public: + void operator()(void *p) { + free(p); + } +}; + +static boost::shared_ptr<void> +aligned_alloc_sptr(size_t size, size_t alignment = 128) +{ + return boost::shared_ptr<void>(aligned_alloc(size, alignment), free_deleter()); +} + +// test forward FFT +void +qa_gcp_fft_1d_r2::t1() +{ + gc_jm_options opts; + opts.program_handle = gc_program_handle_from_address(&gcell_all); + opts.nspes = 1; + gc_job_manager_sptr mgr = gc_make_job_manager(&opts); + +#if 1 + for (int log2_fft_size = 5; log2_fft_size <= 12; log2_fft_size++){ + test(mgr, log2_fft_size, true); + } +#else + test(mgr, 5, true); +#endif +} + +// test reverse FFT +void +qa_gcp_fft_1d_r2::t2() +{ + gc_jm_options opts; + opts.program_handle = gc_program_handle_from_address(&gcell_all); + opts.nspes = 1; + gc_job_manager_sptr mgr = gc_make_job_manager(&opts); + +#if 1 + for (int log2_fft_size = 5; log2_fft_size <= 12; log2_fft_size++){ + test(mgr, log2_fft_size, false); + } +#else + test(mgr, 5, false); +#endif +} + +void +qa_gcp_fft_1d_r2::t3() +{ +} + +void +qa_gcp_fft_1d_r2::t4() +{ +} + +static inline float +abs_diff(std::complex<float> x, std::complex<float> y) +{ + return std::max(std::abs(x.real()-y.real()), + std::abs(x.imag()-y.imag())); +} + +static float +float_abs_rel_error(float ref, float actual) +{ + float delta = ref - actual; + if (std::abs(ref) < 1e-18) + ref = 1e-18; + return std::abs(delta/ref); +} + +static float +abs_rel_error(std::complex<float> ref, std::complex<float> actual) +{ + return std::max(float_abs_rel_error(ref.real(), actual.real()), + float_abs_rel_error(ref.imag(), actual.imag())); +} + +void +qa_gcp_fft_1d_r2::test(gc_job_manager_sptr mgr, int log2_fft_size, bool forward) +{ + int fft_size = 1 << log2_fft_size; + + // allocate aligned buffers with boost shared_ptr's + void_sptr fftw_in_void = aligned_alloc_sptr(fft_size * sizeof(std::complex<float>), 128); + void_sptr fftw_out_void = aligned_alloc_sptr(fft_size * sizeof(std::complex<float>), 128); + void_sptr cell_in_void = aligned_alloc_sptr(fft_size * sizeof(std::complex<float>), 128); + void_sptr cell_out_void = aligned_alloc_sptr(fft_size * sizeof(std::complex<float>), 128); + void_sptr cell_twiddle_void = aligned_alloc_sptr(fft_size/4 * sizeof(std::complex<float>), 128); + + // cast them to the type we really want + std::complex<float> *fftw_in = (std::complex<float> *) fftw_in_void.get(); + std::complex<float> *fftw_out = (std::complex<float> *) fftw_out_void.get(); + std::complex<float> *cell_in = (std::complex<float> *) cell_in_void.get(); + std::complex<float> *cell_out = (std::complex<float> *) cell_out_void.get(); + std::complex<float> *cell_twiddle = (std::complex<float> *) cell_twiddle_void.get(); + + if (forward) + gcp_fft_1d_r2_forward_twiddle(log2_fft_size, cell_twiddle); + else + gcp_fft_1d_r2_reverse_twiddle(log2_fft_size, cell_twiddle); + + srandom(1); // we want reproducibility + + // initialize the input buffers + for (int i = 0; i < fft_size; i++){ + std::complex<float> t((float) (random() & 0xfffff), (float) (random() & 0xfffff)); + fftw_in[i] = t; + cell_in[i] = t; + } + + // ------------------------------------------------------------------------ + // compute the reference answer + fftwf_plan plan = fftwf_plan_dft_1d (fft_size, + reinterpret_cast<fftwf_complex *>(fftw_in), + reinterpret_cast<fftwf_complex *>(fftw_out), + forward ? FFTW_FORWARD : FFTW_BACKWARD, + FFTW_ESTIMATE); + if (plan == 0){ + fprintf(stderr, "qa_gcp_fft_1d_r2: error creating FFTW plan\n"); + throw std::runtime_error ("fftwf_plan_dft_r2c_1d failed"); + } + + fftwf_execute(plan); + fftwf_destroy_plan(plan); + + // ------------------------------------------------------------------------ + // compute the answer on the cell + gc_job_desc *jd = gcp_fft_1d_r2_submit(mgr, log2_fft_size, forward, + cell_out, cell_in, cell_twiddle); + if (!mgr->wait_job(jd)){ + fprintf(stderr, "wait_job failed: %s\n", gc_job_status_string(jd->status).c_str()); + mgr->free_job_desc(jd); + CPPUNIT_ASSERT(0); + } + mgr->free_job_desc(jd); + + // ------------------------------------------------------------------------ + // compute the maximum of the relative error + float max_rel = 0.0; + for (int i = 0; i < fft_size; i++){ + max_rel = std::max(max_rel, abs_rel_error(fftw_out[i], cell_out[i])); + if (0) + printf("(%16.3f, %16.3fj) (%16.3f, %16.3fj) (%16.3f, %16.3fj)\n", + fftw_out[i].real(), fftw_out[i].imag(), + cell_out[i].real(), cell_out[i].imag(), + fftw_out[i].real() - cell_out[i].real(), + fftw_out[i].imag() - cell_out[i].imag()); + } + + fprintf(stdout, "%s fft_size = %4d max_rel_error = %e\n", + forward ? "fwd" : "rev", fft_size, max_rel); + + // CPPUNIT_ASSERT(max_rel <= 1e-4); + +} diff --git a/gcell/src/lib/wrapper/qa_gcp_fft_1d_r2.h b/gcell/src/lib/wrapper/qa_gcp_fft_1d_r2.h new file mode 100644 index 0000000000..38beafb219 --- /dev/null +++ b/gcell/src/lib/wrapper/qa_gcp_fft_1d_r2.h @@ -0,0 +1,48 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_QA_GCP_FFT_1D_R2_H +#define INCLUDED_QA_GCP_FFT_1D_R2_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> +#include <gc_job_manager.h> + +class qa_gcp_fft_1d_r2 : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE(qa_gcp_fft_1d_r2); + CPPUNIT_TEST(t1); + CPPUNIT_TEST(t2); + CPPUNIT_TEST(t3); + CPPUNIT_TEST(t4); + CPPUNIT_TEST_SUITE_END(); + + private: + void t1(); + void t2(); + void t3(); + void t4(); + + void test(gc_job_manager_sptr mgr, int log2_fft_size, bool forward); +}; + + + +#endif /* INCLUDED_QA_GCP_FFT_1D_R2_H */ diff --git a/gcell/src/lib/wrapper/spu/gcs_fft_1d_r2.c b/gcell/src/lib/wrapper/spu/gcs_fft_1d_r2.c new file mode 100644 index 0000000000..36bd878ede --- /dev/null +++ b/gcell/src/lib/wrapper/spu/gcs_fft_1d_r2.c @@ -0,0 +1,39 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <gc_declare_proc.h> +#include <libfft.h> + +static void +gcs_fft_1d_r2(const gc_job_direct_args_t *input, + gc_job_direct_args_t *output __attribute__((unused)), + const gc_job_ea_args_t *eaa) +{ + vector float *out = (vector float *) eaa->arg[0].ls_addr; + vector float *in = (vector float *) eaa->arg[1].ls_addr; + vector float *W = (vector float *) eaa->arg[2].ls_addr; + int log2_fft_length = input->arg[0].u32; + int forward = input->arg[1].u32; // non-zero if forward xform (FIXME use) + + fft_1d_r2(out, in, W, log2_fft_length); +} + +GC_DECLARE_PROC(gcs_fft_1d_r2, "fft_1d_r2"); |