cowboy 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -15,3 +15,7 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ *.bundle
19
+ *.o
20
+ *.log
21
+ ext/cowboy/Makefile
data/README.md CHANGED
@@ -1,9 +1,18 @@
1
1
  # Cowboy
2
2
 
3
- TODO: Write a gem description
3
+ This is a Ruby wrapper for [FFTW3](http://fftw.org). It currently only
4
+ supports 1-dimension transforms, but I'll be adding support for
5
+ multiple dimensions soon.
4
6
 
5
7
  ## Installation
6
8
 
9
+ ### Prerequisites
10
+
11
+ [FFTW3](http://fftw.org) is required. You can generally get it from
12
+ your OS's package manager (e.g. `sudo port install fftw3`).
13
+
14
+ ### Gem installation
15
+
7
16
  Add this line to your application's Gemfile:
8
17
 
9
18
  gem 'cowboy'
@@ -18,7 +27,30 @@ Or install it yourself as:
18
27
 
19
28
  ## Usage
20
29
 
21
- TODO: Write usage instructions here
30
+ After installing, you can call `Cowboy::fft_1d` with a 1d array of
31
+ real numbers. This will return an array of complex numbers. Note that
32
+ your input array will be blanked out.
33
+
34
+ ### Windowing
35
+
36
+ `Cowboy::fft` will do some windowing before calling `fft_1d`. This
37
+ currently behaves somewhat unintelligently and discards points around
38
+ the end of the array (for window size N, it discards N/2 from the
39
+ beginning and N/2 from the end) to allow for a full window at every
40
+ point.
41
+
42
+ The default is to use a
43
+ [Hamming Window](http://en.wikipedia.org/wiki/Window_function#Hamming_window)
44
+ with a window size of 29. This is configurable as the second and third
45
+ arguments to `fft`. (e.g. `Cowboy::fft(my_array, my_windowing_func,
46
+ 12)`)
47
+
48
+ ### Shifting
49
+
50
+ `Cowboy::Frequencies` is a class that, initialized an array of complex
51
+ numbers (ideally the output of a call to `fft`), will shift them to
52
+ their correct order (e.g. `[-nyquist, +nyquist]`). There is also a
53
+ function `buckets` that will give you the frequency buckets in order.
22
54
 
23
55
  ## Contributing
24
56
 
data/Rakefile CHANGED
@@ -1,2 +1,40 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+
4
+ require 'rake/testtask'
5
+ require 'rake/clean'
6
+
7
+ NAME = 'cowboy'
8
+
9
+ # rule to build the extension: this says
10
+ # that the extension should be rebuilt
11
+ # after any change to the files in ext
12
+ file "lib/#{NAME}/#{NAME}.bundle" =>
13
+ Dir.glob("ext/#{NAME}/*{.rb,.c}") do
14
+ Dir.chdir("ext/#{NAME}") do
15
+ # this does essentially the same thing
16
+ # as what RubyGems does
17
+ ruby "extconf.rb"
18
+ sh "make"
19
+ end
20
+ cp "ext/#{NAME}/#{NAME}.bundle", "lib/#{NAME}"
21
+ end
22
+
23
+ # make the :test task depend on the shared
24
+ # object, so it will be built automatically
25
+ # before running the tests
26
+ task :test => "lib/#{NAME}/#{NAME}.bundle"
27
+
28
+ # use 'rake clean' and 'rake clobber' to
29
+ # easily delete generated files
30
+ CLEAN.include('ext/**/*{.o,.log,.so,.bundle}')
31
+ CLEAN.include('ext/**/Makefile')
32
+ CLOBBER.include('lib/**/*{.so,.bundle}')
33
+
34
+ # the same as before
35
+ Rake::TestTask.new do |t|
36
+ t.libs << 'test'
37
+ end
38
+
39
+ desc "Run tests"
40
+ task :default => :test
@@ -4,9 +4,11 @@ require File.expand_path('../lib/cowboy/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Alejandro Ciniglio"]
6
6
  gem.email = ["mail@alejandrociniglio.com"]
7
- gem.description = %q{gem description}
8
- gem.summary = %q{gem summary}
9
- gem.homepage = ""
7
+ gem.description = %q{Cowboy is a wrapper for fftw
8
+ (C fourier transform library)}
9
+ gem.summary = %q{Cowboy allows you to access
10
+ blazing fast FFTs from within Ruby}
11
+ gem.homepage = "https://github.com/ciniglio/cowboy"
10
12
 
11
13
  gem.files = `git ls-files`.split($\)
12
14
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
@@ -2,34 +2,34 @@
2
2
 
3
3
  VALUE mCowboy;
4
4
 
5
- VALUE fft_1d(VALUE m, VALUE nums){
5
+ VALUE fft_1d(VALUE m, VALUE v) {
6
6
  fftw_complex *in, *out;
7
7
  fftw_plan fp;
8
- long n;
8
+ int n;
9
9
 
10
- Check_Type(nums, T_ARRAY);
11
-
12
- n = size_of_ary(nums);
13
- if (n == 0){
14
- rb_raise(rb_eException, "Can't use blank array");
10
+ n = (int) size_of_val(v);
11
+ if (n == 0) {
12
+ rb_raise(rb_eException, "Can't use empty set of samples");
15
13
  }
14
+
16
15
  in = allocate_fftw_complex(n);
17
16
  out = allocate_fftw_complex(n);
18
17
  fp = fftw_plan_dft_1d(n, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
19
18
 
20
- cast_nums_to_complex(in, nums);
19
+ cast_val_to_complex(in, v);
21
20
 
22
21
  fftw_execute(fp);
23
22
  free(in);
24
23
  fftw_destroy_plan(fp);
25
24
 
26
- return complex_to_real_nums(out, n);
25
+ return ca_wrap_struct_class(out, n);
27
26
  }
28
27
 
29
- void Init_cowboy(){
28
+ void Init_cowboy() {
30
29
  mCowboy = rb_define_module("Cowboy");
31
30
 
32
31
  Init_cowboy_complex();
32
+ Init_cowboy_array();
33
33
 
34
34
  rb_define_module_function(mCowboy, "fft_1d", fft_1d, 1);
35
35
  }
@@ -5,6 +5,7 @@
5
5
  #include <fftw3.h>;
6
6
 
7
7
  #include "cowboy_complex.h";
8
+ #include "cowboy_array.h";
8
9
 
9
10
  extern VALUE mCowboy;
10
11
 
@@ -0,0 +1,69 @@
1
+ #include "cowboy.h"
2
+ #include <ruby.h>
3
+
4
+ VALUE cCowboyArray;
5
+
6
+ typedef struct cowboy_array {
7
+ long N;
8
+ fftw_complex* fc;
9
+ } CowboyArray;
10
+
11
+ static void ca_free(void * ca){
12
+ free(((CowboyArray *)ca)->fc);
13
+ free(ca);
14
+ }
15
+
16
+ VALUE array_index(VALUE self, VALUE index){
17
+ fftw_complex * fc;
18
+ CowboyArray * ca;
19
+ long i = NUM2LONG(index);
20
+ Data_Get_Struct(self, CowboyArray, ca);
21
+ if (i >= ca->N){
22
+ return Qnil;
23
+ }
24
+ return c_to_rb_complex(ca->fc[i][0],
25
+ ca->fc[i][1]);
26
+ }
27
+
28
+ VALUE array_size(VALUE self){
29
+ fftw_complex * fc;
30
+ CowboyArray * ca;
31
+ Data_Get_Struct(self, CowboyArray, ca);
32
+ return LONG2NUM(ca->N);
33
+ }
34
+
35
+ VALUE array_each(VALUE self){
36
+ int i;
37
+ fftw_complex * fc;
38
+ CowboyArray * ca;
39
+ Data_Get_Struct(self, CowboyArray, ca);
40
+
41
+ if (!rb_block_given_p())
42
+ rb_raise(rb_eArgError, "Expected Block");
43
+
44
+ for(i = 0; i < ca->N; i++)
45
+ rb_yield(c_to_rb_complex(ca->fc[i][0],
46
+ ca->fc[i][1]));
47
+ return self;
48
+ }
49
+
50
+ VALUE ca_wrap_struct_class(fftw_complex *fc, long N){
51
+ CowboyArray * ca;
52
+ ca = ALLOC(CowboyArray);
53
+ ca->N = N;
54
+ ca->fc = fc;
55
+ return Data_Wrap_Struct(cCowboyArray, 0, free, ca);
56
+ }
57
+
58
+ VALUE ca_not_implemented(VALUE self){
59
+ rb_notimplement();
60
+ }
61
+
62
+ void Init_cowboy_array(){
63
+ cCowboyArray = rb_define_class_under(mCowboy, "CowboyArray", rb_cObject);
64
+ rb_define_method(cCowboyArray, "initialize", ca_not_implemented, 0);
65
+ rb_define_method(cCowboyArray, "size", array_size, 0);
66
+ rb_define_method(cCowboyArray, "[]", array_index, 1);
67
+ rb_define_method(cCowboyArray, "each", array_each, 0);
68
+ rb_include_module(cCowboyArray, rb_mEnumerable);
69
+ }
@@ -0,0 +1,7 @@
1
+ #ifndef RUBY_COWBOY_ARRAY
2
+ #define RUBY_COWBOY_ARRAY
3
+
4
+ extern VALUE cCowboyArray;
5
+ void Init_cowboy_array();
6
+ VALUE ca_wrap_struct_class(fftw_complex *fc, long N);
7
+ #endif
@@ -1,11 +1,48 @@
1
1
  #include "cowboy.h"
2
2
  #include <ruby.h>
3
+ #include <stdlib.h>
3
4
 
4
5
  long size_of_ary(VALUE nums){
5
6
  Check_Type(nums, T_ARRAY);
6
7
  return RARRAY_LEN(nums);
7
8
  }
8
9
 
10
+ long size_of_str(VALUE str){
11
+ Check_Type(str, T_STRING);
12
+ return RSTRING_LEN(RSTRING(str)) / 8;
13
+ }
14
+
15
+ long size_of_val(VALUE v){
16
+ if (TYPE(v) == T_STRING) {
17
+ return size_of_str(v);
18
+ } else if (TYPE(v) == T_ARRAY) {
19
+ return size_of_ary(v);
20
+ } else {
21
+ rb_raise(rb_eNotImpError, "Needs a string or array");
22
+ return 0;
23
+ }
24
+ }
25
+
26
+ void cast_string_to_complex(fftw_complex * fc, VALUE str){
27
+ unsigned char * s;
28
+ long len;
29
+ int i, j, a;
30
+ double d;
31
+ void *p;
32
+ double t = 2.3;
33
+
34
+ p = &d;
35
+
36
+ s = RSTRING_PTR(RSTRING(str));
37
+ len = RSTRING_LEN(RSTRING(str));
38
+
39
+ for(i = 0, j = 0; i < len; i+=8, j++){
40
+ d = *(double *) &s[i];
41
+ fc[j][0] = d;
42
+ fc[j][1] = 0;
43
+ }
44
+ }
45
+
9
46
  void cast_nums_to_complex(fftw_complex * fc, VALUE nums){
10
47
  long len;
11
48
  int i;
@@ -13,10 +50,20 @@ void cast_nums_to_complex(fftw_complex * fc, VALUE nums){
13
50
  Check_Type(nums, T_ARRAY);
14
51
  i = 0;
15
52
 
16
- while((n =rb_ary_shift(nums)) != Qnil){
17
- fc[i][0] = NUM2DBL(n);
18
- fc[i][1] = 0;
19
- i++;
53
+ for(len = 0; len < size_of_ary(nums); len++){
54
+ n = rb_ary_entry(nums, len);
55
+ fc[len][0] = NUM2DBL(n);
56
+ fc[len][1] = 0;
57
+ }
58
+ }
59
+
60
+ void cast_val_to_complex(fftw_complex * fc, VALUE v){
61
+ if (TYPE(v) == T_STRING) {
62
+ cast_string_to_complex(fc, v);
63
+ } else if (TYPE(v) == T_ARRAY) {
64
+ cast_nums_to_complex(fc, v);
65
+ } else {
66
+ rb_raise(rb_eNotImpError, "Needs a string or array");
20
67
  }
21
68
  }
22
69
 
@@ -32,25 +79,5 @@ VALUE c_to_rb_complex(double r, double i){
32
79
  DBL2NUM(r), DBL2NUM(i));
33
80
  }
34
81
 
35
- VALUE complex_to_real_nums(fftw_complex *fc, long N){
36
- VALUE ar = rb_ary_new();
37
- int i;
38
- for(i = 0; i < N; i++){
39
- rb_ary_push(ar, c_to_rb_complex(fc[i][0],
40
- fc[i][1]));
41
- }
42
- return ar;
43
- }
44
-
45
- VALUE test_back_and_from_complex(VALUE m, VALUE nums){
46
- Check_Type(nums, T_ARRAY);
47
- fftw_complex * fc;
48
- fc = allocate_fftw_complex(size_of_ary(nums));
49
- cast_nums_to_complex(fc, nums);
50
- return complex_to_real_nums(fc,
51
- size_of_ary(nums));
52
- }
53
-
54
82
  void Init_cowboy_complex(){
55
- rb_define_module_function(mCowboy, "test_complex", test_back_and_from_complex, 1);
56
83
  }
@@ -5,10 +5,11 @@
5
5
 
6
6
  void Init_cowboy_complex();
7
7
 
8
- void cast_nums_to_complex(fftw_complex * fc, VALUE nums);
8
+ long size_of_val(VALUE v);
9
+ void cast_val_to_complex(fftw_complex * fc, VALUE v);
10
+
9
11
  fftw_complex * allocate_fftw_complex(long n);
10
- VALUE complex_to_real_nums(fftw_complex * fc, long N);
11
- long size_of_ary(VALUE nums);
12
12
 
13
+ VALUE c_to_rb_complex(double r, double i);
13
14
 
14
15
  #endif
@@ -1,6 +1,7 @@
1
+ require 'cowboy/cowboy'
1
2
  require "cowboy/version"
2
3
 
3
- require 'cowboy/cowboy'
4
+
4
5
  module Cowboy
5
6
  # Your code goes here...
6
7
  end
@@ -16,13 +16,17 @@ module Cowboy
16
16
  end
17
17
 
18
18
  def buckets(sample_rate=nil)
19
+ return @buckets if @buckets
19
20
  sample_rate ||= @freq.size
20
21
  nyquist = sample_rate/2.0
22
+ sum = 0
21
23
  a = []
22
24
  n = @freq.size
23
25
  for i in (0...n)
24
- a << -nyquist + (2 * nyquist/n) * i
26
+ a << -nyquist + sum
27
+ sum += (2 * nyquist/n)
25
28
  end
29
+ @buckets = a
26
30
  return a
27
31
  end
28
32
  end
@@ -1,3 +1,3 @@
1
1
  module Cowboy
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,27 @@
1
+ require_relative './test_helper.rb'
2
+
3
+ require 'cowboy'
4
+ require 'test/unit'
5
+
6
+ class TestArray < Test::Unit::TestCase
7
+ def test_version
8
+ assert_not_nil Cowboy::VERSION
9
+ end
10
+
11
+ def test_ffd_1d_4
12
+ a = [1,2,3,4]
13
+ c = Cowboy::fft_1d a
14
+ assert_equal c[0], 10
15
+ assert_equal c[1], Complex(-2,2)
16
+ assert_equal c[2], Complex(-2,0)
17
+ assert_equal c[3], Complex(-2,-2)
18
+ end
19
+
20
+ def test_ffd_1d_8
21
+ a = [1,2,1,2,1,2,1,2]
22
+ c = Cowboy::fft_1d a
23
+ assert_equal c[0], 12
24
+ assert_equal c[1], 0
25
+ assert_equal c[4], -4
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ require_relative './test_helper.rb'
2
+
3
+ class TestEnumerable < Test::Unit::TestCase
4
+ def test_size
5
+ a = (0...10000).to_a
6
+ c = Cowboy::fft_1d a
7
+ assert_equal c.size, a.size
8
+ end
9
+
10
+ def test_each
11
+ a = (0...4).to_a
12
+ c = Cowboy::fft_1d a
13
+ assert_equal (c.respond_to? :each), true
14
+ assert_block do
15
+ c.each do |i|
16
+ assert_not_nil i
17
+ end
18
+ end
19
+ end
20
+
21
+ def test_map
22
+ a = (0...10).to_a
23
+ c = Cowboy::fft_1d a
24
+ assert_equal (c.respond_to? :map), true
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ $LOAD_PATH.unshift File.expand_path('../../ext', __FILE__)
3
+
4
+ require 'cowboy'
5
+ require 'test/unit'
@@ -0,0 +1,22 @@
1
+ require_relative './test_helper.rb'
2
+ require 'cowboy'
3
+ require 'test/unit'
4
+
5
+ class TestString < Test::Unit::TestCase
6
+ def test_ffd_1d_4
7
+ a = [1,2,3,4].pack('D*')
8
+ c = Cowboy::fft_1d a
9
+ assert_equal c[0], 10
10
+ assert_equal c[1], Complex(-2,2)
11
+ assert_equal c[2], Complex(-2,0)
12
+ assert_equal c[3], Complex(-2,-2)
13
+ end
14
+
15
+ def test_ffd_1d_8
16
+ a = [1,2,1,2,1,2,1,2].pack('D*')
17
+ c = Cowboy::fft_1d a
18
+ assert_equal c[0], 12
19
+ assert_equal c[1], 0
20
+ assert_equal c[4], -4
21
+ end
22
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cowboy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,9 +9,10 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-01 00:00:00.000000000 Z
12
+ date: 2012-11-06 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: gem description
14
+ description: ! "Cowboy is a wrapper for fftw\n (C fourier
15
+ transform library)"
15
16
  email:
16
17
  - mail@alejandrociniglio.com
17
18
  executables: []
@@ -27,6 +28,8 @@ files:
27
28
  - cowboy.gemspec
28
29
  - ext/cowboy/cowboy.c
29
30
  - ext/cowboy/cowboy.h
31
+ - ext/cowboy/cowboy_array.c
32
+ - ext/cowboy/cowboy_array.h
30
33
  - ext/cowboy/cowboy_complex.c
31
34
  - ext/cowboy/cowboy_complex.h
32
35
  - ext/cowboy/extconf.rb
@@ -34,7 +37,11 @@ files:
34
37
  - lib/cowboy/fft.rb
35
38
  - lib/cowboy/hamming.rb
36
39
  - lib/cowboy/version.rb
37
- homepage: ''
40
+ - test/test_array.rb
41
+ - test/test_enumerable.rb
42
+ - test/test_helper.rb
43
+ - test/test_string.rb
44
+ homepage: https://github.com/ciniglio/cowboy
38
45
  licenses: []
39
46
  post_install_message:
40
47
  rdoc_options: []
@@ -57,5 +64,9 @@ rubyforge_project:
57
64
  rubygems_version: 1.8.24
58
65
  signing_key:
59
66
  specification_version: 3
60
- summary: gem summary
61
- test_files: []
67
+ summary: Cowboy allows you to access blazing fast FFTs from within Ruby
68
+ test_files:
69
+ - test/test_array.rb
70
+ - test/test_enumerable.rb
71
+ - test/test_helper.rb
72
+ - test/test_string.rb