radio 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.
@@ -0,0 +1,21 @@
1
+ <% if xhr?
2
+ $rig.lo = params['freq'].to_f
3
+ @response.body = [$rig.lo.to_s]
4
+ @response.headers['Content-Type'] = 'text/plain'
5
+ end -%>
6
+ <form action="lo" id="freqForm">
7
+ <input type="text" name="freq" value="<%= $rig.lo %>" %>
8
+ </form>
9
+ <script type="text/javascript">
10
+ $("#freqForm").submit(function(event) {
11
+ event.preventDefault();
12
+ var $form = $( this ),
13
+ term = $form.find( 'input[name="freq"]' ).val(),
14
+ url = $form.attr( 'action' );
15
+ $.post( url, { freq: term },
16
+ function( data ) {
17
+ $form.find( 'input[name="freq"]' ).val(data)
18
+ }
19
+ );
20
+ });
21
+ </script>
@@ -0,0 +1,64 @@
1
+ <%
2
+ if post?
3
+ $rig ||= Radio::Rig.new
4
+ channel_i, channel_q = case params['channel']
5
+ when 'iq' then [0,1]
6
+ when 'qi' then [1,0]
7
+ else [0,nil]
8
+ end
9
+ $rig.rx = Radio::Input.new params['type'], params['id'], params['rate'].to_i, channel_i, channel_q
10
+ response.redirect '/'
11
+ end
12
+ unless response.redirection? -%>
13
+ <!DOCTYPE html>
14
+ <html>
15
+ <head>
16
+ <style type="text/css" media="screen">
17
+ body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}
18
+ select,input,button,textarea,button{font:99% arial,helvetica,clean,sans-serif;}
19
+ </style>
20
+ </head>
21
+ <body>
22
+
23
+ <h2>Input Sources</h2>
24
+ <% if (input_sources = Radio::Input.sources).empty? -%>
25
+ No input sources found.
26
+ <% else -%>
27
+ <dl>
28
+ <% input_sources.each do |k, opts| -%>
29
+ <form action='?' method="post">
30
+ <% input_type, input_id = k -%>
31
+ <dt>
32
+ <input type="submit" value="Select"%>
33
+ <input type="hidden" name="type" value="<%=h input_type %>" %>
34
+ <input type="hidden" name="id" value="<%=h input_id %>" %>
35
+ <%=h input_type %>: <%=h opts[:name] %>
36
+ </dt>
37
+ <dd>
38
+ <% opts[:rates].reverse.each_with_index do |rate, index| %>
39
+ <input type="radio" name="rate" value="<%=h rate %>" <%= "checked=checked" if index==0 %> /><%=h rate %>
40
+ <% end -%>
41
+ </dd>
42
+ <dd>
43
+ <% if opts[:channels] == 1 or input_type != :File %>
44
+ <input type="radio" name="channel" value="i" checked="checked" />LPCM
45
+ <% end -%>
46
+ <% if opts[:channels] == 2 %>
47
+ <input type="radio" name="channel" value="iq" />I/Q
48
+ <input type="radio" name="channel" value="qi" checked="checked" />Q/I
49
+ <% end -%>
50
+ </dd>
51
+ </form>
52
+ <br />
53
+ <% end -%>
54
+ </dl>
55
+ <% end -%>
56
+
57
+ <h2>Driver Status</h2>
58
+ <% Radio::Input.status.each do |k, v| -%>
59
+ <%=h k %>: <%=h v %> <br />
60
+ <% end -%>
61
+
62
+ </body>
63
+ </html>
64
+ <% end -%>
@@ -0,0 +1,36 @@
1
+ <%
2
+ if post?
3
+ $rig.lo = case params['type']
4
+ when 'softrock' then Radio::Controls::Si570AVR.new
5
+ else Radio::Controls::Null.new
6
+ end
7
+ response.redirect '/'
8
+ end
9
+ unless response.redirection? -%>
10
+ <!DOCTYPE html>
11
+ <html>
12
+ <head>
13
+ <style type="text/css" media="screen">
14
+ body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}
15
+ select,input,button,textarea,button{font:99% arial,helvetica,clean,sans-serif;}
16
+ </style>
17
+ </head>
18
+ <body>
19
+
20
+ <h2>Local Oscillator Control</h2>
21
+ <form action='?' method="post">
22
+ <input type="submit" value="Select"%>
23
+ <input type="hidden" name="type" value="null" %>
24
+ No control.
25
+ </form>
26
+ <br />
27
+ <form action='?' method="post">
28
+ <input type="submit" value="Select"%>
29
+ <input type="hidden" name="type" value="softrock" %>
30
+ SoftRock Si570 (DG8SAQ/PE0FKO)
31
+ </form>
32
+ <br />
33
+
34
+ </body>
35
+ </html>
36
+ <% end -%>
@@ -0,0 +1,20 @@
1
+ <%
2
+ $waterfall_gradient ||= (0..127).collect do |i|
3
+ r = g = b = Math.log((i+16)**6.35)*18-312
4
+ redshift = i - 64
5
+ if redshift > 0
6
+ b = g -= redshift * 3
7
+ end
8
+ b *= 0.95
9
+ [r,g,b]
10
+ end
11
+ spectrum = case
12
+ when $rig.rate <= 24000 then $rig.spectrum 1024, 1.0
13
+ when $rig.rate <= 48000 then $rig.spectrum 2048, 2.0
14
+ when $rig.rate <= 96000 then $rig.spectrum 2048, 4.0
15
+ else $rig.spectrum 2048, 8.0
16
+ end
17
+ @response.body = [Radio::Gif.waterfall($waterfall_gradient, [spectrum])]
18
+ @response.headers['Content-Type'] = 'image/gif'
19
+ @response.headers["Cache-Control"] = 'max-age=0, private, must-revalidate'
20
+ %>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: radio
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,24 +9,88 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-11 00:00:00.000000000Z
13
- dependencies: []
12
+ date: 2012-02-14 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fftw3
16
+ requirement: &2198866920 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2198866920
25
+ - !ruby/object:Gem::Dependency
26
+ name: thin
27
+ requirement: &2198866220 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2198866220
36
+ - !ruby/object:Gem::Dependency
37
+ name: libusb
38
+ requirement: &2198865360 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2198865360
14
47
  description:
15
48
  email:
16
49
  - dturnbull@gmail.com
17
- executables: []
50
+ executables:
51
+ - radio-server
18
52
  extensions: []
19
53
  extra_rdoc_files: []
20
54
  files:
55
+ - bin/radio-server
56
+ - lib/radio/controls/null.rb
57
+ - lib/radio/controls/si570avr.rb
58
+ - lib/radio/filter.rb
59
+ - lib/radio/filters/fir.rb
60
+ - lib/radio/gif.rb
61
+ - lib/radio/http/file.rb
62
+ - lib/radio/http/script.rb
63
+ - lib/radio/http/server.rb
64
+ - lib/radio/http/session.rb
65
+ - lib/radio/input.rb
66
+ - lib/radio/inputs/alsa.rb
67
+ - lib/radio/inputs/coreaudio.rb
68
+ - lib/radio/inputs/file.rb
69
+ - lib/radio/inputs/wav.rb
21
70
  - lib/radio/psk31/bit_detect.rb
22
71
  - lib/radio/psk31/decoder.rb
23
- - lib/radio/psk31/filters.rb
24
72
  - lib/radio/psk31/fir_coef.rb
25
73
  - lib/radio/psk31/rx.rb
26
74
  - lib/radio/psk31/varicode.rb
75
+ - lib/radio/rig/lo.rb
76
+ - lib/radio/rig/rx.rb
77
+ - lib/radio/rig/spectrum.rb
78
+ - lib/radio/rig.rb
79
+ - lib/radio/version.rb
27
80
  - lib/radio.rb
81
+ - www/index.erb
82
+ - www/jquery-1.7.js
83
+ - www/lo.erb
84
+ - www/setup/input.erb
85
+ - www/setup/lo.erb
86
+ - www/waterfall.erb
28
87
  - README.md
29
- homepage: https://github.com/dturnbull/radio
88
+ - LICENSE
89
+ - test/test.rb
90
+ - test/wav/bpsk8k.wav
91
+ - test/wav/qpsk8k.wav
92
+ - test/wav/ssb.wav
93
+ homepage: https://github.com/ham21/radio
30
94
  licenses: []
31
95
  post_install_message:
32
96
  rdoc_options: []
@@ -46,9 +110,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
46
110
  version: '0'
47
111
  requirements: []
48
112
  rubyforge_project:
49
- rubygems_version: 1.8.6
113
+ rubygems_version: 1.8.10
50
114
  signing_key:
51
115
  specification_version: 3
52
- summary: Amateur radio software
53
- test_files: []
116
+ summary: The Twenty-First Century Amateur Radio Project
117
+ test_files:
118
+ - test/test.rb
119
+ - test/wav/bpsk8k.wav
120
+ - test/wav/qpsk8k.wav
121
+ - test/wav/ssb.wav
54
122
  has_rdoc:
@@ -1,220 +0,0 @@
1
- class Radio
2
-
3
- class PSK31
4
-
5
- # An instance of Filters handles everything from 48kHz to 500Hz.
6
- # PSK31/PSK63/PSK125 emit at 500Hz/1000Hz/2000Hz respectively.
7
-
8
- class Filters
9
-
10
- attr_accessor :frequency
11
- attr_accessor :clock # 8000.0 +/-
12
- attr_accessor :phase_inc
13
- attr_accessor :speed # 31 | 63 | 125
14
-
15
- # Format of the input data stream is specified in the same
16
- # format as String#unpack. Here are some examples:
17
- # 'C' 8-bit unsigned mono
18
- # 'xxv' Little-endian 16-bit right channel
19
- # Not everything is supported outside this native Ruby implementation.
20
- # The following are guaranteed to be in a C or Java implementation.
21
- # x - ignores a byte (in an unused channel)
22
- # C/c - Unsigned/signed bytes
23
- # n/v - Unsigned 16-bit words in big/little endian format
24
-
25
- # You must supply at least one of the dec16, dec8, or dec4 filters and
26
- # it must be appropriate for the speed you desire. The dec4 filter can
27
- # be used for all speeds, dec8 only for PSK63, and dec16 only for PSK31.
28
-
29
- def initialize sample_rate, format, frequency, filters
30
- @do_dec6 = case sample_rate
31
- when 8000 then false
32
- when 48000 then true
33
- else raise 'only 8000 and 48000 Hz sample rates are supported'
34
- end
35
- @format = format
36
- @sample_size = [0].pack(format).size
37
- case ("\x80"*16).unpack(format)[0]
38
- when 128
39
- @max = 128
40
- @offset = -128
41
- when -128
42
- @max = 128
43
- @offset = 0
44
- when 32768 + 128
45
- @max = 32768
46
- @offset = -32768
47
- when -32768 + 128
48
- @max = 32768
49
- @offset = 0
50
- else
51
- raise 'unable to interpret format'
52
- end
53
- @frequency = frequency
54
- @clock = 8000.0
55
- @phase = 0.0
56
- @speed = 31
57
- @pulse = 0
58
- if filters[:dec6]
59
- @dec6_coef = NArray.to_na filters[:dec6]*2
60
- @dec6_data = NArray.float filters[:dec6].size
61
- @dec6_pos = 0
62
- @dec6_pulse = 6
63
- elsif @do_dec6
64
- raise 'no 48000 Hz filter found'
65
- end
66
- if filters[:dec16]
67
- @dec16_coef = NArray.to_na filters[:dec16]*2
68
- @dec16_sin = NArray.float filters[:dec16].size
69
- @dec16_cos = NArray.float filters[:dec16].size
70
- @dec16_pos = 0
71
- else
72
- @dec16_coef = nil
73
- end
74
- if filters[:dec8]
75
- @dec8_coef = NArray.to_na filters[:dec8]*2
76
- @dec8_sin = NArray.float filters[:dec8].size
77
- @dec8_cos = NArray.float filters[:dec8].size
78
- @dec8_pos = 0
79
- else
80
- @dec8_coef = nil
81
- end
82
- if filters[:dec4]
83
- @dec4_coef = NArray.to_na filters[:dec4]*2
84
- @dec4a_sin = NArray.float filters[:dec4].size
85
- @dec4a_cos = NArray.float filters[:dec4].size
86
- @dec4a_pos = 0
87
- @dec4b_sin = NArray.float filters[:dec4].size
88
- @dec4b_cos = NArray.float filters[:dec4].size
89
- @dec4b_pos = 0
90
- else
91
- @dec4_coef = nil
92
- end
93
- @bit_coef = NArray.to_na filters[:bit]*2
94
- @bit_sin = NArray.float filters[:bit].size
95
- @bit_cos = NArray.float filters[:bit].size
96
- @bit_pos = 0
97
- recalc_phase_inc
98
- end
99
-
100
- def reset
101
- if @dec6_coef
102
- @dec6_data.fill 0.0
103
- end
104
- if @dec16_coef
105
- @dec16_sin_data.fill 0.0
106
- @dec16_cos_data.fill 0.0
107
- end
108
- if @dec8_coef
109
- @dec8_sin_data.fill 0.0
110
- @dec8_cos_data.fill 0.0
111
- end
112
- if @dec4_coef
113
- @dec4a_sin.fill 0.0
114
- @dec4a_cos.fill 0.0
115
- @dec4b_sin.fill 0.0
116
- @dec4b_cos.fill 0.0
117
- end
118
- @bit_sin.fill 0.0
119
- @bit_cos.fill 0.0
120
- end
121
-
122
- def recalc_phase_inc
123
- @phase_inc = PI2 * @frequency / @clock
124
- end
125
-
126
- def call sample_data
127
- raise 'alignment error' unless sample_data.size % @sample_size == 0
128
- sample_data.force_encoding('binary') # Ensure slice is fast like Ruby 1.9.3 byteslice
129
- mod16_8 = @speed == 63 ? 8 : 16
130
- pos = 0
131
- while pos < sample_data.size
132
- pos += @sample_size
133
- sample = sample_data.slice(pos,@sample_size).unpack(@format)[0] || 0
134
- sample = (sample + @offset).to_f / @max
135
- if @do_dec6
136
- @dec6_pos = @dec6_data.size if @dec6_pos == 0
137
- @dec6_pos -= 1
138
- @dec6_data[@dec6_pos] = sample
139
- @dec6_pulse -= 1
140
- next unless @dec6_pulse == 0
141
- @dec6_pulse = 6
142
- sample = @dec6_data.fir(@dec6_coef, @dec6_pos)
143
- end
144
- @phase += @phase_inc
145
- @phase -= PI2 if @phase > PI2
146
- if @dec16_coef and @speed == 31
147
- @dec16_pos = @dec16_sin.size if @dec16_pos == 0
148
- @dec16_pos -= 1
149
- @dec16_sin[@dec16_pos] = sample * Math.sin(@phase)
150
- @dec16_cos[@dec16_pos] = sample * Math.cos(@phase)
151
- next unless ((@pulse = @pulse + 1 & 0xFF) % 16) == 0
152
- ival = @dec16_sin.fir(@dec16_coef, @dec16_pos)
153
- qval = @dec16_cos.fir(@dec16_coef, @dec16_pos)
154
- elsif @dec8_coef and @speed == 63
155
- @dec8_pos = @dec8_sin.size if @dec8_pos == 0
156
- @dec8_pos -= 1
157
- @dec8_sin[@dec8_pos] = sample * Math.sin(@phase)
158
- @dec8_cos[@dec8_pos] = sample * Math.cos(@phase)
159
- next unless ((@pulse = @pulse + 1 & 0xFF) % 8) == 0
160
- ival = @dec8_sin.fir(@dec8_coef, @dec8_pos)
161
- qval = @dec8_cos.fir(@dec8_coef, @dec8_pos)
162
- elsif @dec4_coef
163
- @dec4a_pos = @dec4a_sin.size if @dec4a_pos == 0
164
- @dec4a_pos -= 1
165
- @dec4a_sin[@dec4a_pos] = sample * Math.sin(@phase)
166
- @dec4a_cos[@dec4a_pos] = sample * Math.cos(@phase)
167
- next unless ((@pulse = @pulse + 1 & 0xFF) % 4) == 0
168
- @dec4b_pos = @dec4b_sin.size if @dec4b_pos == 0
169
- @dec4b_pos -= 1
170
- ival = @dec4b_sin[@dec4b_pos] = @dec4a_sin.fir(@dec4_coef, @dec4a_pos)
171
- qval = @dec4b_cos[@dec4b_pos] = @dec4a_cos.fir(@dec4_coef, @dec4a_pos)
172
- next unless @speed == 125 or (@pulse % mod16_8 == 0)
173
- unless @speed == 125
174
- ival = @dec4b_sin.fir(@dec4_coef, @dec4b_pos)
175
- qval = @dec4b_cos.fir(@dec4_coef, @dec4b_pos)
176
- end
177
- else
178
- raise 'no suitable filter found'
179
- end
180
- @bit_pos = @bit_sin.size if @bit_pos == 0
181
- @bit_pos -= 1
182
- @bit_sin[@bit_pos] = ival
183
- @bit_cos[@bit_pos] = qval
184
- yield @bit_sin.fir(@bit_coef, @bit_pos), @bit_cos.fir(@bit_coef, @bit_pos)
185
- end
186
- end
187
-
188
- # NArray can easily double filter performance
189
- begin
190
- require 'narray'
191
- class ::NArray
192
- def fir filter, pos
193
- (self * filter[size-pos..-1-pos]).sum
194
- end
195
- end
196
- rescue LoadError => e
197
- # Pure Ruby fake NArray
198
- class NArray < Array
199
- def self.float arg
200
- new arg, 0.0
201
- end
202
- def self.to_na arg
203
- new(arg).freeze
204
- end
205
- def fir filter, pos
206
- acc = 0.0
207
- index = size - pos
208
- each do |val|
209
- acc += val * filter[index]
210
- index += 1
211
- end
212
- acc
213
- end
214
- end
215
- end
216
-
217
- end
218
- end
219
- end
220
-