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