1 /* CWirc - X-Chat plugin for sending and receiving raw morse code over IRC
2 (c) Pierre-Philippe Coupard - 18/06/2003
3
4 Morse decoder
5
6 This program is distributed under the terms of the GNU General Public License
7 See the COPYING file for details
8 */
9 #include <stdio.h>
10 #include <string.h>
11
12 #include "types.h"
13 #include "cwdecoder.h"
14 #include "morsecodes.h"
15 #include "cwirc.h"
16
17
18
19 /* Definitions */
20 #define CW_BUF_MAX_SIZE CW_SEQUENCE_MAX+2 /* Max. dits or dahs we buffer*/
21 #define DECODER_INERTIA 4 /* How slowly the decoder adapts
22 to a new keying speed */
23
24
25
26 /* Global variables */
27 struct cwcodeset cwirc_cw_table[NB_CW_CODE_SETS]=MORSE_CODES;
28
29
30
31 /* Prototypes */
32 static void decode_morse_code(T_BOOL key,double ticklen,int cwcodeset,
33 T_BOOL reset_decoder);
34 static void decode_dot_code(T_BOOL key,double ticklen,int cwcodeset,
35 T_BOOL reset_decoder);
36 static char *decode_cw_sequence(int cwcodeset,char *cwbuf,char *decoded_char);
37 static void insert_character_in_decoder_buffer(char *c,T_BOOL delete_spaces);
38
39
40
41 /* Functions */
cwirc_decode_cw(T_BOOL key,double ticklen,int cwcodeset)42 void cwirc_decode_cw(T_BOOL key,double ticklen,int cwcodeset)
43 {
44 /* Is the decoder disabled ? */
45 if(cwirc_cw_table[sharedmem->cwcodeset].cwcodetype==NOCODE)
46 if(sharedmem->decoded_msg_wpm!=WPM_DECODER_DISABLED)
47 sharedmem->reset_decoder=1;
48 else
49 return;
50 else
51 if(sharedmem->decoded_msg_wpm==WPM_DECODER_DISABLED)
52 sharedmem->decoded_msg_wpm=WPM_UNKNOWN_WPM;
53
54 /* Have we been asked to reset the decoder ? */
55 if(sharedmem->reset_decoder)
56 {
57 decode_morse_code(key,ticklen,cwcodeset,1);
58 decode_dot_code(key,ticklen,cwcodeset,1);
59 sharedmem->decoded_msg_buf[0]=0;
60 sharedmem->decoded_msg_buf_char_markers[0]=0;
61 sharedmem->decoded_msg_wpm=cwirc_cw_table[sharedmem->cwcodeset].cwcodetype
62 ==NOCODE?WPM_DECODER_DISABLED:WPM_UNKNOWN_WPM;
63 sharedmem->decoded_msg_updated=1;
64 sharedmem->reset_decoder=0;
65 }
66
67 /* Is the chosen code set in Morse code ? */
68 if(cwirc_cw_table[cwcodeset].cwcodetype==MORSE)
69 {
70 decode_morse_code(key,ticklen,cwcodeset,0);
71 return;
72 }
73
74 /* Is the chosen code set in DOT code ? */
75 if(cwirc_cw_table[cwcodeset].cwcodetype==DOT)
76 {
77 decode_dot_code(key,ticklen,cwcodeset,0);
78 return;
79 }
80 }
81
82
83
84 /* Decode international Morse code */
decode_morse_code(T_BOOL key,double ticklen,int cwcodeset,T_BOOL reset_decoder)85 void decode_morse_code(T_BOOL key,double ticklen,int cwcodeset,
86 T_BOOL reset_decoder)
87 {
88 static double evtlen=0;
89 static double beeplenprev=0,lastshortbeeplen=0,lastlongbeeplen=0;
90 static double lastbeep_too_short_by=0; /* in ms */
91 static double ditlen=0;
92 static T_BOOL keyprev=0;
93 static char cwbuf[CW_BUF_MAX_SIZE]="";
94 static T_BOOL wait_for_end_of_word=0;
95 static char newbeep=0;
96 char decoded_char[CW_SYMBOL_MAX+1]="";
97 int i;
98
99 /* Should we reset the decoder's internal states? */
100 if(reset_decoder)
101 {
102 evtlen=0;
103 beeplenprev=0;
104 lastshortbeeplen=0;
105 lastlongbeeplen=0;
106 lastbeep_too_short_by=0;
107 ditlen=0;
108 keyprev=0;
109 cwbuf[0]=0;
110 wait_for_end_of_word=0;
111 newbeep=0;
112 decoded_char[0]=0;
113
114 return;
115 }
116
117 /* Did the key change state ? */
118 if(key!=keyprev)
119 {
120 wait_for_end_of_word=0;
121
122 if(!key) /* Was the key released ? */
123 {
124 if(evtlen<beeplenprev/2)
125 newbeep='.';
126 else if(evtlen>beeplenprev*2)
127 newbeep='-';
128
129 if((i=strlen(cwbuf))<CW_BUF_MAX_SIZE-1)
130 {
131 cwbuf[i]=newbeep;
132 cwbuf[i+1]=0;
133 }
134
135 if(newbeep=='.')
136 lastshortbeeplen=evtlen;
137 else
138 lastlongbeeplen=evtlen;
139
140 /* Progressively deviate the "official" dit length toward the last short
141 beep length, so the decoder isn't thrown off track immediately when the
142 operator made a temporary timing mistake */
143 if(lastlongbeeplen!=0 && lastshortbeeplen!=0)
144 ditlen=(ditlen*(DECODER_INERTIA-1)+
145 ((3*lastshortbeeplen+lastlongbeeplen)/6))/DECODER_INERTIA;
146 else
147 if(lastshortbeeplen!=0)
148 ditlen=(ditlen*(DECODER_INERTIA-1)+lastshortbeeplen)/DECODER_INERTIA;
149 else
150 ditlen=(ditlen*(DECODER_INERTIA-1)+lastlongbeeplen/3)/DECODER_INERTIA;
151
152 if(newbeep=='.')
153 lastbeep_too_short_by=ditlen>evtlen?ditlen-evtlen:0;
154 else
155 lastbeep_too_short_by=ditlen*3>evtlen?ditlen*3-evtlen:0;
156
157 beeplenprev=evtlen;
158 }
159 evtlen=0;
160 }
161
162 /* If the key has been released for over a dit length * 2, decode the
163 character. Allow for timing imprecision if the last beep was too short */
164 if(!key && !keyprev && (evtlen-lastbeep_too_short_by)>ditlen*2 && cwbuf[0])
165 {
166 /* Decode the cw character */
167 decode_cw_sequence(cwcodeset,cwbuf,decoded_char);
168
169 /* Empty the buffer */
170 cwbuf[0]=0;
171
172 wait_for_end_of_word=1;
173 }
174
175 /* If the key has been released for over a dit length * 4, send a space.
176 Allow for timing imprecision if the last beep was too short */
177 if((evtlen-lastbeep_too_short_by)>ditlen*4 && wait_for_end_of_word)
178 {
179 strcpy(decoded_char," ");
180 wait_for_end_of_word=0;
181 }
182
183 /* Is there a new decoded character ? */
184 if(decoded_char[0])
185 {
186 /* Add the character in the buffer, or remove the last character */
187 insert_character_in_decoder_buffer(decoded_char,1);
188 decoded_char[0]=0;
189
190 /* Report the current decoder's speed */
191 if(ditlen>0)
192 sharedmem->decoded_msg_wpm=1200/ditlen;
193 else
194 sharedmem->decoded_msg_wpm=WPM_UNKNOWN_WPM;
195
196 /* Assert this for the display routine */
197 sharedmem->decoded_msg_updated=1;
198 }
199
200 keyprev=key;
201 evtlen+=ticklen;
202 }
203
204
205
206 /* Decode DOT code */
decode_dot_code(T_BOOL key,double ticklen,int cwcodeset,T_BOOL reset_decoder)207 void decode_dot_code(T_BOOL key,double ticklen,int cwcodeset,
208 T_BOOL reset_decoder)
209 {
210 static double evtlen=0;
211 static double ditlen=0;
212 static T_BOOL keyprev=0;
213 static int dotcount=0;
214 static char cwbuf[CW_BUF_MAX_SIZE]="";
215 char decoded_char[CW_SYMBOL_MAX+1]="";
216 int i;
217
218 /* Should we reset the decoder's internal states? */
219 if(reset_decoder)
220 {
221 evtlen=0;
222 ditlen=0;
223 keyprev=0;
224 dotcount=0;
225 cwbuf[0]=0;
226 decoded_char[0]=0;
227
228 return;
229 }
230
231 /* Did the key change state ? */
232 if(key!=keyprev)
233 {
234 if(!key) /* Was the key released ? */
235 {
236 if(dotcount<9)
237 dotcount++; /* Count dots in the current character element */
238
239 /* Progressively deviate the "official" dit length toward the last short
240 beep length, so the decoder isn't thrown off track immediately when the
241 operator made a temporary timing mistake */
242 ditlen=(ditlen*(DECODER_INERTIA-1)+evtlen)/DECODER_INERTIA;
243 }
244 evtlen=0;
245 }
246
247 /* If the key has been released for over a dit length * 2 and there were dots
248 counted, add the current character element to the buffer. */
249 if(!key && !keyprev && evtlen>ditlen*2 && dotcount &&
250 (i=strlen(cwbuf))<CW_BUF_MAX_SIZE-1)
251 {
252 cwbuf[i]='0'+dotcount;
253 cwbuf[i+1]=0;
254 dotcount=0;
255 }
256
257 /* If the key has been released for over a dit length * 5 and there are
258 character elements to decode, decode the character */
259 if(evtlen>ditlen*5 && cwbuf[0])
260 {
261 /* Decode the cw character */
262 decode_cw_sequence(cwcodeset,cwbuf,decoded_char);
263
264 /* Empty the buffer */
265 cwbuf[0]=0;
266 }
267
268 /* Is there a new decoded character ? */
269 if(decoded_char[0])
270 {
271 /* Add the character in the buffer, or remove the last character */
272 insert_character_in_decoder_buffer(decoded_char,0);
273 decoded_char[0]=0;
274
275 /* Report the current decoder's speed */
276 if(ditlen>0)
277 sharedmem->decoded_msg_wpm=600/ditlen; /* Calculated on the basis of
278 100 dits per PARIS word in
279 DOT code */
280 else
281 sharedmem->decoded_msg_wpm=WPM_UNKNOWN_WPM;
282
283 /* Assert this for the display routine */
284 sharedmem->decoded_msg_updated=1;
285 }
286
287 keyprev=key;
288 evtlen+=ticklen;
289 }
290
291
292
293 /* Find a CW sequence in the right table and return it */
decode_cw_sequence(int cwcodeset,char * cwbuf,char * decoded_char)294 static char *decode_cw_sequence(int cwcodeset,char *cwbuf,char *decoded_char)
295 {
296 int i;
297
298 /* Look for a matching sequence */
299 for(i=0;cwirc_cw_table[cwcodeset].cwcode[i].sequence[0] &&
300 strcmp(cwbuf,cwirc_cw_table[cwcodeset].cwcode[i].sequence);i++);
301
302 /* Did we find a sequence ? */
303 if(cwirc_cw_table[cwcodeset].cwcode[i].sequence[0])
304 strcpy(decoded_char,cwirc_cw_table[cwcodeset].cwcode[i].symbol);
305 else
306 {
307 /* Is it a continuous stream of dits ? */
308 if(strchr(cwbuf,'-')==NULL)
309 strcpy(decoded_char,"\b"); /* It's an "error" morse code */
310 else
311 strcpy(decoded_char,UNKNOWN_CHARACTER_SIGN);/* We don't know what it is */
312 }
313
314 return(decoded_char);
315 }
316
317
318
319 /* Insert a decoded character in the decoder buffer, or remove the last one
320 if the character is a backspace */
insert_character_in_decoder_buffer(char * c,T_BOOL delete_spaces)321 static void insert_character_in_decoder_buffer(char *c,T_BOOL delete_spaces)
322 {
323 int i,j,k;
324
325 k=strlen(sharedmem->decoded_msg_buf);
326
327 /* Is the character a BACKSPACE ? */
328 if(c[0]=='\b')
329 {
330 if(delete_spaces)
331 {
332 /* Zap the last spaces in the decoded message line */
333 while(k && sharedmem->decoded_msg_buf[k-1]==' ')
334 {
335 sharedmem->decoded_msg_buf[--k]=0;
336 sharedmem->decoded_msg_buf_char_markers[k]=0;
337 }
338 }
339
340 /* Zap the last character in the decoded message line */
341 while(k && sharedmem->decoded_msg_buf_char_markers[k-1]==' ')
342 {
343 sharedmem->decoded_msg_buf[--k]=0;
344 sharedmem->decoded_msg_buf_char_markers[k]=0;
345 }
346 if(k)
347 {
348 sharedmem->decoded_msg_buf[--k]=0;
349 sharedmem->decoded_msg_buf_char_markers[k]=0;
350 }
351 }
352 else
353 {
354 /* Do we need to make room in the decoded message buffer for the new
355 character ? */
356 j=strlen(c);
357 if(j+k>=DECODED_MSG_SIZE)
358 {
359 /* Find out the position of the character that'll become the new
360 leading character of the decoded message buffer */
361 for(i=j+k+1-DECODED_MSG_SIZE;
362 sharedmem->decoded_msg_buf_char_markers[i]==' ';i++);
363
364 /* Shift the decoded message left accordingly */
365 for(k=i;sharedmem->decoded_msg_buf[k];k++)
366 {
367 sharedmem->decoded_msg_buf[k-i]=sharedmem->decoded_msg_buf[k];
368 sharedmem->decoded_msg_buf_char_markers[k-i]=
369 sharedmem->decoded_msg_buf_char_markers[k];
370 }
371 sharedmem->decoded_msg_buf[k-i]=0;
372 sharedmem->decoded_msg_buf_char_markers[k-i]=0;
373 }
374
375 /* Append the new character in the buffer */
376 strcat(sharedmem->decoded_msg_buf,c);
377
378 /* Append the new character's start marker in the markers buffer */
379 for(i=0;i<j;i++)
380 strcat(sharedmem->decoded_msg_buf_char_markers,i?" ":"|");
381 }
382 }
383