]> git.rkrishnan.org Git - dttsp.git/blob - jDttSP/keyd.c
updated command-vocabulary to reflect changes
[dttsp.git] / jDttSP / keyd.c
1 /* keyd.c */
2 /*
3 This file is part of a program that implements a Software-Defined Radio.
4
5 Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
21 The authors can be reached by email at
22
23 ab2kt@arrl.net
24 or
25 rwmcgwier@comcast.net
26
27 or by paper mail at
28
29 The DTTS Microwave Society
30 6 Kathleen Place
31 Bridgewater, NJ 08807
32 */  
33
34 #include <linux/rtc.h>
35 #include <fromsys.h>
36 #include <banal.h>
37 #include <splitfields.h>
38 #include <datatypes.h>
39 #include <bufvec.h>
40 #include <cxops.h>
41 #include <ringb.h>
42 #include <chan.h>
43 #include <oscillator.h>
44 #include <cwtones.h>
45 #include <keyer.h>
46
47 #define SAMP_RATE (48000)
48
49 // # times key is sampled per sec
50 #define RTC_RATE (64)
51
52 // # samples generated during 1 clock tick at RTC_RATE
53 #define TONE_SIZE (SAMP_RATE / RTC_RATE)
54
55 // ring buffer size; > 1 sec at this sr
56 #define RING_SIZE (01 << 020)
57
58 KeyerState ks;
59 KeyerLogic kl;
60
61 pthread_t play, key, update;
62 sem_t clock_fired, keyer_started, update_ok;
63
64 int fdser, fdrtc;
65
66 jack_client_t *client;
67 jack_port_t *lport, *rport;
68 jack_ringbuffer_t *lring, *rring;
69 jack_nframes_t size;
70
71 CWToneGen gen;
72 BOOLEAN playing = FALSE, iambic = FALSE;
73 double wpm = 18.0, freq = 750.0, ramp = 5.0, gain = -3.0;
74
75 //------------------------------------------------------------
76
77 void
78 jack_ringbuffer_clear(jack_ringbuffer_t *ring, int nbytes) {
79   int i;
80   char zero = 0;
81   for (i = 0; i < nbytes; i++)
82     jack_ringbuffer_write(ring, &zero, 1);
83 }
84
85 void
86 jack_ringbuffer_restart(jack_ringbuffer_t *ring, int nbytes) {
87   jack_ringbuffer_reset(ring);
88   jack_ringbuffer_clear(ring, nbytes);
89 }
90
91 //------------------------------------------------------------
92
93 // generated tone -> output ringbuffer
94 void
95 send_tone(void) {
96   if (jack_ringbuffer_write_space(lring) < TONE_SIZE * sizeof(float)) {
97     write(2, "overrun tone\n", 13);
98     jack_ringbuffer_restart(lring, TONE_SIZE * sizeof(float));
99     jack_ringbuffer_restart(rring, TONE_SIZE * sizeof(float));
100   } else {
101     int i;
102     for (i = 0; i < gen->size; i++) {
103       float l = CXBreal(gen->buf, i),
104             r = CXBimag(gen->buf, i);
105       jack_ringbuffer_write(lring, (char *) &l, sizeof(float));
106       jack_ringbuffer_write(rring, (char *) &r, sizeof(float));
107     }
108   }
109 }
110
111 // silence -> output ringbuffer
112 void
113 send_silence(void) {
114   if (jack_ringbuffer_write_space(lring) < TONE_SIZE * sizeof(float)) {
115     write(2, "overrun zero\n", 13);
116     jack_ringbuffer_restart(lring, TONE_SIZE * sizeof(float));
117     jack_ringbuffer_restart(rring, TONE_SIZE * sizeof(float));
118   } else {
119     int i;
120     for (i = 0; i < gen->size; i++) {
121       float zero = 0.0;
122       jack_ringbuffer_write(lring, (char *) &zero, sizeof(float));
123       jack_ringbuffer_write(rring, (char *) &zero, sizeof(float));
124     }
125   }
126 }
127
128 //------------------------------------------------------------------------
129
130 // sound/silence generation
131 // tone turned on/off asynchronously
132
133 void
134 sound_thread(void) {
135   for (;;) {
136     sem_wait(&clock_fired);
137
138     if (playing) {
139       // CWTone keeps playing for awhile after it's turned off,
140       // in order to allow for a decay envelope;
141       // returns FALSE when it's actually done.
142       playing = CWTone(gen);
143       send_tone();
144     } else {
145       send_silence();
146       // only let updates run when we've just generated silence
147       sem_post(&update_ok);
148     }
149   }
150
151   pthread_exit(0);
152 }
153
154 //------------------------------------------------------------------------
155
156 // basic heartbeat
157 // returns actual dur in msec since last tick;
158 // uses Linux rtc interrupts.
159 // other strategies will work too, so long as they
160 // provide a measurable delay in msec.
161
162 double
163 timed_delay(void) {
164   double del, std = 1000 / (double) RTC_RATE;
165   static int cnt = 0;
166   unsigned long data;
167   
168   if (read(fdrtc, &data, sizeof(unsigned long)) == -1) {
169     perror("read");
170     exit(1);
171   }
172   // indicate whether an interrupt was missed
173   // not really important except for performance tweaks
174   if ((del = (data >> 010) * 1000 / (double) RTC_RATE) != std)
175     fprintf(stderr, "%d %g ms\n", ++cnt, del);
176   return del;
177 }
178
179 // key down? (real or via keyer logic)
180
181 BOOLEAN
182 read_key(double del) {
183   if (iambic)
184     return read_iambic_key_serial(ks, fdser, kl, del);
185   else
186     return read_straight_key_serial(ks, fdser);
187 }
188
189 //------------------------------------------------------------------------
190
191 // main keyer loop
192
193 void
194 key_thread(void) {
195
196   sem_wait(&keyer_started);
197
198   for (;;) {
199     // wait for next tick, get actual dur since last one
200     double del = timed_delay();
201     // read key; tell keyer elapsed time since last call
202     BOOLEAN keydown = read_key(del);
203
204     if (!playing && keydown)
205       CWToneOn(gen), playing = TRUE;
206     else if (playing && !keydown)
207       CWToneOff(gen);
208
209     sem_post(&clock_fired);
210   }
211
212   pthread_exit(0);
213 }
214
215 //------------------------------------------------------------------------
216
217 // update keyer parameters via text input from stdin
218 // <wpm xxx> -> set keyer speed to xxx
219 // <gain xxx> -> set gain to xxx (dB)
220 // <freq xxx> -> set freq to xxx
221 // <ramp xxx> -> set attack/decay times to xxx ms
222
223 #define MAX_ESC (512)
224 #define ESC_L '<'
225 #define ESC_R '>'
226
227 void
228 updater(void) {
229   for (;;) {
230     int c;
231
232     // get or wait for next input char
233     if ((c = getchar()) == EOF) goto finish;
234
235     // if we see the beginning of a command,
236     if (c == ESC_L) {
237       int i = 0;
238       char buf[MAX_ESC];
239
240       // gather up the remainder
241       while ((c = getchar()) != EOF) {
242         if (c == ESC_R) break;
243         buf[i] = c;
244         if (++i >= (MAX_ESC - 1)) break;
245       }
246       if (c == EOF) goto finish;
247       buf[i] = 0;
248
249       // wait until changes are safe
250       sem_wait(&update_ok);
251
252       if (!strncmp(buf, "wpm", 3))
253         ks->wpm = wpm = atof(buf + 3);
254       else if (!strncmp(buf, "ramp", 4)) {
255         ramp = atof(buf + 4);
256         setCWToneGenVals(gen, gain, freq, ramp, ramp);
257       } else if (!strncmp(buf, "freq", 4)) {
258         freq = atof(buf + 4);
259         setCWToneGenVals(gen, gain, freq, ramp, ramp);
260       } else if (!strncmp(buf, "gain", 4)) {
261         gain = atof(buf + 4);
262         setCWToneGenVals(gen, gain, freq, ramp, ramp);
263       } else if (!strncmp(buf, "quit", 4))
264         goto finish;
265
266     } // otherwise go around again
267   }
268
269   // we saw an EOF or quit; kill other threads and exit neatly
270
271  finish:
272   pthread_cancel(play);
273   pthread_cancel(key);
274   pthread_exit(0);
275 }
276
277 //------------------------------------------------------------------------
278
279 PRIVATE void
280 jack_xrun(void *arg) {
281   char *str = "xrun";
282   write(2, str, strlen(str));
283 }
284
285 PRIVATE void
286 jack_shutdown(void *arg) {}
287
288 PRIVATE void
289 jack_callback(jack_nframes_t nframes, void *arg) {
290   float *lp, *rp;
291   int nbytes = nframes * sizeof(float);
292   if (nframes == size) {
293     // output: copy from ring to port
294     lp = (float *) jack_port_get_buffer(lport, nframes);
295     rp = (float *) jack_port_get_buffer(rport, nframes);
296     if (jack_ringbuffer_read_space(lring) >= nbytes) {
297       jack_ringbuffer_read(lring, (char *) lp, nbytes);
298       jack_ringbuffer_read(rring, (char *) rp, nbytes);
299     } else { // rb pathology
300       memset((char *) lp, 0, nbytes);
301       memset((char *) rp, 0, nbytes);
302       jack_ringbuffer_reset(lring);
303       jack_ringbuffer_reset(rring);
304       jack_ringbuffer_clear(lring, nbytes);
305       jack_ringbuffer_clear(rring, nbytes);
306       //write(2, "underrun\n", 9); 
307     }
308   }
309 }
310
311 int
312 main(int argc, char **argv) {
313   int i;
314   char *serialdev = "/dev/ttyS0",
315        *clockdev = "/dev/rtc";
316   int serstatus;
317
318   for (i = 1; i < argc; i++)
319     if (argv[i][0] == '-')
320       switch (argv[i][1]) {
321       case 'f':
322         freq = atof(argv[++i]);
323         break;
324       case 'i':
325         iambic = TRUE;
326         break;
327       case 'g':
328         gain = atof(argv[++i]);
329         break;
330       case 'r':
331         ramp = atof(argv[++i]);
332         break;
333       case 'w':
334         wpm = atof(argv[++i]);
335         break;
336       default:
337         fprintf(stderr,
338                 "keyd [-i] [-w wpm] [-g gain_dB] [-r ramp_ms]\n");
339         exit(1);
340       }
341     else break;
342
343   if (i < argc) {
344     if (!freopen(argv[i], "r", stdin))
345       perror(argv[i]), exit(1);
346     i++;
347   }
348
349   //------------------------------------------------------------
350
351   gen = newCWToneGen(gain, freq, ramp, ramp, TONE_SIZE, 48000.0);
352
353   //------------------------------------------------------------
354
355   kl = newKeyerLogic();
356   ks = newKeyerState();
357   ks->flag.iambic = TRUE;
358   ks->flag.revpdl = TRUE; // depends on port wiring
359   ks->flag.autospace.khar = ks->flag.autospace.word = FALSE;
360   ks->debounce = 1; // could be more if sampled faster
361   ks->mode = MODE_B;
362   ks->weight = 50;
363   ks->wpm = wpm;
364
365   //------------------------------------------------------------
366
367   if (!(client = jack_client_new("keyd")))
368     fprintf(stderr, "can't make client -- jack not running?\n"), exit(1);
369   jack_set_process_callback(client, (void *) jack_callback, 0);
370   jack_on_shutdown(client, (void *) jack_shutdown, 0);
371   jack_set_xrun_callback(client, (void *) jack_xrun, 0);
372   size = jack_get_buffer_size(client);
373   lport = jack_port_register(client,
374                              "ol",
375                              JACK_DEFAULT_AUDIO_TYPE,
376                              JackPortIsOutput,
377                              0);
378   rport = jack_port_register(client,
379                              "or",
380                              JACK_DEFAULT_AUDIO_TYPE,
381                              JackPortIsOutput,
382                              0);
383   lring = jack_ringbuffer_create(RING_SIZE);
384   rring = jack_ringbuffer_create(RING_SIZE);
385   jack_ringbuffer_clear(lring, size * sizeof(float));
386   jack_ringbuffer_clear(rring, size * sizeof(float));
387   
388   //------------------------------------------------------------
389
390   // key
391   if ((fdser = open(serialdev, O_WRONLY)) == -1) {
392     fprintf(stderr, "cannot open serial device %s", serialdev);
393     exit(1);
394   }
395   if (ioctl(fdser, TIOCMGET, &serstatus) == -1) {
396     close(fdser);
397     fprintf(stderr, "cannot get serial device status");
398     exit(1);
399   }
400   serstatus |= TIOCM_DTR;
401   if (ioctl(fdser, TIOCMSET, &serstatus) == -1) {
402     close(fdser);
403     fprintf(stderr, "cannot set serial device status");
404     exit(1);
405   }
406
407   // rtc
408   if ((fdrtc = open(clockdev, O_RDONLY)) == -1) {
409     perror(clockdev);
410     exit(1);
411   }
412   if (ioctl(fdrtc, RTC_IRQP_SET, RTC_RATE) == -1) {
413     perror("ioctl irqp");
414     exit(1);
415   }
416   if (ioctl(fdrtc, RTC_PIE_ON, 0) == -1) {
417     perror("ioctl pie on");
418     exit(1);
419   }
420
421   //------------------------------------------------------------
422
423   sem_init(&clock_fired, 0, 0);
424   sem_init(&keyer_started, 0, 0);
425   sem_init(&update_ok, 0, 0);
426   pthread_create(&play, 0, (void *) sound_thread, 0);
427   pthread_create(&key, 0, (void *) key_thread, 0);
428   pthread_create(&update, 0, (void *) updater, 0);
429
430   //------------------------------------------------------------
431
432   jack_activate(client);
433   {
434     const char **ports;
435     if (!(ports = jack_get_ports(client, 0, 0, JackPortIsPhysical | JackPortIsInput))) {
436       fprintf(stderr, "can't find any physical playback ports\n");
437       exit(1);
438     }
439     if (jack_connect(client, jack_port_name(lport), ports[0])) {
440       fprintf(stderr, "can't connect left output\n");
441       exit(1);
442     }
443     if (jack_connect(client, jack_port_name(rport), ports[1])) {
444       fprintf(stderr, "can't connect left output\n");
445       exit(1);
446     }
447     free(ports);
448   }
449   sem_post(&keyer_started);
450
451   pthread_join(play, 0);
452   pthread_join(key, 0);
453   pthread_join(update, 0);
454   jack_client_close(client);
455
456   //------------------------------------------------------------
457
458   if (ioctl(fdrtc, RTC_PIE_OFF, 0) == -1) {
459     perror("ioctl pie off");
460     exit(1);
461   }
462   close(fdrtc);
463   close(fdser);
464
465   jack_ringbuffer_free(lring);
466   jack_ringbuffer_free(rring);
467
468   sem_destroy(&clock_fired);
469   sem_destroy(&keyer_started);
470
471   delCWToneGen(gen);
472   delKeyerState(ks);
473   delKeyerLogic(kl);
474
475   //------------------------------------------------------------
476
477   exit(0);
478 }