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