cowboy 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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