SoDaRadio-5.0.3-master:8901fb5
CWGenerator.cxx
Go to the documentation of this file.
1 /*
2 Copyright (c) 2012, Matthew H. Reilly (kb1vc)
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8 
9  Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11  Redistributions in binary form must reproduce the above copyright
12  notice, this list of conditions and the following disclaimer in
13  the documentation and/or other materials provided with the
14  distribution.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 
29 #include "CWGenerator.hxx"
30 #include <ctype.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <stdio.h>
36 
37 using namespace SoDa;
38 
39 std::map<char, std::string> CWGenerator::morse_map;
40 
41 CWGenerator::CWGenerator(DatMBox * cw_env_stream, double _samp_rate, unsigned int _env_buf_len)
42 {
43  env_stream = cw_env_stream;
44  sample_rate = _samp_rate;
45  env_buf_len = _env_buf_len;
46 
47  if(morse_map.empty()) initMorseMap();
48 
49  // we want a rising/trailing edge attack that
50  // has a 5mS rise/fall time.
51  // Thats
52  edge_sample_count = (int) round(0.005 * sample_rate);
53 
54  rising_edge = new float[edge_sample_count];
55  falling_edge = new float[edge_sample_count];
56 
57  // make a soft rising edge
58  // and a soft falling edge
59  float ang = 0.0;
60  float ang_incr = M_PI / ((float) edge_sample_count);
61  for(unsigned int i = 0; i < edge_sample_count; i++) {
62  rising_edge[i] = 0.5 * (1.0 - cos(ang));
63  falling_edge[i] = 0.5 * (1.0 + cos(ang));
64  ang += ang_incr;
65  }
66 
67  // now allocate the worst case dit and dah buffers.
68  unsigned int dit_samples = (unsigned int) (round(sample_rate * 2.6));
69  dit = new float[dit_samples]; // "dit is mark + space"
70  dah = new float[dit_samples * 2]; // "dah is mark mark mark + space"
71  inter_word_space = new float[dit_samples * 3];
73 
74  // and set a default speed
75  setCWSpeed(10);
76 
77  // allocate our first outbound buffer
79  cur_buf_idx = 0;
80  // we really want a buffer that is the complex length.
82 
83  // how big is this buffer, and how many of them do I need for 1 second of TX time.
84  unsigned int blen = cur_buf->getComplexMaxLen();
85  bufs_per_sec = (unsigned int) (sample_rate / ((double) blen));
86 
87  // we aren't in the middle of a digraph right now.
88  in_digraph = false;
89 }
90 
91 
93 {
94  // if we've got less than 1 second's worth of
95  // elements buffered up, then return true;
96  unsigned int ifc = env_stream->inFlightCount();
97 
98  return (ifc < bufs_per_sec);
99 }
100 
102 {
103  morse_map['a'] = ".-";
104  morse_map['b'] = "-...";
105  morse_map['c'] = "-.-.";
106  morse_map['d'] = "-..";
107  morse_map['e'] = ".";
108  morse_map['f'] = "..-.";
109  morse_map['g'] = "--.";
110  morse_map['h'] = "....";
111  morse_map['i'] = "..";
112  morse_map['j'] = ".---";
113  morse_map['k'] = "-.-";
114  morse_map['l'] = ".-..";
115  morse_map['m'] = "--";
116  morse_map['n'] = "-.";
117  morse_map['o'] = "---";
118  morse_map['p'] = ".--.";
119  morse_map['q'] = "--.-";
120  morse_map['r'] = ".-.";
121  morse_map['s'] = "...";
122  morse_map['t'] = "-";
123  morse_map['u'] = "..-";
124  morse_map['v'] = "...-";
125  morse_map['w'] = ".--";
126  morse_map['x'] = "-..-";
127  morse_map['y'] = "-.--";
128  morse_map['z'] = "--..";
129  morse_map['0'] = "-----";
130  morse_map['1'] = ".----";
131  morse_map['2'] = "..---";
132  morse_map['3'] = "...--";
133  morse_map['4'] = "....-";
134  morse_map['5'] = ".....";
135  morse_map['6'] = "-....";
136  morse_map['7'] = "--...";
137  morse_map['8'] = "---..";
138  morse_map['9'] = "----.";
139  morse_map['.'] = ".-.-.-";
140  morse_map[','] = "--..--";
141  morse_map['?'] = "..--..";
142  morse_map['-'] = "-...-";
143  morse_map['/'] = "-..-.";
144 }
145 
146 void CWGenerator::setCWSpeed(unsigned int wpm)
147 {
148  if(wpm < 1) wpm = 1;
149  if(wpm > 50) wpm = 50;
150  words_per_minute = wpm;
151 
152  float dot_time_s = 1.20 / ((float) wpm);
153  unsigned int dot_samples = (int) round(sample_rate * dot_time_s);
154  unsigned int dah_samples = dot_samples * 3;
155 
156  // now create the dit, dah, and space pattern.
157  unsigned int i, j;
158  for(i = 0; i < edge_sample_count; i++) {
159  dit[i] = rising_edge[i];
160  }
161  for(; i < (dot_samples - edge_sample_count); i++) {
162  dit[i] = 1.0;
163  }
164  for(j = 0; i < dot_samples; i++, j++) {
165  dit[i] = falling_edge[j];
166  }
167  for(; i < dot_samples * 2; i++) {
168  dit[i] = 0.0;
169  }
170  dit_len = i;
171 
172  for(i = 0; i < edge_sample_count; i++) {
173  dah[i] = rising_edge[i];
174  }
175  for(; i < (dah_samples - edge_sample_count); i++) {
176  dah[i] = 1.0;
177  }
178  for(j = 0; i < dah_samples; i++, j++) {
179  dah[i] = falling_edge[j];
180  }
181  for(; i < dah_samples + dot_samples; i++) {
182  dah[i] = 0.0;
183  }
184  dah_len = i;
185 
186  ics_len = dit_len;
187  iws_len = dit_len * 3;
188  first_iws_len = iws_len - (ics_len / 2);
189 
190  for(i = 0; i < iws_len; i++) {
191  inter_word_space[i] = 0.0;
192  }
193 }
194 
195 void CWGenerator::appendToOut(const float * v, unsigned int vlen)
196 {
197  int svlen = vlen;
198  while(svlen != 0) {
199  int left = cur_buf_len - (cur_buf_idx + 1);
200  float *dbuf = cur_buf->getFloatBuf();
201 
202  if(svlen <= left) {
203  memcpy(&(dbuf[cur_buf_idx]), v, svlen * sizeof(float));
204  cur_buf_idx += svlen;
205  svlen = 0;
206  }
207  else {
208  // fill up the remainder of this buffer.
209  int remlen = cur_buf_len - cur_buf_idx;
210  memcpy(&(dbuf[cur_buf_idx]), v, remlen * sizeof(float));
211  v += remlen;
212  svlen -= remlen;
213  cur_buf_idx += remlen;
214  }
215 
216  if(cur_buf_idx >= cur_buf_len) {
217  // post the buffer.
219  cur_buf = getFreeSoDaBuf();
220  cur_buf_idx = 0;
222  }
223  }
224 }
225 
227 {
228  if(cur_buf_idx > 0) {
229  float *dbuf = cur_buf->getFloatBuf();
230  for(; cur_buf_idx < cur_buf_len; cur_buf_idx++) {
231  // fill the rest with zeros
232  dbuf[cur_buf_idx] = 0.0;
233  }
234  // post the buffer.
237  cur_buf_idx = 0;
238  cur_buf_len = cur_buf->getComplexMaxLen();
239  }
240 }
241 
243 {
244  if(cur_buf != NULL) {
245  cur_buf_idx = 0;
248  }
249 }
250 
251 // returns true if the operation resulted in
252 // something that took time... (an actual element)
254 {
255  c = tolower(c);
256  if(c == '_') {
257  in_digraph = true;
258  return false;
259  }
260 
261  if(c == ' ') {
262  in_digraph = false;
263  if(last_was_space) {
265  }
266  else {
268  }
269  last_was_space = true;
270  return true;
271  }
272  else {
273  last_was_space = false;
274  }
275 
276  if(morse_map.find(c) == morse_map.end()) return false;
277 
278  std::string symb = morse_map[c];
279 
280  for(std::string::iterator si = symb.begin();
281  si != symb.end();
282  ++si) {
283  if(*si == '.') {
285  }
286  else if(*si == '-') {
288  }
289  }
291  else in_digraph = false;
292 
293  return true;
294 }
295 
296 
297 
float * inter_char_space
prototype space between characters
unsigned int words_per_minute
unsigned int env_buf_len
the length of an envelope buffer
unsigned int bufs_per_sec
number of envelope buffers required per second.
bool in_digraph
if true, we&#39;re sending a two-character (no inter-char space) sequence (like _AR)
unsigned int cur_buf_len
how much of the buffer is unfilled?
float * dah
prototype dah buffer
unsigned int first_iws_len
number of samples in prototype space between words
void flushBuffer()
push the current buffer out to the transmitter, filling it with zeros
unsigned int getComplexMaxLen()
Return the maximum number of complex float values that this buffer can hold.
Definition: SoDaBase.hxx:103
bool last_was_space
if true, next interword space should be a little short
float * inter_word_space
prototype space between words
float * getFloatBuf()
Return a pointer to the storage buffer of floats.
Definition: SoDaBase.hxx:137
void setCWSpeed(unsigned int wpm)
set the speed of the cw stream in words per minute
float * dit
prototype dit buffer
double sample_rate
we need to know how long a sample is (in time)
float * rising_edge
a gentle shape for the leading edge of a pulse
bool sendChar(char c)
encode a character into the envelope buffer
void initMorseMap()
setup the mapping from ascii character to morse sequence
DatMBox * env_stream
this is the stream we send envelope buffers into.
unsigned int ics_len
number of samples in prototype space between characters
unsigned int dit_len
number of samples in prototype dit
SoDaBuf * getFreeSoDaBuf()
get an envelope that we can fill in
float * falling_edge
a gentle shape for the trailing edge of a pulse
void clearBuffer()
empty the current envelope, don&#39;t send it along to the TX unit
unsigned int inFlightCount()
Definition: MultiMBox.hxx:153
unsigned int edge_sample_count
edges are &#39;pre-built&#39; this is the length of an edge, in samples
SoDaBuf * cur_buf
the current envelope to be filled in
unsigned int dah_len
number of samples in prototype dah
bool readyForMore()
check envelope stream to see if we have less than 1 second&#39;s worth of stuff enqueued ...
Definition: CWGenerator.cxx:92
bool setFloatLen(unsigned int nl)
set the length of the buffer (in number of floats.)
Definition: SoDaBase.hxx:124
void put(T *m)
Definition: MultiMBox.hxx:97
void appendToOut(const float *v, unsigned int vlen)
add a buffer of envelope pieces to the outgoing envelope buffer
unsigned int cur_buf_idx
where are we in the buffer?
CWGenerator(DatMBox *cw_env_stream, double _samp_rate, unsigned int _env_buf_len)
Constructor.
Definition: CWGenerator.cxx:41
static std::map< char, std::string > morse_map
map from ascii character to dits-and-dahs
unsigned int iws_len
if space is repeated, number of samples in prototype space between words