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.
@@ -1,3 +1,18 @@
1
+ # Copyright 2012 The ham21/radio Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
1
16
  class Radio
2
17
 
3
18
  class PSK31
@@ -8,7 +23,7 @@ class Radio
8
23
  CHANGE_DELAY = 5
9
24
 
10
25
  def initialize
11
- @averages = Array.new 21
26
+ @averages = Array.new 16
12
27
  reset
13
28
  end
14
29
 
@@ -20,10 +35,10 @@ class Radio
20
35
  @change_at = 0
21
36
  end
22
37
 
23
- def call sample_x, sample_y
24
- yield if @phase == @peak
38
+ def call iq
39
+ yield iq if @phase == @peak
25
40
  @peak = @next_peak if @phase == @change_at
26
- energy = sample_x**2 + sample_y**2
41
+ energy = iq.real**2 + iq.imag**2
27
42
  @averages[@phase] = (1.0-1.0/AVG_SAMPLES)*@averages[@phase] + (1.0/AVG_SAMPLES)*energy
28
43
  @phase += 1
29
44
  if @phase > 15
@@ -1,3 +1,18 @@
1
+ # Copyright 2012 The ham21/radio Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
1
16
  class Radio
2
17
  class PSK31
3
18
 
@@ -19,11 +34,11 @@ class Radio
19
34
  @prev_bit = false
20
35
  end
21
36
 
22
- def call new_i, new_q
37
+ def call iq
23
38
  @prev_i = @this_i
24
39
  @prev_q = @this_q
25
- @this_i = new_i
26
- @this_q = new_q
40
+ @this_i = iq.real
41
+ @this_q = iq.imag
27
42
  vect_y = @prev_i*@this_i + @prev_q*@this_q
28
43
  if @mode == :bpsk
29
44
  bit = vect_y >= 0.0 ? 1 : 0
@@ -1,6 +1,11 @@
1
+ # These are from LGPL licensed programs if they are even copyrightable.
2
+ # They will be removed after I implement remez in Ruby.
3
+
4
+
1
5
  class Radio
2
6
  class PSK31
3
7
 
8
+ # 48000 to 8000 decimation
4
9
  FIR_DEC6 = [
5
10
  -0.001026086585,
6
11
  -0.002131424398,
@@ -165,7 +170,7 @@ class Radio
165
170
  # edge edge
166
171
  # 1 0.0 .0125 1.0 1
167
172
  # 2 .125 .5 .000001 10
168
- FIR_DEC4 = [
173
+ FIR_DEC = [
169
174
  -0.00021203644,
170
175
  -0.00070252426,
171
176
  -0.0016680526,
@@ -1,49 +1,44 @@
1
+ # Copyright 2012 The ham21/radio Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
1
16
  class Radio
2
17
 
3
18
  class PSK31
4
19
 
5
20
  class Rx
6
21
 
7
- def initialize sample_rate, format, frequency
8
- @filter = Filters.new sample_rate, format, frequency,
9
- :dec6 => FIR_DEC6,
10
- :dec16 => FIR_DEC16,
11
- :dec4 => FIR_DEC4,
12
- :bit => FIR_BIT
22
+ def initialize frequency
23
+ phase_inc = PI2 * frequency / 8000
24
+ @dec_filter = Filter.new mix:phase_inc, decimate:16, fir:FIR_DEC16
25
+ @bit_filter = Filter.new fir:FIR_BIT
13
26
  @bit_detect = BitDetect.new
14
27
  @decoder = Decoder.new
15
28
  end
16
29
 
17
- def call sample_data
30
+ def call data
18
31
  decoded = ''
19
- @filter.call sample_data do |i, q|
20
- @bit_detect.call i, q do
21
- @decoder.call i, q do |symbol|
22
- decoded += symbol
32
+ @dec_filter.call data do |iq|
33
+ @bit_filter.call iq do |iq|
34
+ @bit_detect.call iq do |iq|
35
+ @decoder.call iq do |symbol|
36
+ decoded += symbol
37
+ end
23
38
  end
24
39
  end
25
40
  end
26
- decoded
27
- end
28
-
29
- def frequency= frequency
30
- if frequency != @filter.frequency
31
- @filter.frequency = frequency
32
- @filter.recalc_phase_inc
33
- reset
34
- end
35
- end
36
-
37
- # To compensate for bad clock in A-D conversion
38
- def adjust_sample_clock ppm
39
- @filter.clock = 8000.0 * ppm / 1000000 + 8000
40
- @filter.recalc_phase_inc
41
- end
42
-
43
- def reset
44
- @filter.reset
45
- @bit_detect.reset
46
- @decoder.reset
41
+ yield decoded
47
42
  end
48
43
 
49
44
  end
@@ -1,3 +1,18 @@
1
+ # Copyright 2012 The ham21/radio Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
1
16
  class Radio
2
17
 
3
18
  class PSK31
@@ -0,0 +1,46 @@
1
+ # Copyright 2012 The ham21/radio Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ class Radio
17
+
18
+ class Rig
19
+
20
+ include Spectrum
21
+ include Rx
22
+ include LO
23
+
24
+ def initialize
25
+ @semaphore = Mutex.new
26
+ super
27
+ end
28
+
29
+ def rate
30
+ @semaphore.synchronize do
31
+ return @rx.rate if @rx
32
+ return @tx.rate if @tx
33
+ return 0
34
+ end
35
+ end
36
+
37
+ def iq?
38
+ @semaphore.synchronize do
39
+ return @rx.channels == 2 if @rx
40
+ return @tx.channels == 2 if @tx
41
+ return false
42
+ end
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,57 @@
1
+ # Copyright 2012 The ham21/radio Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ class Radio
17
+ class Rig
18
+ module LO
19
+
20
+ def initialize
21
+ @lo = Controls::Null.new
22
+ super
23
+ end
24
+
25
+ # Accepts an instance of any LO control to use.
26
+ # Float for the frequency in MHz (sub-Hz ok).
27
+ # Integer for the frequency in Hz.
28
+ def lo= freq_or_control
29
+ @semaphore.synchronize do
30
+ if Numeric === freq_or_control
31
+ @lo.lo = if freq_or_control.integer?
32
+ freq_or_control.to_f / 1000000
33
+ else
34
+ freq_or_control
35
+ end
36
+ else
37
+ @lo.stop if @lo
38
+ @lo = freq_or_control
39
+ end
40
+ end
41
+ end
42
+
43
+ # Returns a float of the LO frequency in MHz.
44
+ # This will read from the actual device for the case of an
45
+ # operator adjusting outside this application, such as with
46
+ # the main dial of a stand-alone radio.
47
+ def lo
48
+ @semaphore.synchronize do
49
+ @lo.lo
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+
57
+
@@ -0,0 +1,61 @@
1
+ # Copyright 2012 The ham21/radio Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ class Radio
17
+
18
+ class Rig
19
+
20
+ module Rx
21
+
22
+ def initialize
23
+ @rx = @rx_thread = nil
24
+ super
25
+ end
26
+
27
+ def rx= input
28
+ old_rx_thread = new_thread = false
29
+ @semaphore.synchronize do
30
+ if @rx
31
+ @rx.stop
32
+ old_rx_thread = @rx_thread
33
+ old_rx_thread.kill
34
+ end
35
+ if @rx = input
36
+ @rx_thread = Thread.new &method(:rx_thread)
37
+ end
38
+ end
39
+ old_rx_thread.join if old_rx_thread
40
+ end
41
+
42
+ private
43
+
44
+ # DSP under an OS requires chunked and streaming processing
45
+ # as opposed to handling every sample as it arrives.
46
+ # We want to work with powers of two but sample rates
47
+ # are multiples of 8000. Let's use a lag of 32ms or 31.25 baud.
48
+ def rx_thread
49
+ qty_samples = @rx.rate / 125 * 4
50
+ loop do
51
+ samples = @rx.call qty_samples
52
+ spectrum_collect samples
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+ end
60
+
61
+
@@ -0,0 +1,96 @@
1
+ # Copyright 2012 The ham21/radio Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ class Radio
17
+ class Rig
18
+ module Spectrum
19
+
20
+ def initialize
21
+ @spectrum_buf = {}
22
+ @spectrum_pending = Hash.new {|h,k|h[k]=[]}
23
+ super
24
+ end
25
+
26
+ # Block until the next collection of data is ready (size*frequency samples).
27
+ # Result is NArray of floats suitable for display purposes.
28
+ # size must be a power of 2 for good performance (use GPU for visual scaling)
29
+ # frequency may be greater or less than 1.0
30
+ # keep_alive in secs set high enough so intervals between results are accurate
31
+ # Results are not queued; missed processing is skipped.
32
+ # This is meant to be an ideal interface for Web UI work, not signal analysis.
33
+ def spectrum size, frequency=1.0, keep_alive=2.0
34
+ @semaphore.synchronize do
35
+ @spectrum_pending[[size, frequency, keep_alive]] << Thread.current
36
+ end
37
+ sleep
38
+ @spectrum_buf[[size, frequency, keep_alive]][2]
39
+ end
40
+
41
+ private
42
+
43
+ def spectrum_collect samples
44
+ time_now = Time.now
45
+ @semaphore.synchronize do
46
+ # Ensure buffers for all requested setups are in place
47
+ @spectrum_pending.keys.each do |size, frequency, keep_alive|
48
+ @spectrum_buf[[size, frequency, keep_alive]] ||= [nil,[],nil]
49
+ @spectrum_buf[[size, frequency, keep_alive]][0] = time_now
50
+ end
51
+ # Stop running anything that's not being used anymore
52
+ @spectrum_buf.delete_if { |k,v| v[0] < time_now - k[2] }
53
+ # Handle the data for each active buffer
54
+ @spectrum_buf.each do |k,v|
55
+ time, data, result = v
56
+ size, frequency, keep_alive = k
57
+ collect_size = size * ((Float === samples[0]) ? 2 : 1)
58
+ data.push samples
59
+ buf_size = data.reduce(0){|a,b|a+b.size}
60
+ size_freq = collect_size*frequency
61
+ # Wait until we have enough data and the time is right
62
+ if buf_size > collect_size and buf_size > size_freq
63
+ # Discard any extra data we won't use (frequency>1)
64
+ while buf_size - data.first.size > collect_size
65
+ buf_size -= data.shift.size
66
+ end
67
+ pending_key = [size, frequency, keep_alive]
68
+ if @spectrum_pending.has_key? pending_key
69
+ spectrum_data = NArray.to_na data
70
+ spectrum_data.reshape! spectrum_data.total
71
+ spectrum_out = FFTW3.fft(spectrum_data[-collect_size..-1])
72
+ if spectrum_out.size == size
73
+ result = NArray.sfloat size
74
+ result[0...size/2] = spectrum_out[size/2..-1].div!(size)
75
+ result[size/2..-1] = spectrum_out[0...size/2].div!(size)
76
+ v[2] = result.abs
77
+ else
78
+ v[2] = spectrum_out[0...size].div!(size).abs
79
+ end
80
+ @spectrum_pending[pending_key].each(&:wakeup)
81
+ @spectrum_pending.delete pending_key
82
+ end
83
+ # Discard enough old buffers to accommodate the frequency
84
+ trim_size = [0, collect_size - size*frequency].max
85
+ while buf_size > trim_size
86
+ buf_size -= data.shift.size
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ end
96
+