cw 0.3.1 → 0.3.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.
- 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
|