3 This file is part of a program that implements a Software-Defined Radio.
5 Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY
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.
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.
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
21 The authors can be reached by email at
29 The DTTS Microwave Society
34 #include <linux/rtc.h>
37 #include <splitfields.h>
38 #include <datatypes.h>
43 #include <oscillator.h>
47 #define SAMP_RATE (48000)
49 // # times key is sampled per sec
50 // > 64 requires root on Linux
51 //#define RTC_RATE (128)
54 // # samples generated during 1 clock tick at RTC_RATE
55 #define TONE_SIZE (SAMP_RATE / RTC_RATE)
57 // ring buffer size; > 1 sec at this sr
58 #define RING_SIZE (01 << 020)
63 pthread_t play, key, update;
64 sem_t clock_fired, keyer_started, update_ok;
68 jack_client_t *client;
69 jack_port_t *lport, *rport;
70 jack_ringbuffer_t *lring, *rring;
74 BOOLEAN playing = FALSE, iambic = FALSE;
75 double wpm = 18.0, freq = 750.0, ramp = 5.0, gain = -3.0;
77 //------------------------------------------------------------
80 jack_ringbuffer_clear(jack_ringbuffer_t *ring, int nbytes) {
83 for (i = 0; i < nbytes; i++)
84 jack_ringbuffer_write(ring, &zero, 1);
88 jack_ringbuffer_restart(jack_ringbuffer_t *ring, int nbytes) {
89 jack_ringbuffer_reset(ring);
90 jack_ringbuffer_clear(ring, nbytes);
93 //------------------------------------------------------------
95 // generated tone -> output ringbuffer
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));
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));
113 // silence -> output ringbuffer
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));
122 for (i = 0; i < gen->size; i++) {
124 jack_ringbuffer_write(lring, (char *) &zero, sizeof(float));
125 jack_ringbuffer_write(rring, (char *) &zero, sizeof(float));
130 //------------------------------------------------------------------------
132 // sound/silence generation
133 // tone turned on/off asynchronously
138 sem_wait(&clock_fired);
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);
148 // only let updates run when we've just generated silence
149 sem_post(&update_ok);
156 //------------------------------------------------------------------------
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.
166 double del, std = 1000 / (double) RTC_RATE;
170 if (read(fdrtc, &data, sizeof(unsigned long)) == -1) {
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);
181 // key down? (real or via keyer logic)
184 read_key(double del) {
186 return read_iambic_key_serial(ks, fdser, kl, del);
188 return read_straight_key_serial(ks, fdser);
191 //------------------------------------------------------------------------
198 sem_wait(&keyer_started);
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);
206 if (!playing && keydown)
207 CWToneOn(gen), playing = TRUE;
208 else if (playing && !keydown)
211 sem_post(&clock_fired);
217 //------------------------------------------------------------------------
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
225 #define MAX_ESC (512)
234 // get or wait for next input char
235 if ((c = getchar()) == EOF) goto finish;
237 // if we see the beginning of a command,
242 // gather up the remainder
243 while ((c = getchar()) != EOF) {
244 if (c == ESC_R) break;
246 if (++i >= (MAX_ESC - 1)) break;
248 if (c == EOF) goto finish;
251 // wait until changes are safe
252 sem_wait(&update_ok);
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))
268 } // otherwise go around again
271 // we saw an EOF or quit; kill other threads and exit neatly
274 pthread_cancel(play);
279 //------------------------------------------------------------------------
282 jack_xrun(void *arg) {
284 write(2, str, strlen(str));
288 jack_shutdown(void *arg) {}
291 jack_callback(jack_nframes_t nframes, void *arg) {
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);
314 main(int argc, char **argv) {
316 char *serialdev = "/dev/ttyS0",
317 *clockdev = "/dev/rtc";
320 for (i = 1; i < argc; i++)
321 if (argv[i][0] == '-')
322 switch (argv[i][1]) {
324 freq = atof(argv[++i]);
330 gain = atof(argv[++i]);
333 ramp = atof(argv[++i]);
336 wpm = atof(argv[++i]);
340 "keyd [-i] [-w wpm] [-g gain_dB] [-r ramp_ms]\n");
346 if (!freopen(argv[i], "r", stdin))
347 perror(argv[i]), exit(1);
351 //------------------------------------------------------------
353 gen = newCWToneGen(gain, freq, ramp, ramp, TONE_SIZE, 48000.0);
355 //------------------------------------------------------------
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
367 //------------------------------------------------------------
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,
377 JACK_DEFAULT_AUDIO_TYPE,
380 rport = jack_port_register(client,
382 JACK_DEFAULT_AUDIO_TYPE,
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));
390 //------------------------------------------------------------
393 if ((fdser = open(serialdev, O_WRONLY)) == -1) {
394 fprintf(stderr, "cannot open serial device %s", serialdev);
397 if (ioctl(fdser, TIOCMGET, &serstatus) == -1) {
399 fprintf(stderr, "cannot get serial device status");
402 serstatus |= TIOCM_DTR;
403 if (ioctl(fdser, TIOCMSET, &serstatus) == -1) {
405 fprintf(stderr, "cannot set serial device status");
410 if ((fdrtc = open(clockdev, O_RDONLY)) == -1) {
414 if (ioctl(fdrtc, RTC_IRQP_SET, RTC_RATE) == -1) {
415 perror("ioctl irqp");
418 if (ioctl(fdrtc, RTC_PIE_ON, 0) == -1) {
419 perror("ioctl pie on");
423 //------------------------------------------------------------
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);
432 //------------------------------------------------------------
434 jack_activate(client);
437 if (!(ports = jack_get_ports(client, 0, 0, JackPortIsPhysical | JackPortIsInput))) {
438 fprintf(stderr, "can't find any physical playback ports\n");
441 if (jack_connect(client, jack_port_name(lport), ports[0])) {
442 fprintf(stderr, "can't connect left output\n");
445 if (jack_connect(client, jack_port_name(rport), ports[1])) {
446 fprintf(stderr, "can't connect left output\n");
451 sem_post(&keyer_started);
453 pthread_join(play, 0);
454 pthread_join(key, 0);
455 pthread_join(update, 0);
456 jack_client_close(client);
458 //------------------------------------------------------------
460 if (ioctl(fdrtc, RTC_PIE_OFF, 0) == -1) {
461 perror("ioctl pie off");
467 jack_ringbuffer_free(lring);
468 jack_ringbuffer_free(rring);
470 sem_destroy(&clock_fired);
471 sem_destroy(&keyer_started);
477 //------------------------------------------------------------