radio 0.0.1

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.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ Amateur radio software
data/lib/radio.rb ADDED
@@ -0,0 +1,14 @@
1
+ %w{
2
+ radio/psk31/*.rb
3
+ }.each do |glob|
4
+ Dir.glob(File.expand_path(glob, File.dirname(__FILE__))).each do |filename|
5
+ require filename
6
+ end
7
+ end
8
+
9
+ class Radio
10
+
11
+ PI = Math::PI.freeze
12
+ PI2 = (8.0 * Math.atan(1.0)).freeze
13
+
14
+ end
@@ -0,0 +1,46 @@
1
+ class Radio
2
+
3
+ class PSK31
4
+
5
+ class BitDetect
6
+
7
+ AVG_SAMPLES = 50.freeze
8
+ CHANGE_DELAY = 5
9
+
10
+ def initialize
11
+ @averages = Array.new 21
12
+ reset
13
+ end
14
+
15
+ def reset
16
+ @averages.fill 0.0
17
+ @phase = 0
18
+ @peak = 0
19
+ @next_peak = 0
20
+ @change_at = 0
21
+ end
22
+
23
+ def call sample_x, sample_y
24
+ yield if @phase == @peak
25
+ @peak = @next_peak if @phase == @change_at
26
+ energy = sample_x**2 + sample_y**2
27
+ @averages[@phase] = (1.0-1.0/AVG_SAMPLES)*@averages[@phase] + (1.0/AVG_SAMPLES)*energy
28
+ @phase += 1
29
+ if @phase > 15
30
+ @phase = 0
31
+ max = -1e10
32
+ for i in 0...16
33
+ energy = @averages[i]
34
+ if energy > max
35
+ @next_peak = i
36
+ @change_at = (i + CHANGE_DELAY) & 0x0F
37
+ max = energy
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,65 @@
1
+ class Radio
2
+ class PSK31
3
+
4
+ class Decoder
5
+
6
+ attr_accessor :mode # :bpsk or :qpsk or :qpsklsb
7
+
8
+ def initialize
9
+ @mode = :bpsk
10
+ reset
11
+ end
12
+
13
+ def reset
14
+ @prev_i = 0.0
15
+ @prev_q = 0.0
16
+ @this_i = 0.0
17
+ @this_q = 0.0
18
+ @code = 0
19
+ @prev_bit = false
20
+ end
21
+
22
+ def call new_i, new_q
23
+ @prev_i = @this_i
24
+ @prev_q = @this_q
25
+ @this_i = new_i
26
+ @this_q = new_q
27
+ vect_y = @prev_i*@this_i + @prev_q*@this_q
28
+ if @mode == :bpsk
29
+ bit = vect_y >= 0.0 ? 1 : 0
30
+ else
31
+ vect_x = @prev_i*@this_q - @this_i*@prev_q
32
+ if vect_y == 0.0 # atan2 errors on (0,0)
33
+ angle = PI
34
+ elsif @mode == :qpsklsb
35
+ angle = PI + Math.atan2(vect_y, -vect_x)
36
+ else # :qpsk or :bpsk
37
+ angle = PI + Math.atan2(vect_y, vect_x)
38
+ end
39
+ bit = viterbi angle
40
+ end
41
+ if bit==0 and @prev_bit==0
42
+ if @code != 0
43
+ @code >>= 2
44
+ @code &= 0x07FF
45
+ ch = VARICODE_DECODE_TABLE[@code]
46
+ yield ch if ch
47
+ @code = 0
48
+ end
49
+ else
50
+ @code <<= 1
51
+ @code |= bit
52
+ @prev_bit = bit
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def viterbi
59
+ raise 'todo'
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,220 @@
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
+
@@ -0,0 +1,287 @@
1
+ class Radio
2
+ class PSK31
3
+
4
+ FIR_DEC6 = [
5
+ -0.001026086585,
6
+ -0.002131424398,
7
+ -0.003473651912,
8
+ -0.005415991401,
9
+ -0.007094896149,
10
+ -0.008685456592,
11
+ -0.009325598368,
12
+ -0.009096149588,
13
+ -0.007453841473,
14
+ -0.004787743788,
15
+ -0.001155271744,
16
+ 0.002594565142,
17
+ 0.005930466661,
18
+ 0.007902971637,
19
+ 0.008117998323,
20
+ 0.006209532708,
21
+ 0.002565394219,
22
+ -0.002211033166,
23
+ -0.006928668876,
24
+ -0.010456837484,
25
+ -0.011638240040,
26
+ -0.009915015895,
27
+ -0.005261804435,
28
+ 0.001469200215,
29
+ 0.008865519609,
30
+ 0.015027698257,
31
+ 0.018157576105,
32
+ 0.016869453853,
33
+ 0.010745489111,
34
+ 0.000474997075,
35
+ -0.012015135186,
36
+ -0.023901641616,
37
+ -0.031878023716,
38
+ -0.032876349281,
39
+ -0.024689692215,
40
+ -0.006586586958,
41
+ 0.020417464549,
42
+ 0.053543134171,
43
+ 0.088652482149,
44
+ 0.120891532319,
45
+ 0.145553464727,
46
+ 0.158910442030,
47
+ 0.158910442030,
48
+ 0.145553464727,
49
+ 0.120891532319,
50
+ 0.088652482149,
51
+ 0.053543134171,
52
+ 0.020417464549,
53
+ -0.006586586958,
54
+ -0.024689692215,
55
+ -0.032876349281,
56
+ -0.031878023716,
57
+ -0.023901641616,
58
+ -0.012015135186,
59
+ 0.000474997075,
60
+ 0.010745489111,
61
+ 0.016869453853,
62
+ 0.018157576105,
63
+ 0.015027698257,
64
+ 0.008865519609,
65
+ 0.001469200215,
66
+ -0.005261804435,
67
+ -0.009915015895,
68
+ -0.011638240040,
69
+ -0.010456837484,
70
+ -0.006928668876,
71
+ -0.002211033166,
72
+ 0.002565394219,
73
+ 0.006209532708,
74
+ 0.008117998323,
75
+ 0.007902971637,
76
+ 0.005930466661,
77
+ 0.002594565142,
78
+ -0.001155271744,
79
+ -0.004787743788,
80
+ -0.007453841473,
81
+ -0.009096149588,
82
+ -0.009325598368,
83
+ -0.008685456592,
84
+ -0.007094896149,
85
+ -0.005415991401,
86
+ -0.003473651912,
87
+ -0.002131424398,
88
+ -0.001026086585
89
+ ].freeze
90
+
91
+
92
+ # 64-tap raised-cosine
93
+ FIR_DEC16 = [
94
+ 0.000000,
95
+ 0.000038,
96
+ 0.000150,
97
+ 0.000336,
98
+ 0.000595,
99
+ 0.000922,
100
+ 0.001317,
101
+ 0.001773,
102
+ 0.002288,
103
+ 0.002856,
104
+ 0.003472,
105
+ 0.004130,
106
+ 0.004823,
107
+ 0.005545,
108
+ 0.006288,
109
+ 0.007047,
110
+ 0.007812,
111
+ 0.008578,
112
+ 0.009337,
113
+ 0.010080,
114
+ 0.010802,
115
+ 0.011495,
116
+ 0.012153,
117
+ 0.012769,
118
+ 0.013337,
119
+ 0.013852,
120
+ 0.014308,
121
+ 0.014703,
122
+ 0.015030,
123
+ 0.015289,
124
+ 0.015475,
125
+ 0.015587,
126
+ 0.015625,
127
+ 0.015587,
128
+ 0.015475,
129
+ 0.015289,
130
+ 0.015030,
131
+ 0.014703,
132
+ 0.014308,
133
+ 0.013852,
134
+ 0.013337,
135
+ 0.012769,
136
+ 0.012153,
137
+ 0.011495,
138
+ 0.010802,
139
+ 0.010080,
140
+ 0.009337,
141
+ 0.008578,
142
+ 0.007813,
143
+ 0.007047,
144
+ 0.006288,
145
+ 0.005545,
146
+ 0.004823,
147
+ 0.004130,
148
+ 0.003472,
149
+ 0.002856,
150
+ 0.002288,
151
+ 0.001773,
152
+ 0.001317,
153
+ 0.000922,
154
+ 0.000595,
155
+ 0.000336,
156
+ 0.000150,
157
+ 0.000038
158
+ ].freeze
159
+
160
+
161
+ # Design method: Parks-McClellan method
162
+ # Number of taps = 35
163
+ # Number of bands = 2
164
+ # Band Lower Upper Value Weight
165
+ # edge edge
166
+ # 1 0.0 .0125 1.0 1
167
+ # 2 .125 .5 .000001 10
168
+ FIR_DEC4 = [
169
+ -0.00021203644,
170
+ -0.00070252426,
171
+ -0.0016680526,
172
+ -0.0031934799,
173
+ -0.0051899752,
174
+ -0.0072862086,
175
+ -0.0087714235,
176
+ -0.0086272102,
177
+ -0.0056735648,
178
+ 0.0011784719,
179
+ 0.01261353,
180
+ 0.028615709,
181
+ 0.048280707,
182
+ 0.069812051,
183
+ 0.090735013,
184
+ 0.10830381,
185
+ 0.12001897,
186
+ 0.12413413,
187
+ 0.12001897,
188
+ 0.10830381,
189
+ 0.090735013,
190
+ 0.069812051,
191
+ 0.048280707,
192
+ 0.028615709,
193
+ 0.01261353,
194
+ 0.0011784719,
195
+ -0.0056735648,
196
+ -0.0086272102,
197
+ -0.0087714235,
198
+ -0.0072862086,
199
+ -0.0051899752,
200
+ -0.0031934799,
201
+ -0.0016680526,
202
+ -0.00070252426,
203
+ -0.00021203644
204
+ ].freeze
205
+
206
+
207
+ # 16 Hz bw LP filter for data recovery
208
+ # Filter type: Multiband filter
209
+ # Design method: Parks-McClellan method
210
+ # Number of taps = 65
211
+ # Number of bands = 2
212
+ # Band Lower Upper Value Weight
213
+ # edge edge
214
+ #
215
+ # 1 0.0 .03125 1. 1.
216
+ # 2 .0625 .5 .0000 286.
217
+ FIR_BIT = [
218
+ 4.3453566e-005,
219
+ -0.00049122414,
220
+ -0.00078771292,
221
+ -0.0013507826,
222
+ -0.0021287814,
223
+ -0.003133466,
224
+ -0.004366817,
225
+ -0.0058112187,
226
+ -0.0074249976,
227
+ -0.0091398882,
228
+ -0.010860157,
229
+ -0.012464086,
230
+ -0.013807772,
231
+ -0.014731191,
232
+ -0.015067057,
233
+ -0.014650894,
234
+ -0.013333425,
235
+ -0.01099166,
236
+ -0.0075431246,
237
+ -0.0029527849,
238
+ 0.0027546292,
239
+ 0.0094932775,
240
+ 0.017113308,
241
+ 0.025403511,
242
+ 0.034099681,
243
+ 0.042895839,
244
+ 0.051458575,
245
+ 0.059444853,
246
+ 0.066521003,
247
+ 0.072381617,
248
+ 0.076767694,
249
+ 0.079481619,
250
+ 0.080420311,
251
+ 0.079481619,
252
+ 0.076767694,
253
+ 0.072381617,
254
+ 0.066521003,
255
+ 0.059444853,
256
+ 0.051458575,
257
+ 0.042895839,
258
+ 0.034099681,
259
+ 0.025403511,
260
+ 0.017113308,
261
+ 0.0094932775,
262
+ 0.0027546292,
263
+ -0.0029527849,
264
+ -0.0075431246,
265
+ -0.01099166,
266
+ -0.013333425,
267
+ -0.014650894,
268
+ -0.015067057,
269
+ -0.014731191,
270
+ -0.013807772,
271
+ -0.012464086,
272
+ -0.010860157,
273
+ -0.0091398882,
274
+ -0.0074249976,
275
+ -0.0058112187,
276
+ -0.004366817,
277
+ -0.003133466,
278
+ -0.0021287814,
279
+ -0.0013507826,
280
+ -0.00078771292,
281
+ -0.00049122414,
282
+ 4.3453566e-005
283
+ ].freeze
284
+
285
+
286
+ end
287
+ end
@@ -0,0 +1,52 @@
1
+ class Radio
2
+
3
+ class PSK31
4
+
5
+ class Rx
6
+
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
13
+ @bit_detect = BitDetect.new
14
+ @decoder = Decoder.new
15
+ end
16
+
17
+ def call sample_data
18
+ 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
23
+ end
24
+ end
25
+ 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
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,275 @@
1
+ class Radio
2
+
3
+ class PSK31
4
+
5
+ VARICODE_TABLE = [
6
+ 0xAAC0, # 0 1010101011
7
+ 0xB6C0, # 1 1011011011
8
+ 0xBB40, # 2 1011101101
9
+ 0xDDC0, # 3 1101110111
10
+ 0xBAC0, # 4 1011101011
11
+ 0xD7C0, # 5 1101011111
12
+ 0xBBC0, # 6 1011101111
13
+ 0xBF40, # 7 1011111101
14
+ 0xBFC0, # 8 1011111111
15
+ 0xEF00, # 9 11101111
16
+ 0xE800, # 10 11101
17
+ 0xDBC0, # 11 1101101111
18
+ 0xB740, # 12 1011011101
19
+ 0xF800, # 13 11111
20
+ 0xDD40, # 14 1101110101
21
+ 0xEAC0, # 15 1110101011
22
+ 0xBDC0, # 16 1011110111
23
+ 0xBD40, # 17 1011110101
24
+ 0xEB40, # 18 1110101101
25
+ 0xEBC0, # 19 1110101111
26
+ 0xD6C0, # 20 1101011011
27
+ 0xDAC0, # 21 1101101011
28
+ 0xDB40, # 22 1101101101
29
+ 0xD5C0, # 23 1101010111
30
+ 0xDEC0, # 24 1101111011
31
+ 0xDF40, # 25 1101111101
32
+ 0xEDC0, # 26 1110110111
33
+ 0xD540, # 27 1101010101
34
+ 0xD740, # 28 1101011101
35
+ 0xEEC0, # 29 1110111011
36
+ 0xBEC0, # 30 1011111011
37
+ 0xDFC0, # 31 1101111111
38
+ 0x8000, # ' ' 1
39
+ 0xFF80, # '!' 111111111
40
+ 0xAF80, # '"' 101011111
41
+ 0xFA80, # '#' 111110101
42
+ 0xED80, # '$' 111011011
43
+ 0xB540, # '%' 1011010101
44
+ 0xAEC0, # '&' 1010111011
45
+ 0xBF80, # ''' 101111111
46
+ 0xFB00, # '(' 11111011
47
+ 0xF700, # ')' 11110111
48
+ 0xB780, # '*' 101101111
49
+ 0xEF80, # '+' 111011111
50
+ 0xEA00, # ',' 1110101
51
+ 0xD400, # '-' 110101
52
+ 0xAE00, # '.' 1010111
53
+ 0xD780, # '/' 110101111
54
+ 0xB700, # '0' 10110111
55
+ 0xBD00, # '1' 10111101
56
+ 0xED00, # '2' 11101101
57
+ 0xFF00, # '3' 11111111
58
+ 0xBB80, # '4' 101110111
59
+ 0xAD80, # '5' 101011011
60
+ 0xB580, # '6' 101101011
61
+ 0xD680, # '7' 110101101
62
+ 0xD580, # '8' 110101011
63
+ 0xDB80, # '9' 110110111
64
+ 0xF500, # ':' 11110101
65
+ 0xDE80, # ';' 110111101
66
+ 0xF680, # '<' 111101101
67
+ 0xAA00, # '=' 1010101
68
+ 0xEB80, # '>' 111010111
69
+ 0xABC0, # '?' 1010101111
70
+ 0xAF40, # '@' 1010111101
71
+ 0xFA00, # 'A' 1111101
72
+ 0xEB00, # 'B' 11101011
73
+ 0xAD00, # 'C' 10101101
74
+ 0xB500, # 'D' 10110101
75
+ 0xEE00, # 'E' 1110111
76
+ 0xDB00, # 'F' 11011011
77
+ 0xFD00, # 'G' 11111101
78
+ 0xAA80, # 'H' 101010101
79
+ 0xFE00, # 'I' 1111111
80
+ 0xFE80, # 'J' 111111101
81
+ 0xBE80, # 'K' 101111101
82
+ 0xD700, # 'L' 11010111
83
+ 0xBB00, # 'M' 10111011
84
+ 0xDD00, # 'N' 11011101
85
+ 0xAB00, # 'O' 10101011
86
+ 0xD500, # 'P' 11010101
87
+ 0xEE80, # 'Q' 111011101
88
+ 0xAF00, # 'R' 10101111
89
+ 0xDE00, # 'S' 1101111
90
+ 0xDA00, # 'T' 1101101
91
+ 0xAB80, # 'U' 101010111
92
+ 0xDA80, # 'V' 110110101
93
+ 0xAE80, # 'W' 101011101
94
+ 0xBA80, # 'X' 101110101
95
+ 0xBD80, # 'Y' 101111011
96
+ 0xAB40, # 'Z' 1010101101
97
+ 0xFB80, # '[' 111110111
98
+ 0xF780, # '\' 111101111
99
+ 0xFD80, # ']' 111111011
100
+ 0xAFC0, # '^' 1010111111
101
+ 0xB680, # '_' 101101101
102
+ 0xB7C0, # '`' 1011011111
103
+ 0xB000, # 'a' 1011
104
+ 0xBE00, # 'b' 1011111
105
+ 0xBC00, # 'c' 101111
106
+ 0xB400, # 'd' 101101
107
+ 0xC000, # 'e' 11
108
+ 0xF400, # 'f' 111101
109
+ 0xB600, # 'g' 1011011
110
+ 0xAC00, # 'h' 101011
111
+ 0xD000, # 'i' 1101
112
+ 0xF580, # 'j' 111101011
113
+ 0xBF00, # 'k' 10111111
114
+ 0xD800, # 'l' 11011
115
+ 0xEC00, # 'm' 111011
116
+ 0xF000, # 'n' 1111
117
+ 0xE000, # 'o' 111
118
+ 0xFC00, # 'p' 111111
119
+ 0xDF80, # 'q' 110111111
120
+ 0xA800, # 'r' 10101
121
+ 0xB800, # 's' 10111
122
+ 0xA000, # 't' 101
123
+ 0xDC00, # 'u' 110111
124
+ 0xF600, # 'v' 1111011
125
+ 0xD600, # 'w' 1101011
126
+ 0xDF00, # 'x' 11011111
127
+ 0xBA00, # 'y' 1011101
128
+ 0xEA80, # 'z' 111010101
129
+ 0xADC0, # '{' 1010110111
130
+ 0xDD80, # '|' 110111011
131
+ 0xAD40, # '}' 1010110101
132
+ 0xB5C0, # '~' 1011010111
133
+ 0xED40, # 127 1110110101
134
+ 0xEF40, # 128 1110111101
135
+ 0xEFC0, # 129 1110111111
136
+ 0xF540, # 130 1111010101
137
+ 0xF5C0, # 131 1111010111
138
+ 0xF6C0, # 132 1111011011
139
+ 0xF740, # 133 1111011101
140
+ 0xF7C0, # 134 1111011111
141
+ 0xFAC0, # 135 1111101011
142
+ 0xFB40, # 136 1111101101
143
+ 0xFBC0, # 137 1111101111
144
+ 0xFD40, # 138 1111110101
145
+ 0xFDC0, # 139 1111110111
146
+ 0xFEC0, # 140 1111111011
147
+ 0xFF40, # 141 1111111101
148
+ 0xFFC0, # 142 1111111111
149
+ 0xAAA0, # 143 10101010101
150
+ 0xAAE0, # 144 10101010111
151
+ 0xAB60, # 145 10101011011
152
+ 0xABA0, # 146 10101011101
153
+ 0xABE0, # 147 10101011111
154
+ 0xAD60, # 148 10101101011
155
+ 0xADA0, # 149 10101101101
156
+ 0xADE0, # 150 10101101111
157
+ 0xAEA0, # 151 10101110101
158
+ 0xAEE0, # 152 10101110111
159
+ 0xAF60, # 153 10101111011
160
+ 0xAFA0, # 154 10101111101
161
+ 0xAFE0, # 155 10101111111
162
+ 0xB560, # 156 10110101011
163
+ 0xB5A0, # 157 10110101101
164
+ 0xB5E0, # 158 10110101111
165
+ 0xB6A0, # 159 10110110101
166
+ 0xB6E0, # 160 10110110111
167
+ 0xB760, # 161 10110111011
168
+ 0xB7A0, # 162 10110111101
169
+ 0xB7E0, # 163 10110111111
170
+ 0xBAA0, # 164 10111010101
171
+ 0xBAE0, # 165 10111010111
172
+ 0xBB60, # 166 10111011011
173
+ 0xBBA0, # 167 10111011101
174
+ 0xBBE0, # 168 10111011111
175
+ 0xBD60, # 169 10111101011
176
+ 0xBDA0, # 170 10111101101
177
+ 0xBDE0, # 171 10111101111
178
+ 0xBEA0, # 172 10111110101
179
+ 0xBEE0, # 173 10111110111
180
+ 0xBF60, # 174 10111111011
181
+ 0xBFA0, # 175 10111111101
182
+ 0xBFE0, # 176 10111111111
183
+ 0xD560, # 177 11010101011
184
+ 0xD5A0, # 178 11010101101
185
+ 0xD5E0, # 179 11010101111
186
+ 0xD6A0, # 180 11010110101
187
+ 0xD6E0, # 181 11010110111
188
+ 0xD760, # 182 11010111011
189
+ 0xD7A0, # 183 11010111101
190
+ 0xD7E0, # 184 11010111111
191
+ 0xDAA0, # 185 11011010101
192
+ 0xDAE0, # 186 11011010111
193
+ 0xDB60, # 187 11011011011
194
+ 0xDBA0, # 188 11011011101
195
+ 0xDBE0, # 189 11011011111
196
+ 0xDD60, # 190 11011101011
197
+ 0xDDA0, # 191 11011101101
198
+ 0xDDE0, # 192 11011101111
199
+ 0xDEA0, # 193 11011110101
200
+ 0xDEE0, # 194 11011110111
201
+ 0xDF60, # 195 11011111011
202
+ 0xDFA0, # 196 11011111101
203
+ 0xDFE0, # 197 11011111111
204
+ 0xEAA0, # 198 11101010101
205
+ 0xEAE0, # 199 11101010111
206
+ 0xEB60, # 200 11101011011
207
+ 0xEBA0, # 201 11101011101
208
+ 0xEBE0, # 202 11101011111
209
+ 0xED60, # 203 11101101011
210
+ 0xEDA0, # 204 11101101101
211
+ 0xEDE0, # 205 11101101111
212
+ 0xEEA0, # 206 11101110101
213
+ 0xEEE0, # 207 11101110111
214
+ 0xEF60, # 208 11101111011
215
+ 0xEFA0, # 209 11101111101
216
+ 0xEFE0, # 210 11101111111
217
+ 0xF560, # 211 11110101011
218
+ 0xF5A0, # 212 11110101101
219
+ 0xF5E0, # 213 11110101111
220
+ 0xF6A0, # 214 11110110101
221
+ 0xF6E0, # 215 11110110111
222
+ 0xF760, # 216 11110111011
223
+ 0xF7A0, # 217 11110111101
224
+ 0xF7E0, # 218 11110111111
225
+ 0xFAA0, # 219 11111010101
226
+ 0xFAE0, # 220 11111010111
227
+ 0xFB60, # 221 11111011011
228
+ 0xFBA0, # 222 11111011101
229
+ 0xFBE0, # 223 11111011111
230
+ 0xFD60, # 224 11111101011
231
+ 0xFDA0, # 225 11111101101
232
+ 0xFDE0, # 226 11111101111
233
+ 0xFEA0, # 227 11111110101
234
+ 0xFEE0, # 228 11111110111
235
+ 0xFF60, # 229 11111111011
236
+ 0xFFA0, # 230 11111111101
237
+ 0xFFE0, # 231 11111111111
238
+ 0xAAB0, # 232 101010101011
239
+ 0xAAD0, # 233 101010101101
240
+ 0xAAF0, # 234 101010101111
241
+ 0xAB50, # 235 101010110101
242
+ 0xAB70, # 236 101010110111
243
+ 0xABB0, # 237 101010111011
244
+ 0xABD0, # 238 101010111101
245
+ 0xABF0, # 239 101010111111
246
+ 0xAD50, # 240 101011010101
247
+ 0xAD70, # 241 101011010111
248
+ 0xADB0, # 242 101011011011
249
+ 0xADD0, # 243 101011011101
250
+ 0xADF0, # 244 101011011111
251
+ 0xAEB0, # 245 101011101011
252
+ 0xAED0, # 246 101011101101
253
+ 0xAEF0, # 247 101011101111
254
+ 0xAF50, # 248 101011110101
255
+ 0xAF70, # 249 101011110111
256
+ 0xAFB0, # 250 101011111011
257
+ 0xAFD0, # 251 101011111101
258
+ 0xAFF0, # 252 101011111111
259
+ 0xB550, # 253 101101010101
260
+ 0xB570, # 254 101101010111
261
+ 0xB5B0 # 255 101101011011
262
+ ].freeze
263
+
264
+ VARICODE_DECODE_TABLE = []
265
+ for i in 0...256
266
+ wTemp = VARICODE_TABLE[i]
267
+ wTemp >>= 4
268
+ wTemp >>= 1 until (wTemp&1 == 1)
269
+ wTemp >>= 1
270
+ VARICODE_DECODE_TABLE[wTemp] = i.chr
271
+ end
272
+ VARICODE_DECODE_TABLE.freeze
273
+
274
+ end
275
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: radio
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Turnbull
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-11 00:00:00.000000000Z
13
+ dependencies: []
14
+ description:
15
+ email:
16
+ - dturnbull@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/radio/psk31/bit_detect.rb
22
+ - lib/radio/psk31/decoder.rb
23
+ - lib/radio/psk31/filters.rb
24
+ - lib/radio/psk31/fir_coef.rb
25
+ - lib/radio/psk31/rx.rb
26
+ - lib/radio/psk31/varicode.rb
27
+ - lib/radio.rb
28
+ - README.md
29
+ homepage: https://github.com/dturnbull/radio
30
+ licenses: []
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project:
49
+ rubygems_version: 1.8.6
50
+ signing_key:
51
+ specification_version: 3
52
+ summary: Amateur radio software
53
+ test_files: []
54
+ has_rdoc: