radio 0.0.1

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