radio 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-