/*
signal.c
Ruby/GSL: Ruby extension library for GSL (GNU Scientific Library)
(C) Copyright 2004 by Yoshiki Tsunesada
Ruby/GSL is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY.
*/
#include "rb_gsl_config.h"
#include "rb_gsl_fft.h"
enum FFT_CONV_CORR {
RB_GSL_FFT_CONVOLVE = 0,
RB_GSL_FFT_CORRELATE = 1,
RB_GSL_FFT_REAL = 2,
RB_GSL_FFT_HALFCOMPLEX = 3,
};
#ifndef WAVETABLE_P
#define WAVETABLE_P(x) (rb_obj_is_kind_of(x,cgsl_fft_halfcomplex_wavetable)?1:0)
#endif
#ifndef CHECK_WAVETABLE
#define CHECK_WAVETABLE(x) if(!rb_obj_is_kind_of(x,cgsl_fft_halfcomplex_wavetable))\
rb_raise(rb_eTypeError, "wrong argument type (FFT::HalfComplex::Wavetable expected)");
#endif
#ifndef WORKSPACE_P
#define WORKSPACE_P(x) (rb_obj_is_kind_of(x,cgsl_fft_real_workspace)?1:0)
#endif
#ifndef CHECK_WORKSPACE
#define CHECK_WORKSPACE(x) if(!rb_obj_is_kind_of(x,cgsl_fft_real_workspace))\
rb_raise(rb_eTypeError, "wrong argument type (FFT::Real::Workspace expected)");
#endif
/* data1, data2: FFTed data */
static void rbgsl_convolve_c(const double *data1, const double *data2,
double *data3, size_t size,
gsl_fft_halfcomplex_wavetable *table,
gsl_fft_real_workspace *space)
{
size_t i;
double re1, re2, im1, im2;
data3[0] = data1[0]*data2[0];
data3[size-1] = data1[size-1]*data2[size-1];
for (i = 1; i < size-1; i+=2) {
re1 = data1[i]; im1 = data1[i+1];
re2 = data2[i]; im2 = data2[i+1];
/* complex multiplication */
data3[i] = re1*re2 - im1*im2;
data3[i+1] = re1*im2 + im1*re2;
}
gsl_fft_halfcomplex_inverse(data3, 1, size, table, space);
}
static void rbgsl_correlate_c(const double *data1, const double *data2,
double *data3, size_t size,
gsl_fft_halfcomplex_wavetable *table,
gsl_fft_real_workspace *space)
{
size_t i;
double re1, re2, im1, im2;
data3[0] = data1[0]*data2[0];
data3[size-1] = data1[size-1]*data2[size-1];
for (i = 1; i < size-1; i+=2) {
re1 = data1[i]; im1 = data1[i+1];
re2 = data2[i]; im2 = data2[i+1];
/* complex conjugate multiplication */
data3[i] = re1*re2 + im1*im2;
data3[i+1] = -re1*im2 + im1*re2;
}
gsl_fft_halfcomplex_inverse(data3, 1, size, table, space);
}
/* Singleton method, GSL::FFT::HalfComplex */
static VALUE rb_gsl_fft_conv_corr(int argc, VALUE *argv, VALUE obj,
enum FFT_CONV_CORR flag1,
enum FFT_CONV_CORR flag2)
{
double *data1, *data2, *data3;
size_t stride1, stride2, stride3 = 1, size1, size2;
int naflag1, naflag2, shape;
gsl_vector *v = NULL;
gsl_fft_halfcomplex_wavetable *table = NULL;
gsl_fft_real_wavetable *rtable = NULL;
gsl_fft_real_workspace *space = NULL, *space2 = NULL;
int flagt = 0, flagw = 0;
gsl_vector *vtmp1 = NULL, *vtmp2 = NULL;
VALUE ary;
switch (argc) {
case 4:
data1 = get_ptr_double3(argv[0], &size1, &stride1, &naflag1);
data2 = get_ptr_double3(argv[1], &size2, &stride2, &naflag2);
CHECK_WAVETABLE(argv[2]);
Data_Get_Struct(argv[2], gsl_fft_halfcomplex_wavetable, table);
CHECK_WORKSPACE(argv[3]);
Data_Get_Struct(argv[3], gsl_fft_real_workspace, space);
break;
case 3:
data1 = get_ptr_double3(argv[0], &size1, &stride1, &naflag1);
data2 = get_ptr_double3(argv[1], &size2, &stride2, &naflag2);
if (WAVETABLE_P(argv[2])) {
Data_Get_Struct(argv[2], gsl_fft_halfcomplex_wavetable, table);
space = gsl_fft_real_workspace_alloc(size1);
flagw = 1;
} else if (WORKSPACE_P(argv[2])) {
Data_Get_Struct(argv[2], gsl_fft_real_workspace, space);
table = gsl_fft_halfcomplex_wavetable_alloc(size1);
flagt = 1;
} else {
rb_raise(rb_eTypeError,
"wrong argument type %s "
"(FFT::HalfComplex::Wavetable or FFT::Real::Workspace expected)",
rb_class2name(CLASS_OF(argv[2])));
}
break;
case 2:
data1 = get_ptr_double3(argv[0], &size1, &stride1, &naflag1);
data2 = get_ptr_double3(argv[1], &size2, &stride2, &naflag2);
table = gsl_fft_halfcomplex_wavetable_alloc(size1);
space = gsl_fft_real_workspace_alloc(size1);
flagt = 1;
flagw = 1;
break;
default:
rb_raise(rb_eArgError, "wrong number of arguments (%d for 2-4)", argc);
}
switch (naflag1*naflag2) {
case 0:
v = gsl_vector_alloc(size1);
ary = Data_Wrap_Struct(cgsl_vector, 0, gsl_vector_free, v);
data3 = v->data;
stride3 = 1;
break;
case 1:
#ifdef HAVE_NARRAY_H
shape = (int) size1;
ary = na_make_object(NA_DFLOAT, 1, &shape, cNArray);
data3 = NA_PTR_TYPE(ary, double*);
stride3 = 1;
#endif
break;
default:
break;
}
switch (flag1) {
case RB_GSL_FFT_REAL:
vtmp1 = gsl_vector_alloc(size1);
vtmp2 = gsl_vector_alloc(size2);
memcpy(vtmp1->data, data1, sizeof(double)*size1);
memcpy(vtmp2->data, data2, sizeof(double)*size2);
data1 = vtmp1->data;
data2 = vtmp2->data;
rtable = gsl_fft_real_wavetable_alloc(size1);
if (size1 == space->n) {
gsl_fft_real_transform(data1, stride1, size1, rtable, space);
} else {
space2 = gsl_fft_real_workspace_alloc(size1);
gsl_fft_real_transform(data1, stride1, size1, rtable, space2);
/* no freeing space2 here */
}
if (size1 != size2) {
if (rtable) gsl_fft_real_wavetable_free(rtable);
rtable = gsl_fft_real_wavetable_alloc(size2);
}
if (size2 == space->n) {
gsl_fft_real_transform(data2, stride2, size2, rtable, space);
} else if (size2 == size1) {
gsl_fft_real_transform(data2, stride2, size2, rtable, space2);
gsl_fft_real_workspace_free(space2);
} else {
if (space2) gsl_fft_real_workspace_free(space2);
space2 = gsl_fft_real_workspace_alloc(size2);
gsl_fft_real_transform(data2, stride2, size2, rtable, space2);
gsl_fft_real_workspace_free(space2);
}
gsl_fft_real_wavetable_free(rtable);
space2 = NULL;
rtable = NULL;
break;
case RB_GSL_FFT_HALFCOMPLEX:
/* do nothing */
break;
default:
/* not occur */
break;
}
switch (flag2) {
case RB_GSL_FFT_CONVOLVE:
rbgsl_convolve_c(data1, data2, data3, size1, table, space);
break;
case RB_GSL_FFT_CORRELATE:
rbgsl_correlate_c(data1, data2, data3, size1, table, space);
break;
default:
break;
}
if (flagt == 1) gsl_fft_halfcomplex_wavetable_free(table);
if (flagw == 1) gsl_fft_real_workspace_free(space);
if (vtmp1) gsl_vector_free(vtmp1);
if (vtmp2) gsl_vector_free(vtmp2);
return ary;
}
/* Singleton method, GSL::FFT::Real */
static VALUE rb_gsl_fft_real_convolve(int argc, VALUE *argv, VALUE obj)
{
return rb_gsl_fft_conv_corr(argc, argv, obj,
RB_GSL_FFT_REAL,
RB_GSL_FFT_CONVOLVE);
}
/* Singleton method, GSL::FFT::Real */
static VALUE rb_gsl_fft_real_correlate(int argc, VALUE *argv, VALUE obj)
{
return rb_gsl_fft_conv_corr(argc, argv, obj,
RB_GSL_FFT_REAL,
RB_GSL_FFT_CORRELATE);
}
/* Singleton method, GSL::FFT::HalfComplex */
static VALUE rb_gsl_fft_halfcomplex_convolve(int argc, VALUE *argv, VALUE obj)
{
return rb_gsl_fft_conv_corr(argc, argv, obj,
RB_GSL_FFT_HALFCOMPLEX,
RB_GSL_FFT_CONVOLVE);
}
/* Singleton method, GSL::FFT::HalfComplex */
static VALUE rb_gsl_fft_halfcomplex_correlate(int argc, VALUE *argv, VALUE obj)
{
return rb_gsl_fft_conv_corr(argc, argv, obj,
RB_GSL_FFT_HALFCOMPLEX,
RB_GSL_FFT_CORRELATE);
}
void Init_gsl_signal(VALUE module)
{
rb_define_singleton_method(mgsl_fft_real, "convolve",
rb_gsl_fft_real_convolve, -1);
rb_define_singleton_method(mgsl_fft_real, "correlate",
rb_gsl_fft_real_correlate, -1);
rb_define_singleton_method(mgsl_fft_halfcomplex, "convolve",
rb_gsl_fft_halfcomplex_convolve, -1);
rb_define_singleton_method(mgsl_fft_halfcomplex, "correlate",
rb_gsl_fft_halfcomplex_correlate, -1);
}
#undef WAVETABLE_P
#undef CHECK_WAVETABLE
#undef WORKSPACE_P
#undef CHECK_WORKSPACE
syntax highlighted by Code2HTML, v. 0.9.1