cw 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.cw_config +4 -1
- data/.gitignore +2 -0
- data/Gemfile +6 -0
- data/VERSION +1 -1
- data/cw.gemspec +6 -6
- data/lib/cw.rb +8 -2
- data/lib/cw/audio_player.rb +4 -1
- data/lib/cw/config.rb +3 -3
- data/lib/cw/coreaudio.rb +144 -0
- data/lib/cw/cw_dsl.rb +12 -12
- data/lib/cw/cw_threads.rb +54 -24
- data/lib/cw/key_input.rb +15 -8
- data/lib/cw/play.rb +1 -1
- data/lib/cw/print.rb +25 -1
- data/lib/cw/repeat_word.rb +9 -4
- data/lib/cw/rss.rb +23 -20
- data/lib/cw/tester.rb +6 -2
- data/lib/cw/tone_generator.rb +68 -83
- data/lib/cw/tone_helpers.rb +1 -1
- data/lib/cw/tx.rb +487 -0
- data/lib/cw/winkey.rb +121 -0
- data/lib/cw/words.rb +1 -1
- data/test/test_cw.rb +28 -27
- data/test/test_tone_generator.rb +141 -0
- metadata +42 -15
- data/calculator.rb +0 -63
- data/chart.rb +0 -33
- data/chart2.rb +0 -35
- data/chart3.rb +0 -47
- data/chart4.rb +0 -45
data/lib/cw/play.rb
CHANGED
data/lib/cw/print.rb
CHANGED
@@ -46,12 +46,12 @@ module CWG
|
|
46
46
|
|
47
47
|
def reset
|
48
48
|
@print_count = 0
|
49
|
-
puts "\r"
|
50
49
|
end
|
51
50
|
|
52
51
|
def newline
|
53
52
|
reset
|
54
53
|
update_console_size
|
54
|
+
puts "\r"
|
55
55
|
end
|
56
56
|
|
57
57
|
def force_newline_maybe
|
@@ -125,6 +125,18 @@ module CWG
|
|
125
125
|
print paint("#{word}", list_colour)
|
126
126
|
end
|
127
127
|
|
128
|
+
def tx word
|
129
|
+
print paint("#{word}", tx_colour)
|
130
|
+
end
|
131
|
+
|
132
|
+
def rx word
|
133
|
+
print paint("#{word}", rx_colour)
|
134
|
+
end
|
135
|
+
|
136
|
+
def menu word
|
137
|
+
print paint("#{word}", menu_colour)
|
138
|
+
end
|
139
|
+
|
128
140
|
def success word
|
129
141
|
newline_maybe word
|
130
142
|
return if(@print_count == 0 && word == ' ')
|
@@ -143,6 +155,18 @@ module CWG
|
|
143
155
|
Cfg.config["list_colour"].to_sym || :default
|
144
156
|
end
|
145
157
|
|
158
|
+
def tx_colour
|
159
|
+
Cfg.config["tx_colour"].to_sym || :red
|
160
|
+
end
|
161
|
+
|
162
|
+
def rx_colour
|
163
|
+
Cfg.config["rx_colour"].to_sym || :blue
|
164
|
+
end
|
165
|
+
|
166
|
+
def menu_colour
|
167
|
+
Cfg.config["menu_colour"].to_sym || :yellow
|
168
|
+
end
|
169
|
+
|
146
170
|
def print_advice name
|
147
171
|
puts "#{name}: Press Q 4 times to Exit"
|
148
172
|
end
|
data/lib/cw/repeat_word.rb
CHANGED
@@ -58,23 +58,28 @@ module CWG
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
def threads
|
62
|
+
threads ||= CWThreads.new(self, thread_processes)
|
63
|
+
end
|
64
|
+
|
61
65
|
def run words
|
62
66
|
temp_words = words.all
|
63
67
|
temp_words.each do |word|
|
64
68
|
loop do
|
69
|
+
Cfg.config.params["exit"] = false
|
65
70
|
break if test_env
|
66
71
|
@input_word, @words = '', Words.new
|
67
72
|
Cfg.config.params["quit"] = false
|
68
73
|
@words.assign word
|
69
|
-
|
70
|
-
|
71
|
-
|
74
|
+
threads.run
|
75
|
+
# threads.start_threads
|
76
|
+
# threads.wait_for_threads
|
72
77
|
@play = nil
|
73
78
|
system("stty -raw echo")
|
74
79
|
break unless failed?
|
75
80
|
end
|
76
81
|
end
|
77
|
-
|
82
|
+
Cfg.config.params["exit"] = true
|
78
83
|
end
|
79
84
|
end
|
80
85
|
end
|
data/lib/cw/rss.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require 'oga'
|
4
|
+
require 'httpclient'
|
5
|
+
require "htmlentities"
|
6
|
+
require 'sanitize'
|
7
|
+
|
3
8
|
module CWG
|
4
9
|
|
5
10
|
#class Rss
|
@@ -22,25 +27,25 @@ module CWG
|
|
22
27
|
end
|
23
28
|
|
24
29
|
def read_rss(src, article_count = 3)
|
25
|
-
|
26
|
-
|
27
|
-
|
30
|
+
|
31
|
+
Cfg.config.params["words_counted"] = true
|
32
|
+
@rss_articles = []
|
33
|
+
count = 0
|
28
34
|
coder = HTMLEntities.new
|
29
35
|
url = source(src)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
title = entry.title
|
36
|
+
content = HTTPClient.new.get_content(url)
|
37
|
+
document = Oga.parse_xml(content)
|
38
|
+
document.xpath('rss/channel/item').each do |item|
|
39
|
+
title = item.at_xpath('title').text
|
40
|
+
description = item.at_xpath('description').text
|
36
41
|
unless(title.include?('VIDEO:') ||
|
37
42
|
title.include?('In pictures:') ||
|
38
43
|
title.include?('Morning business round-up'))
|
39
|
-
|
40
|
-
|
44
|
+
@rss_articles << Sanitize.clean(coder.decode(title)) + '. ' +
|
45
|
+
Sanitize.clean(coder.decode(description))
|
46
|
+
count += 1
|
47
|
+
break if count >= article_count
|
41
48
|
end
|
42
|
-
@rss_articles << (Sanitize.clean coder.decode words).split(',')
|
43
|
-
break if entry_count >= article_count
|
44
49
|
end
|
45
50
|
end
|
46
51
|
|
@@ -53,14 +58,12 @@ module CWG
|
|
53
58
|
end
|
54
59
|
|
55
60
|
def next_article
|
56
|
-
|
57
|
-
return unless
|
61
|
+
article = @rss_articles[article_index]
|
62
|
+
return unless article
|
58
63
|
inc_article_index
|
59
|
-
|
60
|
-
|
61
|
-
|
64
|
+
article.split.collect do |chars|
|
65
|
+
cw_chars(chars.strip.delete("\"").downcase)
|
66
|
+
end
|
62
67
|
end
|
63
|
-
|
64
68
|
end
|
65
|
-
|
66
69
|
end
|
data/lib/cw/tester.rb
CHANGED
@@ -227,6 +227,11 @@ module CWG
|
|
227
227
|
exit!
|
228
228
|
end
|
229
229
|
|
230
|
+
def cw_threads
|
231
|
+
@cw_threads ||= CWThreads.new(self, thread_processes)
|
232
|
+
end
|
233
|
+
|
234
|
+
|
230
235
|
def thread_processes
|
231
236
|
[
|
232
237
|
:monitor_keys_thread,
|
@@ -237,8 +242,7 @@ module CWG
|
|
237
242
|
|
238
243
|
def run words
|
239
244
|
@words = words
|
240
|
-
|
241
|
-
@cw_threads.run
|
245
|
+
cw_threads.run
|
242
246
|
@play = nil
|
243
247
|
reset_stdin
|
244
248
|
print.newline
|
data/lib/cw/tone_generator.rb
CHANGED
@@ -11,22 +11,48 @@ module CWG
|
|
11
11
|
include ToneHelpers
|
12
12
|
include FileDetails
|
13
13
|
|
14
|
+
attr_reader :code
|
15
|
+
|
16
|
+
class Code
|
17
|
+
|
18
|
+
include FileDetails
|
19
|
+
|
20
|
+
def initialize sample_rate, wpm
|
21
|
+
@sample_rate = sample_rate
|
22
|
+
@wpm = wpm
|
23
|
+
@spb_short = @sample_rate * 1.2 / @wpm
|
24
|
+
@spb_long = @sample_rate * 3.6 / @wpm
|
25
|
+
end
|
26
|
+
|
27
|
+
def filename element
|
28
|
+
self.send "#{element}_path"
|
29
|
+
end
|
30
|
+
|
31
|
+
def spb element
|
32
|
+
spb = element == :dash ?
|
33
|
+
@spb_long :
|
34
|
+
@spb_short
|
35
|
+
spb.to_i
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
14
39
|
def initialize
|
40
|
+
@sample_rate = 2400
|
15
41
|
@max_amplitude = (Cfg.config["volume"].to_f > 1.0 ?
|
16
42
|
1.0 : Cfg.config["volume"].to_f)
|
17
43
|
@wpm = Cfg.config["wpm"].to_f
|
18
44
|
@frequency = Cfg.config["frequency"].to_i
|
19
45
|
@effective_wpm = Cfg.config["effective_wpm"] ?
|
20
46
|
Cfg.config["effective_wpm"].to_f : @wpm
|
21
|
-
@sample_rate = 2400
|
22
47
|
@print = Print.new
|
23
48
|
end
|
24
49
|
|
25
|
-
def
|
50
|
+
def code
|
51
|
+
@code ||= Code.new(@sample_rate, @wpm)
|
52
|
+
end
|
26
53
|
|
54
|
+
def generate wrds
|
27
55
|
word_parts(wrds)
|
28
|
-
# progress.init elements.size * 3 + (wrds.size)
|
29
|
-
create_element_methods
|
30
56
|
compile_fundamentals
|
31
57
|
write_word_parts
|
32
58
|
end
|
@@ -35,11 +61,6 @@ module CWG
|
|
35
61
|
@play_filename ||= File.join(audio_dir, audio_filename)
|
36
62
|
end
|
37
63
|
|
38
|
-
def play
|
39
|
-
cmd = play_command + ' ' + play_filename
|
40
|
-
@pid = ! @dry_run ? Process.spawn(cmd) : cmd
|
41
|
-
end
|
42
|
-
|
43
64
|
def cw_encoding
|
44
65
|
@encoding ||= CwEncoding.new
|
45
66
|
end
|
@@ -48,21 +69,6 @@ module CWG
|
|
48
69
|
@progress ||= Progress.new('Compiling')
|
49
70
|
end
|
50
71
|
|
51
|
-
def data
|
52
|
-
{ :dot => {:name => :dot,
|
53
|
-
:filename => dot_path,
|
54
|
-
:spb => (@sample_rate * 1.2 / @wpm).to_i },
|
55
|
-
:dash => {:name => :dash,
|
56
|
-
:filename => dash_path,
|
57
|
-
:spb => (@sample_rate * 3.6 / @wpm).to_i },
|
58
|
-
:space => {:name => :space,
|
59
|
-
:filename => space_path,
|
60
|
-
:spb => (@sample_rate * 1.2 / @wpm).to_i },
|
61
|
-
:e_space => {:name => :e_space,
|
62
|
-
:filename => e_space_path,
|
63
|
-
:spb => (@sample_rate * 1.2 / @effective_wpm).to_i }}
|
64
|
-
end
|
65
|
-
|
66
72
|
def filter_maybe(size, count)
|
67
73
|
ramp = 0.05
|
68
74
|
ramp_point = @max_amplitude / ramp
|
@@ -83,11 +89,13 @@ module CWG
|
|
83
89
|
end
|
84
90
|
|
85
91
|
def generate_buffer audio_samples, ele
|
86
|
-
WaveFile::Buffer.new(audio_samples,
|
92
|
+
WaveFile::Buffer.new(audio_samples,
|
93
|
+
WaveFile::Format.new(:mono, :float, code.spb(ele)))
|
87
94
|
end
|
88
95
|
|
89
96
|
def write_element_audio_file ele, buffer
|
90
|
-
WaveFile::Writer.new(
|
97
|
+
WaveFile::Writer.new(code.filename(ele),
|
98
|
+
WaveFile::Format.new(:mono, :pcm_16, @sample_rate)) do |writer|
|
91
99
|
writer.write(buffer)
|
92
100
|
end
|
93
101
|
end
|
@@ -96,23 +104,9 @@ module CWG
|
|
96
104
|
[:dot, :dash, :space, :e_space]
|
97
105
|
end
|
98
106
|
|
99
|
-
# create dot, dash, space or e_space method
|
100
|
-
|
101
|
-
def create_element_method ele
|
102
|
-
define_singleton_method(ele) {data[ele]}
|
103
|
-
end
|
104
|
-
|
105
|
-
# create dot, dash, space and e_space methods
|
106
|
-
|
107
|
-
def create_element_methods
|
108
|
-
elements.each do |ele|
|
109
|
-
create_element_method ele
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
107
|
def generate_samples ele
|
114
|
-
return generate_space(
|
115
|
-
generate_tone(
|
108
|
+
return generate_space(code.spb(ele)) if space_sample? ele
|
109
|
+
generate_tone(code.spb(ele)) unless space_sample? ele
|
116
110
|
end
|
117
111
|
|
118
112
|
def compile_fundamentals
|
@@ -123,50 +117,43 @@ module CWG
|
|
123
117
|
end
|
124
118
|
end
|
125
119
|
|
120
|
+
def ewpm?
|
121
|
+
@effective_wpm != @wpm
|
122
|
+
end
|
123
|
+
|
126
124
|
def space_or_espace
|
127
|
-
|
125
|
+
{ name: (ewpm? ? :e_space : :space) }
|
128
126
|
end
|
129
127
|
|
130
128
|
def push_enc chr
|
131
129
|
arry = []
|
132
130
|
chr.each_with_index do |c,idx|
|
133
131
|
arry << c
|
134
|
-
arry << (
|
132
|
+
arry << (last_element?(idx, chr) ? space_or_espace : { name: :space })
|
135
133
|
end
|
136
134
|
arry += char_space
|
137
135
|
end
|
138
136
|
|
139
|
-
def send_char
|
140
|
-
enc =
|
141
|
-
|
142
|
-
|
143
|
-
else
|
144
|
-
enc = cw_encoding.fetch(c).map { |e| send(e)}
|
145
|
-
end
|
137
|
+
def send_char(c)
|
138
|
+
enc = c == ' ' ? [word_space] : cw_encoding.fetch(c).map { |e|
|
139
|
+
{ :name => e }
|
140
|
+
}
|
146
141
|
push_enc enc
|
147
142
|
end
|
148
143
|
|
149
|
-
def word_parts
|
144
|
+
def word_parts(str = nil)
|
150
145
|
return @word_parts if @word_parts
|
151
146
|
@word_parts = []
|
152
|
-
str.split('').each { |part| @word_parts << part}
|
147
|
+
str.split('').each { |part| @word_parts << part }
|
153
148
|
@word_parts
|
154
149
|
end
|
155
150
|
|
156
|
-
def make_word_parts
|
157
|
-
parts = []
|
158
|
-
@word_parts.each do |part|
|
159
|
-
parts += send_char part.downcase
|
160
|
-
end
|
161
|
-
parts
|
162
|
-
end
|
163
|
-
|
164
151
|
def prepare_buffers
|
165
152
|
@buffers = {}
|
166
153
|
elements.each do |ele|
|
167
154
|
@buffers[ele] = []
|
168
|
-
WaveFile::Reader.new(
|
169
|
-
|
155
|
+
WaveFile::Reader.new(code.filename(ele))
|
156
|
+
.each_buffer(code.spb(ele)) do |buffer|
|
170
157
|
@buffers[ele] = buffer
|
171
158
|
end
|
172
159
|
end
|
@@ -178,42 +165,40 @@ module CWG
|
|
178
165
|
end
|
179
166
|
|
180
167
|
def char_space
|
181
|
-
|
168
|
+
Array.new(2, word_space)
|
182
169
|
end
|
183
170
|
|
184
171
|
def word_space
|
185
|
-
|
172
|
+
{ name: (ewpm? ? :e_space : :space) }
|
186
173
|
end
|
187
174
|
|
188
|
-
def word_composite
|
175
|
+
def word_composite(word)
|
189
176
|
send_char word.downcase
|
190
177
|
end
|
191
178
|
|
192
|
-
def
|
193
|
-
WaveFile::
|
194
|
-
new(play_filename,
|
195
|
-
WaveFile::Format.
|
196
|
-
new(:mono,
|
197
|
-
:pcm_16,
|
198
|
-
@sample_rate)) do |writer|
|
199
|
-
yield.each do |char|
|
200
|
-
char.each do |fta|
|
201
|
-
writer.write(@buffers[fta[:name]])
|
202
|
-
end
|
203
|
-
end
|
204
|
-
end
|
179
|
+
def format
|
180
|
+
WaveFile::Format.new(:mono, :pcm_16, @sample_rate)
|
205
181
|
end
|
206
182
|
|
207
|
-
def
|
208
|
-
@
|
209
|
-
|
183
|
+
def write_buffer(writer, fta)
|
184
|
+
writer.write(@buffers[fta[:name]])
|
185
|
+
end
|
186
|
+
|
187
|
+
def write_audio
|
188
|
+
WaveFile::Writer.new(play_filename, format) do |writer|
|
189
|
+
yield.map { |ch| ch.map { |fta| write_buffer(writer, fta) } }
|
190
|
+
end
|
210
191
|
end
|
211
192
|
|
212
193
|
def write_audio_file
|
213
194
|
write_audio do
|
214
|
-
@word_parts.collect {|part| word_composite(part) }
|
195
|
+
@word_parts.collect { |part| word_composite(part) }
|
215
196
|
end
|
216
197
|
reset
|
217
198
|
end
|
199
|
+
|
200
|
+
def reset
|
201
|
+
@word_parts = @progress = nil
|
202
|
+
end
|
218
203
|
end
|
219
204
|
end
|
data/lib/cw/tone_helpers.rb
CHANGED
data/lib/cw/tx.rb
ADDED
@@ -0,0 +1,487 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module CWG
|
3
|
+
|
4
|
+
class Tx < Tester
|
5
|
+
|
6
|
+
include ToneHelpers
|
7
|
+
include FileDetails
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@max_amplitude = (Cfg.config["volume"].to_f > 1.0 ?
|
11
|
+
1.0 : Cfg.config["volume"].to_f)
|
12
|
+
@wpm = Cfg.config["wpm"].to_f
|
13
|
+
@frequency = Cfg.config["frequency"].to_i
|
14
|
+
@effective_wpm = Cfg.config["effective_wpm"] ?
|
15
|
+
Cfg.config["effective_wpm"].to_f : @wpm
|
16
|
+
@sample_rate = 2400
|
17
|
+
@print = Print.new
|
18
|
+
@words = []
|
19
|
+
@recording = []
|
20
|
+
print.rx "\n"
|
21
|
+
print.tx "\n"
|
22
|
+
print.menu "\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
def core_audio
|
26
|
+
@core_audio ||= Coreaudio.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_space words
|
30
|
+
str = ''
|
31
|
+
words.to_array.collect { |word| str << word + ' '}
|
32
|
+
str
|
33
|
+
end
|
34
|
+
|
35
|
+
def monitor_keys
|
36
|
+
@char = '#'
|
37
|
+
process_letter
|
38
|
+
|
39
|
+
loop do
|
40
|
+
@char = key_input.read
|
41
|
+
break if quit_key_input?
|
42
|
+
break if quit?
|
43
|
+
break if exit?
|
44
|
+
check_sentence_navigation(@char) if self.class == Book
|
45
|
+
check_clear @char
|
46
|
+
process_letter
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def check_clear char
|
51
|
+
if char == "\e"
|
52
|
+
@words = []
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def print_mode(mode)
|
57
|
+
msg = "\n\r" + (mode == :rx ?
|
58
|
+
"Receive Mode:" :
|
59
|
+
"Transmit Mode:") + "\n\r"
|
60
|
+
print.tx msg if :tx == mode
|
61
|
+
print.rx msg if :rx == mode
|
62
|
+
end
|
63
|
+
|
64
|
+
def receive_mode
|
65
|
+
print_mode :rx
|
66
|
+
loop do
|
67
|
+
char = key_input.read
|
68
|
+
check_clear char
|
69
|
+
case char
|
70
|
+
when '#'
|
71
|
+
print_mode :tx
|
72
|
+
return
|
73
|
+
when '|'
|
74
|
+
capture
|
75
|
+
when '\\'
|
76
|
+
info
|
77
|
+
when '>'
|
78
|
+
return '>'
|
79
|
+
when "\n"
|
80
|
+
puts "\n"
|
81
|
+
when "\r"
|
82
|
+
puts "\r"
|
83
|
+
when 'Q'
|
84
|
+
puts "Quitting..."
|
85
|
+
quit!
|
86
|
+
else
|
87
|
+
if(@recording.include? ' ')
|
88
|
+
@recording = [char]
|
89
|
+
else
|
90
|
+
@recording << char
|
91
|
+
end
|
92
|
+
puts 'here'
|
93
|
+
print.rx char
|
94
|
+
end
|
95
|
+
sleep 0.001
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def quit!
|
100
|
+
Cfg.config.params["exit"] = true
|
101
|
+
end
|
102
|
+
|
103
|
+
def capture
|
104
|
+
puts "\n\r"
|
105
|
+
puts "\r Menu: [C]allsign, [N]ame, "
|
106
|
+
puts "\r [L]ocation, [R]st, "
|
107
|
+
puts "\r [T]emperature, [S]tore,"
|
108
|
+
puts "\r [I]nfo, [W]PM, "
|
109
|
+
puts "\r [Q]uit capture!"
|
110
|
+
puts "\r"
|
111
|
+
puts "\r Use CAPITAL to capture"
|
112
|
+
|
113
|
+
loop do
|
114
|
+
char = key_input.read
|
115
|
+
# print.menu char
|
116
|
+
upcase = /[[:upper:]]/.match(char)
|
117
|
+
case char.downcase
|
118
|
+
when 'c'
|
119
|
+
return capture_attribute :callsign, upcase
|
120
|
+
when 'n'
|
121
|
+
return capture_attribute :name, upcase
|
122
|
+
when 'l'
|
123
|
+
return capture_attribute :qth, upcase
|
124
|
+
when 'r'
|
125
|
+
return capture_attribute :rst, upcase
|
126
|
+
when 't'
|
127
|
+
return capture_attribute :temperature, upcase
|
128
|
+
when 'i'
|
129
|
+
return info
|
130
|
+
when 's'
|
131
|
+
return store
|
132
|
+
when 'q'
|
133
|
+
puts "\rCancelled!"
|
134
|
+
return
|
135
|
+
else
|
136
|
+
puts "\nInvalid letter(#{char}) - try again!"
|
137
|
+
end
|
138
|
+
sleep 0.001
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def store
|
143
|
+
puts "\n\r"
|
144
|
+
puts "\rStoring info:"
|
145
|
+
info
|
146
|
+
@their_callsign
|
147
|
+
@their_name
|
148
|
+
@their_location
|
149
|
+
@their_rst
|
150
|
+
puts "\rCleared info!"
|
151
|
+
puts "\r"
|
152
|
+
end
|
153
|
+
|
154
|
+
def capture_attribute(attr_type, use_recording = false)
|
155
|
+
puts "\n\r"
|
156
|
+
puts "Enter their #{attr_type.to_s} followed by SPACE" unless use_recording
|
157
|
+
temp = use_recording ? @recording : []
|
158
|
+
print.menu temp.join('').delete(' ') if(@recording && ! use_recording)
|
159
|
+
@recording = []
|
160
|
+
if use_recording
|
161
|
+
write_attribute temp, attr_type
|
162
|
+
return
|
163
|
+
end
|
164
|
+
loop do
|
165
|
+
char = key_input.read
|
166
|
+
if key_input.is_relevant_char?(char)
|
167
|
+
temp << char
|
168
|
+
print.menu char unless use_recording
|
169
|
+
if ' ' == char
|
170
|
+
temp << char
|
171
|
+
write_attribute temp, attr_type
|
172
|
+
return
|
173
|
+
end
|
174
|
+
elsif char == 'Q'
|
175
|
+
puts "\n\n\rCancelled!\n\n\r"
|
176
|
+
return
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def write_attribute attr, type
|
182
|
+
clip = attr.join('').delete(' ')
|
183
|
+
@their_callsign = clip if :callsign == type
|
184
|
+
@their_name = clip if :name == type
|
185
|
+
@their_rst = clip if :rst == type
|
186
|
+
@their_qth = clip if :qth == type
|
187
|
+
@temperature = clip if :temperature == type
|
188
|
+
puts "\n\n\r#{type.to_s}: #{@their_callsign.upcase}\n\n\r" if :callsign == type
|
189
|
+
unless :callsign == type
|
190
|
+
puts "\n\n\rAttr: #{@temperature}°C\n\n\r" if :temperature == type
|
191
|
+
puts "\n\n\rTheir #{type.to_s}: #{clip}\n\n\r" unless :temperature == type
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def my_callsign
|
196
|
+
morsify "m0gzg "
|
197
|
+
end
|
198
|
+
|
199
|
+
def their_callsign
|
200
|
+
morsify @their_callsign unless @their_callsign.nil?
|
201
|
+
end
|
202
|
+
|
203
|
+
def return_with_k
|
204
|
+
acknowledge
|
205
|
+
morsify 'k '
|
206
|
+
end
|
207
|
+
|
208
|
+
def return_with_kn
|
209
|
+
morsify '+ '
|
210
|
+
acknowledge
|
211
|
+
morsify 'kn '
|
212
|
+
end
|
213
|
+
|
214
|
+
def acknowledge
|
215
|
+
their_callsign
|
216
|
+
morsify ' de '
|
217
|
+
my_callsign
|
218
|
+
end
|
219
|
+
|
220
|
+
def received_ok
|
221
|
+
acknowledge
|
222
|
+
morsify 'en en '
|
223
|
+
end
|
224
|
+
|
225
|
+
def sign_off
|
226
|
+
morsify "ok om #{@their_name} 73 es tnx fer nice qso = hpe 2cuagn sn + "
|
227
|
+
their_callsign
|
228
|
+
morsify ' de '
|
229
|
+
my_callsign
|
230
|
+
morsify ' sk '
|
231
|
+
end
|
232
|
+
|
233
|
+
def stop
|
234
|
+
morsify '= '
|
235
|
+
end
|
236
|
+
|
237
|
+
def long_cq
|
238
|
+
morsify "cqcq cqcq de m0gzg m0gzg k "
|
239
|
+
end
|
240
|
+
|
241
|
+
def morsify sentence
|
242
|
+
sentence.split('').collect{ |letr| char_to_tx letr }
|
243
|
+
end
|
244
|
+
|
245
|
+
def rst
|
246
|
+
morsify "rst is 599 5nn = "
|
247
|
+
end
|
248
|
+
|
249
|
+
def name
|
250
|
+
morsify "name is m a r t y n martyn = "
|
251
|
+
end
|
252
|
+
|
253
|
+
def qth
|
254
|
+
morsify "qth is o l d w o o d nr l i n c o l n lincoln = "
|
255
|
+
end
|
256
|
+
|
257
|
+
def weather
|
258
|
+
morsify "hr wx is sunny with some clouds = temp #{@temperature}c = "
|
259
|
+
end
|
260
|
+
|
261
|
+
def dit_dit
|
262
|
+
morsify 'e e '
|
263
|
+
end
|
264
|
+
|
265
|
+
def info
|
266
|
+
now = Time.now.utc
|
267
|
+
time = now.strftime("%H:%M:%S Z")
|
268
|
+
date = now.strftime("%d/%m/%y")
|
269
|
+
puts "\r----------------------------\r"
|
270
|
+
puts "Time: #{time}\r"
|
271
|
+
puts "Date: #{date}\r"
|
272
|
+
puts "Temp: #{@temperature}°C\r"
|
273
|
+
puts "\r"
|
274
|
+
callsign = @their_callsign.nil? ? '' : @their_callsign.upcase
|
275
|
+
puts "Their Call: #{callsign}\r"
|
276
|
+
puts "Their Name: #{@their_name}\r"
|
277
|
+
puts "Their RST: #{@their_rst}\r"
|
278
|
+
puts "Their qth: #{@their_qth}\r"
|
279
|
+
puts "\r----------------------------\r"
|
280
|
+
puts "\n\r"
|
281
|
+
end
|
282
|
+
|
283
|
+
def process_letter
|
284
|
+
@input_word ||= ''
|
285
|
+
case @char
|
286
|
+
when '#'
|
287
|
+
return_val = receive_mode
|
288
|
+
if '>' == return_val
|
289
|
+
return_with_k
|
290
|
+
else
|
291
|
+
@char = ''
|
292
|
+
end
|
293
|
+
when '.'
|
294
|
+
stop
|
295
|
+
when 'F'
|
296
|
+
@wpm += 1
|
297
|
+
@effective_wpm = @wpm
|
298
|
+
when 'S'
|
299
|
+
@wpm -= 1
|
300
|
+
@effective_wpm = @wpm
|
301
|
+
when '='
|
302
|
+
morsify '= '
|
303
|
+
when ','
|
304
|
+
morsify ', '
|
305
|
+
when '\\'
|
306
|
+
info
|
307
|
+
when '|'
|
308
|
+
capture
|
309
|
+
when '?'
|
310
|
+
morsify '? '
|
311
|
+
when '@'
|
312
|
+
long_cq
|
313
|
+
when '£'
|
314
|
+
my_callsign
|
315
|
+
when '$'
|
316
|
+
their_callsign
|
317
|
+
when '}'
|
318
|
+
sign_off
|
319
|
+
when '>'
|
320
|
+
return_with_k
|
321
|
+
when ')'
|
322
|
+
return_with_kn
|
323
|
+
when '('
|
324
|
+
received_ok
|
325
|
+
when '^'
|
326
|
+
qth
|
327
|
+
when '%'
|
328
|
+
rst
|
329
|
+
when '&'
|
330
|
+
name
|
331
|
+
when '*'
|
332
|
+
weather
|
333
|
+
when 'E'
|
334
|
+
dit_dit
|
335
|
+
when "\n"
|
336
|
+
puts "\n"
|
337
|
+
when "\r"
|
338
|
+
puts "\r"
|
339
|
+
when 'Q'
|
340
|
+
puts "Quitting..."
|
341
|
+
quit!
|
342
|
+
else
|
343
|
+
if key_input.is_relevant_char?(@char)
|
344
|
+
char_to_tx @char
|
345
|
+
end
|
346
|
+
end
|
347
|
+
@char = ''
|
348
|
+
end
|
349
|
+
|
350
|
+
def char_to_tx char
|
351
|
+
@words << char
|
352
|
+
# print.tx char
|
353
|
+
end
|
354
|
+
|
355
|
+
def thread_processes
|
356
|
+
[
|
357
|
+
:tx_words_thread,
|
358
|
+
:monitor_keys_thread,
|
359
|
+
# :print_words_thread
|
360
|
+
]
|
361
|
+
end
|
362
|
+
|
363
|
+
def word_composite word
|
364
|
+
send_char word.downcase
|
365
|
+
end
|
366
|
+
|
367
|
+
def push_enc chr
|
368
|
+
arry = []
|
369
|
+
chr.each_with_index do |c,idx|
|
370
|
+
arry << c
|
371
|
+
arry << ((last_element?(idx, chr)) ? (space_or_espace) : space)
|
372
|
+
end
|
373
|
+
arry += char_space
|
374
|
+
end
|
375
|
+
|
376
|
+
def cw_encoding
|
377
|
+
@encoding ||= CwEncoding.new
|
378
|
+
end
|
379
|
+
|
380
|
+
def space_or_espace
|
381
|
+
(@effective_wpm == @wpm) ? space : e_space
|
382
|
+
end
|
383
|
+
|
384
|
+
def char_space
|
385
|
+
@effective_wpm == @wpm ? [space,space] : [e_space,e_space]
|
386
|
+
end
|
387
|
+
|
388
|
+
def word_space
|
389
|
+
@effective_wpm == @wpm ? [space] : [e_space]
|
390
|
+
end
|
391
|
+
|
392
|
+
def send_char c
|
393
|
+
enc = nil
|
394
|
+
if c == ' '
|
395
|
+
enc = word_space
|
396
|
+
else
|
397
|
+
enc = cw_encoding.fetch(c).map { |e| send(e)}
|
398
|
+
end
|
399
|
+
push_enc enc
|
400
|
+
end
|
401
|
+
|
402
|
+
def audio_thread
|
403
|
+
temp = @word_parts.collect {|part| word_composite(part) }
|
404
|
+
@word_parts = nil
|
405
|
+
wpm = 1.5
|
406
|
+
temp.each do |letr|
|
407
|
+
letr.each do |ele|
|
408
|
+
if (:space == ele[:name]) || (:e_space == ele[:name])
|
409
|
+
core_audio.generate_silence(tone.data[
|
410
|
+
ele[:name]][:spb] * 20)
|
411
|
+
else
|
412
|
+
core_audio.generate_tone( tone.data[
|
413
|
+
ele[:name]][:spb] * 20)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
def generate wrds
|
420
|
+
word_parts(wrds)
|
421
|
+
create_element_methods
|
422
|
+
core_audio.start
|
423
|
+
cw_threads.add self, :audio_thread
|
424
|
+
# th = Thread.start do
|
425
|
+
cw_threads.join :audio_thread
|
426
|
+
sleep 0.001
|
427
|
+
core_audio.stop
|
428
|
+
cw_threads.kill_thread_x :audio_thread
|
429
|
+
# puts 'end thread'
|
430
|
+
end
|
431
|
+
|
432
|
+
def word_parts str = nil
|
433
|
+
return @word_parts if @word_parts
|
434
|
+
@word_parts = []
|
435
|
+
str.split('').each { |part| @word_parts << part}
|
436
|
+
@word_parts
|
437
|
+
end
|
438
|
+
|
439
|
+
def create_element_method ele
|
440
|
+
define_singleton_method(ele) {tone.data[ele]}
|
441
|
+
end
|
442
|
+
|
443
|
+
def create_element_methods
|
444
|
+
elements.each do |ele|
|
445
|
+
create_element_method ele
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
def elements
|
450
|
+
[:dot, :dash, :space, :e_space]
|
451
|
+
end
|
452
|
+
|
453
|
+
def tone
|
454
|
+
@tone ||= ToneGenerator.new
|
455
|
+
end
|
456
|
+
|
457
|
+
def cw_threads
|
458
|
+
@cw_threads ||= CWThreads.new(self, thread_processes)
|
459
|
+
end
|
460
|
+
|
461
|
+
def tx string
|
462
|
+
@winkey = Winkey.new
|
463
|
+
@winkey.on
|
464
|
+
@winkey.echo
|
465
|
+
@winkey.no_weighting
|
466
|
+
@winkey.wpm @wpm
|
467
|
+
# @winkey.string "ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890/=+?>(:;"
|
468
|
+
@winkey.string ' fb '
|
469
|
+
@winkey.wait_while_sending
|
470
|
+
cw_threads.run
|
471
|
+
# @winkey.close
|
472
|
+
end
|
473
|
+
|
474
|
+
def tx_words_thread
|
475
|
+
loop do
|
476
|
+
unless @words == []
|
477
|
+
temp = @words.shift
|
478
|
+
# generate temp
|
479
|
+
# p temp
|
480
|
+
@winkey.stringtemp.to_s.upcase
|
481
|
+
print.tx temp
|
482
|
+
end
|
483
|
+
sleep 0.01
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|