turtleshell 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0020ab1903bd5ebd8bf3338fbf008ed6c8496411
4
+ data.tar.gz: 1243d95e4ae08009ff350e827fd03876d1f93a93
5
+ SHA512:
6
+ metadata.gz: 7ff72851c7c4aac05e27ae19a2ae84872d454e9342565a63c084449b87b2d0cf0f8f53ffe7e9ff5c380fd364616bf67f069ab9109e965f74b156723849f47df0
7
+ data.tar.gz: c2ebcfd7eb0001075f1645f87e2a16e5c0a2952a738c1dcd2b2cc175223ade07feefd047d8be95d7774fa420285c3f2bd89227a0ea08e7c7ed37a3b27f21b49e
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ **/*.bundle
3
+ **/*.o
4
+ tmp
5
+ Gemfile.lock
6
+ .rvmrc
@@ -0,0 +1,5 @@
1
+ ### 1.0.0 (2013-12-28)
2
+
3
+ * Added sample rate, gain, center frequency getters / setters
4
+ * Added `read_samples` method to read syncronously from the device
5
+ * Added `read_samples_async` to read asyncronously
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rake'
4
+ gem 'rake-compiler'
5
+
6
+ group :development do
7
+ gem 'rspec'
8
+ gem 'pry'
9
+ gem 'pry-nav'
10
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Tim Jarratt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ [![Build Status](https://travis-ci.org/tjarratt/turtleshell.png)](https://travis-ci.org/tjarratt/turtleshell) turRTLshell
2
+ ============
3
+
4
+ Overview
5
+ --------
6
+ Turtleshell is a ruby wrapper around librtlsdr, a C library for interfacing with Realtek RTL2832 DVB USB devices. More information about librtlsdr can be found [on their website](http://sdr.osmocom.org/trac/wiki/rtl-sdr).
7
+
8
+ Usage
9
+ -----
10
+ Turtleshell is available on rubygems. You can install it with `gem install turtleshell` or by adding it to your `Gemfile`.
11
+
12
+ Most of the key functions in librtlsdr are available, more will be added as development continues.
13
+
14
+ Examples
15
+ --------
16
+ Getting a reference to a device and reading from it could not be easier
17
+ ```ruby
18
+ require 'turtleshell'
19
+ sdr = TurtleShell::Device.new
20
+
21
+ # configure device properties
22
+ sdr.sample_rate = 2.4e6
23
+ sdr.center_frequency = 100e6
24
+ sdr.gain = 10
25
+
26
+ # read samples from the device
27
+ samples = sdr.read_samples
28
+ puts "signal mean: #{samples.inject(:+)/samples.length}"
29
+ ```
30
+
31
+ More examples are available in the [examples directory](https://github.com/tjarratt/turtleshell/tree/master/examples).
32
+
33
+ Dependencies
34
+ ------------
35
+ * ruby (version 1.9.2 or later)
36
+ * librtlsdr
37
+
38
+ License
39
+ -------
40
+ Turtleshell is released under the permissive MIT license.
41
+
42
+ Credit
43
+ ------
44
+ The idea for this project came to me when I was watching [@0xabad1dea's](https://github.com/0xabad1dea) talk at Defcon 21. Without that initial idea and inspiration, this wouldn't exist!
45
+
46
+ Credit is due to [@roger-](https://github.com/roger-) for writing pyrtlsdr, which provided a great reference when working on Turtleshell.
@@ -0,0 +1,25 @@
1
+ require 'bundler/setup'
2
+ require 'rspec/core/rake_task'
3
+ require 'rake/extensiontask'
4
+
5
+ task :default => 'compile:librtlsdr'
6
+ task :default => :spec
7
+ task :spec => 'spec:progress'
8
+
9
+ Rake::ExtensionTask.new(:librtlsdr) do |ext|
10
+ ext.lib_dir = "lib/librtlsdr"
11
+ end
12
+
13
+ namespace :spec do
14
+ desc 'Run all specs in spec directory (format=progress)'
15
+ RSpec::Core::RakeTask.new(:progress) do |t|
16
+ t.pattern = 'spec/**/*_spec.rb'
17
+ t.rspec_opts = %w{ --color --format=progress }
18
+ end
19
+
20
+ desc 'Run all specs in spec directory (format=documentation)'
21
+ RSpec::Core::RakeTask.new(:documentation) do |t|
22
+ t.pattern = 'spec/**/*_spec.rb'
23
+ t.rspec_opts = %w{ --color --format=documentation }
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ lib = File.expand_path('../../lib', __FILE__)
3
+ $:.unshift lib unless $:.include? lib
4
+
5
+ require 'turtleshell'
6
+
7
+ begin
8
+ sdr = TurtleShell::Device.new
9
+ rescue TurtleShell::DeviceNotFoundError
10
+ puts 'No compatible devices found. bailing!'
11
+ exit 1
12
+ end
13
+
14
+
15
+ sdr.sample_rate = 2.4e6
16
+ sdr.center_frequency = 100e6
17
+ sdr.gain = 8
18
+
19
+ count = 0
20
+ puts "\n\nTesting async callbacks..."
21
+ sdr.read_samples_async(512) do |buffer|
22
+ count += 1
23
+ puts "\t in async callback with buffer of length #{buffer.length}"
24
+ puts "\t signal mean: #{buffer.inject(:+)/buffer.length}"
25
+
26
+ count == 3 # exit when this has been called three times
27
+ end
28
+
29
+ sdr.close_device
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ lib = File.expand_path('../../lib', __FILE__)
3
+ $:.unshift lib unless $:.include? lib
4
+
5
+ require 'turtleshell'
6
+
7
+ puts "Found #{TurtleShell.count_of_devices} devices connected"
8
+
9
+ TurtleShell.all_devices.each_with_index do |device_name, index|
10
+ puts "#{index} : #{device_name}"
11
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ lib = File.expand_path('../../lib', __FILE__)
3
+ $:.unshift lib unless $:.include? lib
4
+
5
+ require 'turtleshell'
6
+
7
+ begin
8
+ sdr = TurtleShell::Device.new
9
+ rescue TurtleShell::DeviceNotFoundError
10
+ puts 'No compatible devices found. bailing!'
11
+ exit 1
12
+ end
13
+
14
+ puts "\n\nConfiguring SDR device\n"
15
+
16
+ sdr.sample_rate = 2.4e6
17
+ sdr.center_frequency = 100e6
18
+ sdr.gain = 10
19
+
20
+ puts "\t sample rate: #{sdr.sample_rate / 1e6} MHz"
21
+ puts "\t center frequency: #{sdr.center_frequency / 1e6} MHz"
22
+ puts "\t gain: #{sdr.gain} dB"
23
+
24
+ puts "\n\n"
25
+ puts "Reading Samples..."
26
+ samples = sdr.read_samples(1024)
27
+ print "\t signal mean: #{samples.inject(:+)/samples.length}"
28
+
29
+ sdr.close_device
@@ -0,0 +1,217 @@
1
+
2
+ SHELL = /bin/sh
3
+
4
+ # V=0 quiet, V=1 verbose. other values don't work.
5
+ V = 0
6
+ Q1 = $(V:1=)
7
+ Q = $(Q1:0=@)
8
+ n=$(NULLCMD)
9
+ ECHO1 = $(V:1=@$n)
10
+ ECHO = $(ECHO1:0=@echo)
11
+
12
+ #### Start of system configuration section. ####
13
+
14
+ srcdir = .
15
+ topdir = /Users/tjarratt/.rvm/rubies/ruby-1.9.3-p327/include/ruby-1.9.1
16
+ hdrdir = /Users/tjarratt/.rvm/rubies/ruby-1.9.3-p327/include/ruby-1.9.1
17
+ arch_hdrdir = /Users/tjarratt/.rvm/rubies/ruby-1.9.3-p327/include/ruby-1.9.1/$(arch)
18
+ VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
19
+ prefix = $(DESTDIR)/Users/tjarratt/.rvm/rubies/ruby-1.9.3-p327
20
+ rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
21
+ exec_prefix = $(prefix)
22
+ vendorhdrdir = $(rubyhdrdir)/vendor_ruby
23
+ sitehdrdir = $(rubyhdrdir)/site_ruby
24
+ rubyhdrdir = $(includedir)/$(RUBY_BASE_NAME)-$(ruby_version)
25
+ vendordir = $(rubylibprefix)/vendor_ruby
26
+ sitedir = $(rubylibprefix)/site_ruby
27
+ ridir = $(datarootdir)/$(RI_BASE_NAME)
28
+ mandir = $(datarootdir)/man
29
+ localedir = $(datarootdir)/locale
30
+ libdir = $(exec_prefix)/lib
31
+ psdir = $(docdir)
32
+ pdfdir = $(docdir)
33
+ dvidir = $(docdir)
34
+ htmldir = $(docdir)
35
+ infodir = $(datarootdir)/info
36
+ docdir = $(datarootdir)/doc/$(PACKAGE)
37
+ oldincludedir = $(DESTDIR)/usr/include
38
+ includedir = $(prefix)/include
39
+ localstatedir = $(prefix)/var
40
+ sharedstatedir = $(prefix)/com
41
+ sysconfdir = $(DESTDIR)/etc
42
+ datadir = $(datarootdir)
43
+ datarootdir = $(prefix)/share
44
+ libexecdir = $(exec_prefix)/libexec
45
+ sbindir = $(exec_prefix)/sbin
46
+ bindir = $(exec_prefix)/bin
47
+ rubylibdir = $(rubylibprefix)/$(ruby_version)
48
+ archdir = $(rubylibdir)/$(arch)
49
+ sitelibdir = $(sitedir)/$(ruby_version)
50
+ sitearchdir = $(sitelibdir)/$(sitearch)
51
+ vendorlibdir = $(vendordir)/$(ruby_version)
52
+ vendorarchdir = $(vendorlibdir)/$(sitearch)
53
+
54
+ NULLCMD = :
55
+
56
+ CC = /usr/bin/gcc-4.2
57
+ CXX = g++
58
+ LIBRUBY = $(LIBRUBY_A)
59
+ LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
60
+ LIBRUBYARG_SHARED =
61
+ LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
62
+ empty =
63
+ OUTFLAG = -o $(empty)
64
+ COUTFLAG = -o $(empty)
65
+
66
+ RUBY_EXTCONF_H =
67
+ cflags = $(optflags) $(debugflags) $(warnflags)
68
+ optflags = -O3
69
+ debugflags = -ggdb
70
+ warnflags = -Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wshorten-64-to-32 -Wimplicit-function-declaration
71
+ CFLAGS = -fno-common -I/Users/mpapis/.sm/pkg/active/include -pipe $(ARCH_FLAG)
72
+ INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
73
+ DEFS =
74
+ CPPFLAGS = -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE $(DEFS) $(cppflags)
75
+ CXXFLAGS = $(CFLAGS) $(cxxflags)
76
+ ldflags = -L. -Bstatic -L/Users/mpapis/.sm/pkg/active/lib
77
+ dldflags = -Wl,-undefined,dynamic_lookup -Wl,-multiply_defined,suppress -Wl,-flat_namespace
78
+ ARCH_FLAG = -arch x86_64
79
+ DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
80
+ LDSHARED = $(CC) -dynamic -bundle
81
+ LDSHAREDXX = $(CXX) -dynamic -bundle
82
+ AR = ar
83
+ EXEEXT =
84
+
85
+ RUBY_BASE_NAME = ruby
86
+ RUBY_INSTALL_NAME = ruby
87
+ RUBY_SO_NAME = ruby
88
+ arch = x86_64-darwin12.2.0
89
+ sitearch = $(arch)
90
+ ruby_version = 1.9.1
91
+ ruby = /Users/tjarratt/.rvm/rubies/ruby-1.9.3-p327/bin/ruby
92
+ RUBY = $(ruby)
93
+ RM = rm -f
94
+ RM_RF = $(RUBY) -run -e rm -- -rf
95
+ RMDIRS = rmdir -p
96
+ MAKEDIRS = mkdir -p
97
+ INSTALL = /usr/bin/install -c
98
+ INSTALL_PROG = $(INSTALL) -m 0755
99
+ INSTALL_DATA = $(INSTALL) -m 644
100
+ COPY = cp
101
+ TOUCH = exit >
102
+
103
+ #### End of system configuration section. ####
104
+
105
+ preload =
106
+
107
+ libpath = . $(libdir)
108
+ LIBPATH = -L. -L$(libdir)
109
+ DEFFILE =
110
+
111
+ CLEANFILES = mkmf.log
112
+ DISTCLEANFILES =
113
+ DISTCLEANDIRS =
114
+
115
+ extout =
116
+ extout_prefix =
117
+ target_prefix =
118
+ LOCAL_LIBS =
119
+ LIBS = -lpthread -ldl -lobjc
120
+ SRCS = librtlsdr.c
121
+ OBJS = librtlsdr.o
122
+ TARGET = librtlsdr
123
+ DLLIB = $(TARGET).bundle
124
+ EXTSTATIC =
125
+ STATIC_LIB =
126
+
127
+ BINDIR = $(bindir)
128
+ RUBYCOMMONDIR = $(sitedir)$(target_prefix)
129
+ RUBYLIBDIR = $(sitelibdir)$(target_prefix)
130
+ RUBYARCHDIR = $(sitearchdir)$(target_prefix)
131
+ HDRDIR = $(rubyhdrdir)/ruby$(target_prefix)
132
+ ARCHHDRDIR = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)
133
+
134
+ TARGET_SO = $(DLLIB)
135
+ CLEANLIBS = $(TARGET).bundle
136
+ CLEANOBJS = *.o *.bak
137
+
138
+ all: $(DLLIB)
139
+ static: $(STATIC_LIB)
140
+ .PHONY: all install static install-so install-rb
141
+ .PHONY: clean clean-so clean-rb
142
+
143
+ clean-rb-default::
144
+ clean-rb::
145
+ clean-so::
146
+ clean: clean-so clean-rb-default clean-rb
147
+ @-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)
148
+
149
+ distclean-rb-default::
150
+ distclean-rb::
151
+ distclean-so::
152
+ distclean: clean distclean-so distclean-rb-default distclean-rb
153
+ @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
154
+ @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
155
+ @-$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
156
+
157
+ realclean: distclean
158
+ install: install-so install-rb
159
+
160
+ install-so: $(RUBYARCHDIR)/$(DLLIB)
161
+ $(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
162
+ -$(Q)$(MAKEDIRS) $(@D)
163
+ $(INSTALL_PROG) $(DLLIB) $(@D)
164
+ clean-static::
165
+ -$(Q)$(RM) $(STATIC_LIB)
166
+ install-rb: pre-install-rb install-rb-default
167
+ install-rb-default: pre-install-rb-default
168
+ pre-install-rb: Makefile
169
+ pre-install-rb-default: Makefile
170
+ pre-install-rb-default:
171
+ $(ECHO) installing default librtlsdr libraries
172
+ ./.RUBYARCHDIR.time:
173
+ $(Q) $(MAKEDIRS) $(RUBYARCHDIR)
174
+ $(Q) $(TOUCH) $@
175
+
176
+ site-install: site-install-so site-install-rb
177
+ site-install-so: install-so
178
+ site-install-rb: install-rb
179
+
180
+ .SUFFIXES: .c .m .cc .mm .cxx .cpp .C .o
181
+
182
+ .cc.o:
183
+ $(ECHO) compiling $(<)
184
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
185
+
186
+ .mm.o:
187
+ $(ECHO) compiling $(<)
188
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
189
+
190
+ .cxx.o:
191
+ $(ECHO) compiling $(<)
192
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
193
+
194
+ .cpp.o:
195
+ $(ECHO) compiling $(<)
196
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
197
+
198
+ .C.o:
199
+ $(ECHO) compiling $(<)
200
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
201
+
202
+ .c.o:
203
+ $(ECHO) compiling $(<)
204
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
205
+
206
+ .m.o:
207
+ $(ECHO) compiling $(<)
208
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
209
+
210
+ $(DLLIB): $(OBJS) Makefile
211
+ $(ECHO) linking shared-object $(DLLIB)
212
+ -$(Q)$(RM) $(@)
213
+ $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
214
+
215
+
216
+
217
+ $(OBJS): $(hdrdir)/ruby.h $(hdrdir)/ruby/defines.h $(arch_hdrdir)/ruby/config.h
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require 'mkmf'
3
+
4
+ have_library('rtlsdr')
5
+ extension_name = 'librtlsdr'
6
+
7
+ dir_config('librtlsdr')
8
+ create_makefile('librtlsdr/librtlsdr')
@@ -0,0 +1,233 @@
1
+ #include "ruby.h"
2
+ #include "rtl-sdr.h"
3
+
4
+ VALUE m_turtleshell = Qnil;
5
+ VALUE c_device;
6
+ static VALUE m_rtlsdr;
7
+
8
+ static VALUE turtleshell_count() {
9
+ return INT2NUM(rtlsdr_get_device_count());
10
+ }
11
+
12
+ static VALUE turtleshell_first_device() {
13
+ rtlsdr_dev_t *device = NULL;
14
+ VALUE wrapped_device;
15
+ VALUE hash;
16
+ int count = rtlsdr_get_device_count();
17
+
18
+ // ensure we have at least one device
19
+ if (!count) { return Qnil; }
20
+
21
+ rtlsdr_open(&device, 0);
22
+ wrapped_device = Data_Wrap_Struct(c_device, NULL, NULL, device);
23
+
24
+ hash = rb_hash_new();
25
+ rb_hash_aset(hash, rb_str_new2("name"), rb_str_new2(rtlsdr_get_device_name(0)));
26
+ rb_hash_aset(hash, rb_str_new2("device_handle"), wrapped_device);
27
+
28
+ return hash;
29
+ }
30
+
31
+ static VALUE turtleshell_all_devices() {
32
+ int i, count;
33
+ VALUE devices_array = rb_ary_new();
34
+ count = rtlsdr_get_device_count();
35
+
36
+ for (i = 0; i < count; ++i) {
37
+ rb_ary_push(devices_array, rb_str_new2(rtlsdr_get_device_name(i)));
38
+ }
39
+
40
+ return devices_array;
41
+ }
42
+
43
+ static VALUE turtleshell_nth_device(VALUE self, VALUE n) {
44
+ int open_success;
45
+ uint32_t int_n = NUM2UINT(n);
46
+ rtlsdr_dev_t *device = NULL;
47
+ VALUE wrapped_device, hash = rb_hash_new();
48
+ uint32_t device_count = (uint32_t)rtlsdr_get_device_count();
49
+
50
+ if (int_n >= device_count) { return Qnil; }
51
+
52
+ open_success = rtlsdr_open(&device, int_n);
53
+ if (open_success != 0) {
54
+ return Qnil;
55
+ }
56
+
57
+ wrapped_device = Data_Wrap_Struct(c_device, NULL, NULL, device);
58
+
59
+ rb_hash_aset(hash, ID2SYM(rb_intern("name")), rb_str_new2(rtlsdr_get_device_name(0)));
60
+ rb_hash_aset(hash, ID2SYM(rb_intern("device")), wrapped_device);
61
+
62
+ return hash;
63
+ }
64
+
65
+ static VALUE turtleshell_close(VALUE self, VALUE device) {
66
+ rtlsdr_dev_t *dev = NULL;
67
+ Data_Get_Struct(device, rtlsdr_dev_t, dev);
68
+ rtlsdr_close(dev);
69
+ return Qnil;
70
+ }
71
+
72
+ static VALUE turtleshell_read_synchronous(VALUE self,
73
+ VALUE device,
74
+ VALUE bytes_to_read) {
75
+ int success, i;
76
+ int bytes_read;
77
+ VALUE buffer = rb_ary_new();
78
+ int length = NUM2INT(bytes_to_read);
79
+ uint8_t *p_buffer = malloc(sizeof(uint8_t) * bytes_to_read);
80
+ rtlsdr_dev_t *p_device;
81
+
82
+ Data_Get_Struct(device, rtlsdr_dev_t, p_device);
83
+
84
+ rtlsdr_reset_buffer(p_device);
85
+ success = rtlsdr_read_sync(p_device, p_buffer, length, &bytes_read);
86
+ if (success != 0) {
87
+ printf("error reading bytes. read_sync returned %d\n", success);
88
+ free(p_buffer);
89
+ return buffer;
90
+ }
91
+
92
+ for (i = 0; i < bytes_read; ++i) {
93
+ rb_ary_push(buffer, UINT2NUM((uint8_t)p_buffer[i]));
94
+ }
95
+
96
+ free(p_buffer);
97
+ return buffer;
98
+ }
99
+
100
+ struct turtleshell_context {
101
+ VALUE callback;
102
+ rtlsdr_dev_t *device;
103
+ };
104
+
105
+ typedef struct turtleshell_context turtleshell_context;
106
+
107
+ static void turtleshell_callback(unsigned char *buffer, uint32_t length, void *context) {
108
+ uint32_t i;
109
+ rtlsdr_dev_t *device;
110
+ VALUE buffer_array, callback, bool_value;
111
+ struct turtleshell_context unwrapped_context;
112
+
113
+ unwrapped_context = *(struct turtleshell_context *)context;
114
+
115
+ if (!unwrapped_context.callback || !unwrapped_context.device) {
116
+ printf("unexpected error: could not read callback / device from unwrapped context\n");
117
+ exit(1);
118
+ }
119
+
120
+ device = unwrapped_context.device;
121
+ callback = unwrapped_context.callback;
122
+
123
+ buffer_array = rb_ary_new();
124
+ for (i = 0; i < length; ++i) {
125
+ rb_ary_push(buffer_array, UINT2NUM((uint8_t)buffer[i]));
126
+ }
127
+
128
+ // TODO: do we need to free(buffer) or context ?
129
+ bool_value = RTEST(rb_funcall(callback, rb_intern("call"), 1, buffer_array));
130
+
131
+ if (!bool_value) {
132
+ rtlsdr_cancel_async(device);
133
+ }
134
+ }
135
+
136
+ static VALUE turtleshell_read_asynchronous(VALUE self,
137
+ VALUE device,
138
+ VALUE bytes_to_read,
139
+ VALUE callback) {
140
+ int success, length = NUM2INT(bytes_to_read);
141
+ rtlsdr_dev_t *p_device;
142
+ struct turtleshell_context context;
143
+ void *wrapped_context;
144
+
145
+ Data_Get_Struct(device, rtlsdr_dev_t, p_device);
146
+
147
+ context.device = p_device;
148
+ context.callback = callback;
149
+ wrapped_context = (void *)&context;
150
+
151
+ rtlsdr_reset_buffer(p_device);
152
+ success = rtlsdr_read_async(p_device, turtleshell_callback, wrapped_context, 0, length);
153
+
154
+ if (success != 0) {
155
+ printf("error reading async: return code %d\n", success);
156
+ }
157
+
158
+ return Qnil;
159
+ }
160
+
161
+ static VALUE turtleshell_get_sample_rate(VALUE self, VALUE wrapped_device) {
162
+ rtlsdr_dev_t *device;
163
+ Data_Get_Struct(wrapped_device, rtlsdr_dev_t, device);
164
+ return UINT2NUM(rtlsdr_get_sample_rate(device));
165
+ }
166
+
167
+ static VALUE turtleshell_set_sample_rate(VALUE self,
168
+ VALUE wrapped_device,
169
+ VALUE value) {
170
+ uint32_t rate = NUM2UINT(value);
171
+ rtlsdr_dev_t *device;
172
+ Data_Get_Struct(wrapped_device, rtlsdr_dev_t, device);
173
+ rtlsdr_set_sample_rate(device, rate);
174
+
175
+ return Qnil;
176
+ }
177
+
178
+ static VALUE turtleshell_get_center_frequency(VALUE self, VALUE wrapped_device) {
179
+ rtlsdr_dev_t *device;
180
+ Data_Get_Struct(wrapped_device, rtlsdr_dev_t, device);
181
+ return UINT2NUM(rtlsdr_get_center_freq(device));
182
+ }
183
+
184
+ static VALUE turtleshell_set_center_frequency(VALUE self,
185
+ VALUE wrapped_device,
186
+ VALUE value) {
187
+ uint32_t freq = NUM2UINT(value);
188
+ rtlsdr_dev_t *device;
189
+ Data_Get_Struct(wrapped_device, rtlsdr_dev_t, device);
190
+ rtlsdr_set_center_freq(device, freq);
191
+ return Qnil;
192
+ }
193
+
194
+ static VALUE turtleshell_get_gain(VALUE self, VALUE wrapped_device) {
195
+ rtlsdr_dev_t *device;
196
+ Data_Get_Struct(wrapped_device, rtlsdr_dev_t, device);
197
+ return INT2NUM(rtlsdr_get_tuner_gain(device));
198
+ }
199
+
200
+ static VALUE turtleshell_set_gain(VALUE self, VALUE wrapped_device, VALUE value) {
201
+ int gain = NUM2INT(value);
202
+ rtlsdr_dev_t *device;
203
+ Data_Get_Struct(wrapped_device, rtlsdr_dev_t, device);
204
+ rtlsdr_set_tuner_gain(device, gain);
205
+ return Qnil;
206
+ }
207
+
208
+ void Init_librtlsdr() {
209
+ m_turtleshell = rb_define_module("TurtleShell");
210
+ m_rtlsdr = rb_define_module_under(m_turtleshell, "RTLSDR");
211
+ c_device = rb_define_class_under(m_rtlsdr, "Device", rb_cObject);
212
+
213
+ // count of devices
214
+ rb_define_module_function(m_rtlsdr, "count", turtleshell_count, 0);
215
+
216
+ // life cycle of devices
217
+ rb_define_module_function(m_rtlsdr, "first_device", turtleshell_first_device, 0);
218
+ rb_define_module_function(m_rtlsdr, "all_devices", turtleshell_all_devices, 0);
219
+ rb_define_module_function(m_rtlsdr, "nth_device", turtleshell_nth_device, 1);
220
+ rb_define_module_function(m_rtlsdr, "close_device", turtleshell_close, 1);
221
+
222
+ // reading bytes
223
+ rb_define_module_function(m_rtlsdr, "read_sync", turtleshell_read_synchronous, 2);
224
+ rb_define_module_function(m_rtlsdr, "read_async", turtleshell_read_asynchronous, 3);
225
+
226
+ // getters and setters
227
+ rb_define_module_function(m_rtlsdr, "get_sample_rate", turtleshell_get_sample_rate, 1);
228
+ rb_define_module_function(m_rtlsdr, "set_sample_rate", turtleshell_set_sample_rate, 2);
229
+ rb_define_module_function(m_rtlsdr, "get_center_freq", turtleshell_get_center_frequency, 1);
230
+ rb_define_module_function(m_rtlsdr, "set_center_freq", turtleshell_set_center_frequency, 2);
231
+ rb_define_module_function(m_rtlsdr, "get_gain", turtleshell_get_gain, 1);
232
+ rb_define_module_function(m_rtlsdr, "set_gain", turtleshell_set_gain, 2);
233
+ }
@@ -0,0 +1,22 @@
1
+ module TurtleShell
2
+ end
3
+
4
+ Dir.entries(Dir.pwd).select do |f|
5
+ File.directory?(f) && f[0] != '.'
6
+ end.each do |dir|
7
+ lib = File.expand_path("../#{dir}", __FILE__)
8
+ $:.unshift lib unless $:.include? lib
9
+ end
10
+
11
+ require 'librtlsdr/librtlsdr'
12
+ require 'turtleshell/device'
13
+
14
+ module TurtleShell
15
+ def self.all_devices
16
+ TurtleShell::RTLSDR.all_devices
17
+ end
18
+
19
+ def self.count_of_devices
20
+ TurtleShell::RTLSDR.count
21
+ end
22
+ end
@@ -0,0 +1,104 @@
1
+ module TurtleShell
2
+
3
+ class DeviceNotFoundError < Exception; end
4
+
5
+ class Device
6
+ attr_reader :name
7
+
8
+ def initialize(n = 0)
9
+ raise ArgumentError.new('TurtleShell::Device.new expects a number') unless n.is_a? Fixnum
10
+
11
+ unless raw_device_attrs = TurtleShell::RTLSDR.nth_device(n)
12
+ raise TurtleShell::DeviceNotFoundError
13
+ end
14
+
15
+ @name = raw_device_attrs[:name]
16
+ @device = raw_device_attrs[:device]
17
+ end
18
+
19
+ def self.device_with_name(name)
20
+ unless index = TurtleShell.all_devices.index(name)
21
+ raise ArgumentError.new('No device with that name')
22
+ end
23
+
24
+ new(index)
25
+ end
26
+
27
+ def sample_rate
28
+ TurtleShell::RTLSDR.get_sample_rate(@device)
29
+ end
30
+
31
+ def sample_rate=(rate)
32
+ TurtleShell::RTLSDR.set_sample_rate(@device, rate)
33
+ end
34
+
35
+ def center_frequency
36
+ TurtleShell::RTLSDR.get_center_freq(@device)
37
+ end
38
+
39
+ def center_frequency=(freq)
40
+ TurtleShell::RTLSDR.set_center_freq(@device, freq)
41
+ end
42
+
43
+ def gain
44
+ TurtleShell::RTLSDR.get_gain(@device)
45
+ end
46
+
47
+ def gain=(gain)
48
+ TurtleShell::RTLSDR.set_gain(@device, gain)
49
+ end
50
+
51
+ # read specified number of complex samples from tuner
52
+ # real and imaginary parts are normalized between [-1, 1]
53
+ def read_samples(number_of_samples = 1024)
54
+ number_of_bytes = 2 * number_of_samples
55
+ raw_data = read_bytes(number_of_bytes)
56
+ packed_bytes_to_complex(raw_data)
57
+ end
58
+
59
+ # read specified number of complex samples from tuner into a block
60
+ # real and imaginary parts are normalized between [-1, 1]
61
+ def read_samples_async(number_of_samples = 1024, &block)
62
+ block = proc { } unless block_given?
63
+ wrapped_block = proc do |raw_data|
64
+ block.call(packed_bytes_to_complex(raw_data))
65
+ end
66
+
67
+ number_of_bytes_to_read = 2 * number_of_samples
68
+
69
+ begin
70
+ TurtleShell::RTLSDR.read_async(@device, number_of_bytes_to_read, wrapped_block)
71
+ rescue SignalException => e
72
+ puts 'Caught signal, cancelling async callback.'
73
+ TurtleShell::RTLSDR.end_async(@device)
74
+ raise e
75
+ end
76
+ end
77
+
78
+ def close_device
79
+ unless @device.nil?
80
+ TurtleShell::RTLSDR.close_device(@device)
81
+ end
82
+ end
83
+
84
+ private
85
+ def read_bytes(bytes_to_read)
86
+ buffer = TurtleShell::RTLSDR.read_sync(@device, bytes_to_read)
87
+
88
+ if buffer.size != bytes_to_read
89
+ close_device
90
+ raise IOError.new("Error reading from device. Requested #{bytes_to_read} but got #{buffer.size} back")
91
+ end
92
+
93
+ buffer
94
+ end
95
+
96
+ # converts an array of bytes to a list of complex numbers
97
+ # and normalizes them within the range [-1, 1]
98
+ def packed_bytes_to_complex(bytes)
99
+ bytes.each_slice(2).map do |i, r|
100
+ Complex(i / 127.5 - 1, r / 127.5 - 1)
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,184 @@
1
+ require 'turtleshell'
2
+
3
+ describe 'TurtleShell:Device behavior' do
4
+ it 'should return the name' do
5
+ TurtleShell::RTLSDR.
6
+ should_receive(:nth_device).
7
+ and_return({:name => 'slippery-slope'})
8
+
9
+ expect(TurtleShell::Device.new.name).to eq('slippery-slope')
10
+ end
11
+ end
12
+
13
+ describe 'creating devices' do
14
+ describe 'finding a device by index' do
15
+ it 'should create a new device' do
16
+ TurtleShell::RTLSDR.
17
+ should_receive(:nth_device).
18
+ with(750).
19
+ and_return({:name => 'ill-sorted-limbic-system'})
20
+
21
+ expect(TurtleShell::Device.new(750)).to be_an_instance_of TurtleShell::Device
22
+ end
23
+ end
24
+
25
+ describe 'finding a device by name' do
26
+ before do
27
+ TurtleShell.
28
+ should_receive(:all_devices).
29
+ and_return(['electro-positive-self-activity', 'dry-shod-leze-majesty'])
30
+ end
31
+
32
+ it 'should create a new device' do
33
+ TurtleShell::RTLSDR.
34
+ should_receive(:nth_device).
35
+ with(1).
36
+ and_return({:name => 'dry-shod-leze-majesty'})
37
+
38
+ expect(TurtleShell::Device.device_with_name('dry-shod-leze-majesty')).
39
+ to be_an_instance_of TurtleShell::Device
40
+ end
41
+
42
+ it 'should raise an ArgumentError when there is matching device' do
43
+ expect { TurtleShell::Device.device_with_name('clincher-built-parity-check') }.
44
+ to raise_error(ArgumentError, 'No device with that name')
45
+ end
46
+ end
47
+
48
+ describe 'creating a new device' do
49
+ it 'should create a new device' do
50
+ TurtleShell::RTLSDR.
51
+ should_receive(:nth_device).
52
+ with(437).
53
+ and_return({:name => 'self-applying-cat-silver'})
54
+
55
+ expect(TurtleShell::Device.new(437)).to be_an_instance_of TurtleShell::Device
56
+ end
57
+
58
+ it 'should raise an error if the first argument is not an integer' do
59
+ expect { TurtleShell::Device.new('a') }.
60
+ to raise_error ArgumentError, 'TurtleShell::Device.new expects a number'
61
+
62
+ expect { TurtleShell::Device.new(nil) }.
63
+ to raise_error ArgumentError, 'TurtleShell::Device.new expects a number'
64
+ end
65
+ end
66
+ end
67
+
68
+ def create_mock_device
69
+ TurtleShell::RTLSDR.
70
+ should_receive(:nth_device).
71
+ with(0).
72
+ and_return({
73
+ :name => 'self-applying-cat-silver',
74
+ :sample_rate => 666,
75
+ :center_frequency => 70e6,
76
+ :gain => :auto,
77
+ })
78
+
79
+ TurtleShell::Device.new(0)
80
+ end
81
+
82
+ describe 'device attribues' do
83
+ describe 'getting and setting the sample rate' do
84
+ describe 'getting the value' do
85
+ before do
86
+ @device = create_mock_device
87
+ TurtleShell::RTLSDR.
88
+ should_receive(:get_sample_rate).
89
+ and_return(666)
90
+ end
91
+
92
+ it 'gets the value' do
93
+ expect(@device.sample_rate).to eq(666)
94
+ end
95
+ end
96
+
97
+ describe 'setting the value' do
98
+ before do
99
+ @device = create_mock_device
100
+ TurtleShell::RTLSDR.
101
+ should_receive(:set_sample_rate)
102
+ .with(nil, 12) ### mocked out device struct
103
+
104
+ TurtleShell::RTLSDR.
105
+ should_receive(:get_sample_rate)
106
+ .and_return(12)
107
+ end
108
+
109
+ it 'sets the value' do
110
+ @device.sample_rate = 12
111
+ expect(@device.sample_rate).to eq(12)
112
+ end
113
+ end
114
+ end
115
+
116
+ describe 'getting and setting the center frequency' do
117
+ describe 'getting the value' do
118
+ before do
119
+ @device = create_mock_device
120
+
121
+ TurtleShell::RTLSDR.
122
+ should_receive(:get_center_freq)
123
+ .and_return(70e6)
124
+
125
+ it 'gets the value' do
126
+ expect(@device.center_frequency).to eq(70e6)
127
+ end
128
+ end
129
+ end
130
+
131
+ describe 'setting the value' do
132
+ before do
133
+ @device = create_mock_device
134
+
135
+ TurtleShell::RTLSDR.
136
+ should_receive(:set_center_freq).
137
+ with(nil, 12e12)
138
+
139
+ TurtleShell::RTLSDR.
140
+ should_receive(:get_center_freq).
141
+ and_return(12e12)
142
+ end
143
+
144
+ it 'sets the value' do
145
+ @device.center_frequency = 12e12
146
+ expect(@device.center_frequency).to eq(12e12)
147
+ end
148
+ end
149
+ end
150
+
151
+ describe 'getting and setting the gain' do
152
+ describe 'getting the value' do
153
+ before do
154
+ @device = create_mock_device
155
+ TurtleShell::RTLSDR.
156
+ should_receive(:get_gain).
157
+ and_return(:auto)
158
+ end
159
+
160
+ it 'gets the gain' do
161
+ expect(@device.gain).to eq(:auto)
162
+ end
163
+ end
164
+
165
+ describe 'setting the value' do
166
+ before do
167
+ @device = create_mock_device
168
+
169
+ TurtleShell::RTLSDR.
170
+ should_receive(:set_gain).
171
+ with(nil, 4) # xxx mocked out device
172
+
173
+ TurtleShell::RTLSDR.
174
+ should_receive(:get_gain).
175
+ and_return(4)
176
+ end
177
+
178
+ it 'sets the value' do
179
+ @device.gain = 4
180
+ expect(@device.gain).to eq(4)
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,22 @@
1
+ require 'turtleshell'
2
+
3
+ describe 'TurtleShell' do
4
+ it 'tells you how many devices are connected' do
5
+ TurtleShell::RTLSDR.
6
+ should_receive(:count).
7
+ and_return(886)
8
+
9
+ expect(TurtleShell.count_of_devices).to eq(886)
10
+ end
11
+
12
+ it 'tells you which devices are at which index' do
13
+ TurtleShell::RTLSDR.
14
+ should_receive(:all_devices).
15
+ and_return(['guilt-ridden-humpback-whale', 'four-ply-self-repellency', 'lute-backed-cross-stone'])
16
+
17
+ names = TurtleShell.all_devices
18
+ expect(names[0]).to eq('guilt-ridden-humpback-whale')
19
+ expect(names[1]).to eq('four-ply-self-repellency')
20
+ expect(names[2]).to eq('lute-backed-cross-stone')
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'turtleshell'
3
+ s.version = '1.0.4'
4
+ s.date = '2013-11-05'
5
+ s.summary = 'A ruby wrapper for librtlsdr'
6
+ s.description = 'turtleshell is an interface for realtek USB software defined radio devices'
7
+ s.authors = ['Tim Jarratt']
8
+ s.email = 'tjarratt@gmail.com'
9
+ s.files = ['lib/turtleshell.rb']
10
+ s.homepage = 'http://github.com/tjarratt/turtleshell'
11
+ s.license = 'MIT'
12
+ s.require_path = 'lib'
13
+
14
+ s.extensions = ['ext/librtlsdr/extconf.rb']
15
+
16
+ s.add_dependency 'rake', '~> 10.1'
17
+ s.add_dependency 'rake-compiler', '~> 0.9'
18
+ s.add_development_dependency 'rspec', '~> 2.14'
19
+
20
+ ignores = File.readlines('.gitignore').grep(/\S+/).map(&:chomp)
21
+ dotfiles = %w[.gitignore]
22
+
23
+ all_files_without_ignores = Dir['**/*'].reject { |f|
24
+ File.directory?(f) || ignores.any? { |i| File.fnmatch(i, f) }
25
+ }
26
+
27
+ s.files = (all_files_without_ignores + dotfiles).sort
28
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: turtleshell
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Tim Jarratt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '10.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '10.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake-compiler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '0.9'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '0.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.14'
55
+ description: turtleshell is an interface for realtek USB software defined radio devices
56
+ email: tjarratt@gmail.com
57
+ executables: []
58
+ extensions:
59
+ - ext/librtlsdr/extconf.rb
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - CHANGELOG.md
64
+ - Gemfile
65
+ - LICENSE
66
+ - README.md
67
+ - Rakefile
68
+ - examples/async_read_from_device.rb
69
+ - examples/listing_devices.rb
70
+ - examples/simple_read_from_device.rb
71
+ - ext/librtlsdr/Makefile
72
+ - ext/librtlsdr/extconf.rb
73
+ - ext/librtlsdr/librtlsdr.c
74
+ - lib/turtleshell.rb
75
+ - lib/turtleshell/device.rb
76
+ - spec/device_spec.rb
77
+ - spec/turtleshell_spec.rb
78
+ - tmp/x86_64-darwin13.0.0/librtlsdr/2.0.0/Makefile
79
+ - tmp/x86_64-darwin13.0.0/librtlsdr/2.0.0/mkmf.log
80
+ - turtleshell.gemspec
81
+ homepage: http://github.com/tjarratt/turtleshell
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.1.11
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: A ruby wrapper for librtlsdr
105
+ test_files: []