]> git.rkrishnan.org Git - dttsp.git/blob - jDttSP/keyd.c
Bug fixes to jsdr, keyer
[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;
64 sem_t clock_fired, keyer_started;
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;
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 // sound/silence generation
131 // tone turned on/off asynchronously
132 void
133 sound_thread(void) {
134   for (;;) {
135     sem_wait(&clock_fired);
136
137     if (playing) {
138       // CWTone keeps playing for awhile after it's turned off,
139       // in order to allow for a decay envelope;
140       // returns FALSE when it's actually done.
141       playing = CWTone(gen);
142       send_tone();
143     } else
144       send_silence();
145   }
146
147   pthread_exit(0);
148 }
149
150 // basic heartbeat
151 // returns actual dur in msec since last tick;
152 // uses Linux rtc interrupts.
153 // other strategies will work too, so long as they
154 // provide a measurable delay in msec.
155 double
156 timed_delay(void) {
157   double del, std = 1000 / (double) RTC_RATE;
158   static int cnt = 0;
159   unsigned long data;
160   
161   if (read(fdrtc, &data, sizeof(unsigned long)) == -1) {
162     perror("read");
163     exit(1);
164   }
165   // indicate whether an interrupt was missed
166   // not really important except for performance tweaks
167   if ((del = (data >> 010) * 1000 / (double) RTC_RATE) != std)
168     fprintf(stderr, "%d %g ms\n", ++cnt, del);
169   return del;
170 }
171
172 // key down? (real or via keyer logic)
173 BOOLEAN
174 read_key(double del) {
175   if (iambic)
176     return read_iambic_key_serial(ks, fdser, kl, del);
177   else
178     return read_straight_key_serial(ks, fdser);
179 }
180
181 // main keyer loop
182 // 
183 void
184 key_thread(void) {
185
186   sem_wait(&keyer_started);
187
188   for (;;) {
189     // wait for next tick, get actual dur since last one
190     double del = timed_delay();
191     // read key; tell keyer elapsed time since last call
192     BOOLEAN keydown = read_key(del);
193
194     if (!playing && keydown)
195       CWToneOn(gen), playing = TRUE;
196     else if (playing && !keydown)
197       CWToneOff(gen);
198
199     sem_post(&clock_fired);
200   }
201
202   pthread_exit(0);
203 }
204
205 PRIVATE void
206 jack_xrun(void *arg) {
207   char *str = "xrun";
208   write(2, str, strlen(str));
209 }
210
211 PRIVATE void
212 jack_shutdown(void *arg) {}
213
214 PRIVATE void
215 jack_callback(jack_nframes_t nframes, void *arg) {
216   float *lp, *rp;
217   int nbytes = nframes * sizeof(float);
218   if (nframes == size) {
219     // output: copy from ring to port
220     lp = (float *) jack_port_get_buffer(lport, nframes);
221     rp = (float *) jack_port_get_buffer(rport, nframes);
222     if (jack_ringbuffer_read_space(lring) >= nbytes) {
223       jack_ringbuffer_read(lring, (char *) lp, nbytes);
224       jack_ringbuffer_read(rring, (char *) rp, nbytes);
225     } else { // rb pathology
226       memset((char *) lp, 0, nbytes);
227       memset((char *) rp, 0, nbytes);
228       jack_ringbuffer_reset(lring);
229       jack_ringbuffer_reset(rring);
230       jack_ringbuffer_clear(lring, nbytes);
231       jack_ringbuffer_clear(rring, nbytes);
232       //write(2, "underrun\n", 9); 
233     }
234   }
235 }
236
237 int
238 main(int argc, char **argv) {
239   int i;
240   char *serialdev = "/dev/ttyS0",
241        *clockdev = "/dev/rtc";
242   int serstatus;
243
244   for (i = 1; i < argc; i++)
245     if (argv[i][0] == '-')
246       switch (argv[i][1]) {
247       case 'f':
248         freq = atof(argv[++i]);
249         break;
250       case 'i':
251         iambic = TRUE;
252         break;
253       case 'w':
254         wpm = atof(argv[++i]);
255         break;
256       default:
257         fprintf(stderr, "keyd [-i] [-w wpm]\n");
258         exit(1);
259       }
260     else break;
261
262   //------------------------------------------------------------
263
264   gen = newCWToneGen(-3.0, freq, 5.0, 5.0, TONE_SIZE, 48000.0);
265
266   //------------------------------------------------------------
267
268   kl = newKeyerLogic();
269   ks = newKeyerState();
270   ks->flag.iambic = TRUE;
271   ks->flag.revpdl = TRUE; // depends on port wiring
272   ks->flag.autospace.khar = ks->flag.autospace.word = FALSE;
273   ks->debounce = 1; // could be more if sampled faster
274   ks->mode = MODE_B;
275   ks->weight = 50;
276   ks->wpm = wpm;
277
278   //------------------------------------------------------------
279
280   if (!(client = jack_client_new("keyd")))
281     fprintf(stderr, "can't make client -- jack not running?\n"), exit(1);
282   jack_set_process_callback(client, (void *) jack_callback, 0);
283   jack_on_shutdown(client, (void *) jack_shutdown, 0);
284   jack_set_xrun_callback(client, (void *) jack_xrun, 0);
285   size = jack_get_buffer_size(client);
286   lport = jack_port_register(client,
287                              "ol",
288                              JACK_DEFAULT_AUDIO_TYPE,
289                              JackPortIsOutput,
290                              0);
291   rport = jack_port_register(client,
292                              "or",
293                              JACK_DEFAULT_AUDIO_TYPE,
294                              JackPortIsOutput,
295                              0);
296   lring = jack_ringbuffer_create(RING_SIZE);
297   rring = jack_ringbuffer_create(RING_SIZE);
298   jack_ringbuffer_clear(lring, size * sizeof(float));
299   jack_ringbuffer_clear(rring, size * sizeof(float));
300   
301   //------------------------------------------------------------
302
303   // key
304   if ((fdser = open(serialdev, O_WRONLY)) == -1) {
305     fprintf(stderr, "cannot open serial device %s", serialdev);
306     exit(1);
307   }
308   if (ioctl(fdser, TIOCMGET, &serstatus) == -1) {
309     close(fdser);
310     fprintf(stderr, "cannot get serial device status");
311     exit(1);
312   }
313   serstatus |= TIOCM_DTR;
314   if (ioctl(fdser, TIOCMSET, &serstatus) == -1) {
315     close(fdser);
316     fprintf(stderr, "cannot set serial device status");
317     exit(1);
318   }
319
320   // rtc
321   if ((fdrtc = open(clockdev, O_RDONLY)) == -1) {
322     perror(clockdev);
323     exit(1);
324   }
325   if (ioctl(fdrtc, RTC_IRQP_SET, RTC_RATE) == -1) {
326     perror("ioctl irqp");
327     exit(1);
328   }
329   if (ioctl(fdrtc, RTC_PIE_ON, 0) == -1) {
330     perror("ioctl pie on");
331     exit(1);
332   }
333
334   //------------------------------------------------------------
335
336   sem_init(&clock_fired, 0, 0);
337   sem_init(&keyer_started, 0, 0);
338   pthread_create(&play, 0, (void *) sound_thread, 0);
339   pthread_create(&key, 0, (void *) key_thread, 0);
340
341   //------------------------------------------------------------
342
343   jack_activate(client);
344   {
345     const char **ports;
346     if (!(ports = jack_get_ports(client, 0, 0, JackPortIsPhysical | JackPortIsInput))) {
347       fprintf(stderr, "can't find any physical playback ports\n");
348       exit(1);
349     }
350     if (jack_connect(client, jack_port_name(lport), ports[0])) {
351       fprintf(stderr, "can't connect left output\n");
352       exit(1);
353     }
354     if (jack_connect(client, jack_port_name(rport), ports[1])) {
355       fprintf(stderr, "can't connect left output\n");
356       exit(1);
357     }
358     free(ports);
359   }
360   sem_post(&keyer_started);
361
362   pthread_join(play, 0);
363   pthread_join(key, 0);
364   jack_client_close(client);
365
366   //------------------------------------------------------------
367
368   if (ioctl(fdrtc, RTC_PIE_OFF, 0) == -1) {
369     perror("ioctl pie off");
370     exit(1);
371   }
372   close(fdrtc);
373   close(fdser);
374
375   jack_ringbuffer_free(lring);
376   jack_ringbuffer_free(rring);
377
378   sem_destroy(&clock_fired);
379   sem_destroy(&keyer_started);
380
381   delCWToneGen(gen);
382   delKeyerState(ks);
383   delKeyerLogic(kl);
384
385   //------------------------------------------------------------
386
387   exit(0);
388 }