turtleshell 1.0.4

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.
@@ -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: []