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
52 // # samples generated during 1 clock tick at RTC_RATE
53 #define TONE_SIZE (SAMP_RATE / RTC_RATE)
55 // ring buffer size; > 1 sec at this sr
56 #define RING_SIZE (01 << 020)
61 pthread_t play, key, update;
62 sem_t clock_fired, keyer_started, update_ok;
66 jack_client_t *client;
67 jack_port_t *lport, *rport;
68 jack_ringbuffer_t *lring, *rring;
72 BOOLEAN playing = FALSE, iambic = FALSE;
73 double wpm = 18.0, freq = 750.0, ramp = 5.0, gain = -3.0;
75 //------------------------------------------------------------
78 jack_ringbuffer_clear(jack_ringbuffer_t *ring, int nbytes) {
81 for (i = 0; i < nbytes; i++)
82 jack_ringbuffer_write(ring, &zero, 1);
86 jack_ringbuffer_restart(jack_ringbuffer_t *ring, int nbytes) {
87 jack_ringbuffer_reset(ring);
88 jack_ringbuffer_clear(ring, nbytes);
91 //------------------------------------------------------------
93 // generated tone -> output ringbuffer
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));
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));
111 // silence -> output ringbuffer
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));
120 for (i = 0; i < gen->size; i++) {
122 jack_ringbuffer_write(lring, (char *) &zero, sizeof(float));
123 jack_ringbuffer_write(rring, (char *) &zero, sizeof(float));
128 //------------------------------------------------------------------------
130 // sound/silence generation
131 // tone turned on/off asynchronously
136 sem_wait(&clock_fired);
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);
146 // only let updates run when we've just generated silence
147 sem_post(&update_ok);
154 //------------------------------------------------------------------------
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.
164 double del, std = 1000 / (double) RTC_RATE;
168 if (read(fdrtc, &data, sizeof(unsigned long)) == -1) {
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);
179 // key down? (real or via keyer logic)
182 read_key(double del) {
184 return read_iambic_key_serial(ks, fdser, kl, del);
186 return read_straight_key_serial(ks, fdser);
189 //------------------------------------------------------------------------
196 sem_wait(&keyer_started);
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);
204 if (!playing && keydown)
205 CWToneOn(gen), playing = TRUE;
206 else if (playing && !keydown)
209 sem_post(&clock_fired);
215 //------------------------------------------------------------------------
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
223 #define MAX_ESC (512)
232 // get or wait for next input char
233 if ((c = getchar()) == EOF) goto finish;
235 // if we see the beginning of a command,
240 // gather up the remainder
241 while ((c = getchar()) != EOF) {
242 if (c == ESC_R) break;
244 if (++i >= (MAX_ESC - 1)) break;
246 if (c == EOF) goto finish;
249 // wait until changes are safe
250 sem_wait(&update_ok);
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))
266 } // otherwise go around again
269 // we saw an EOF or quit; kill other threads and exit neatly
272 pthread_cancel(play);
277 //------------------------------------------------------------------------
280 jack_xrun(void *arg) {
282 write(2, str, strlen(str));
286 jack_shutdown(void *arg) {}
289 jack_callback(jack_nframes_t nframes, void *arg) {
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);
312 main(int argc, char **argv) {
314 char *serialdev = "/dev/ttyS0",
315 *clockdev = "/dev/rtc";
318 for (i = 1; i < argc; i++)
319 if (argv[i][0] == '-')
320 switch (argv[i][1]) {
322 freq = atof(argv[++i]);
328 gain = atof(argv[++i]);
331 ramp = atof(argv[++i]);
334 wpm = atof(argv[++i]);
338 "keyd [-i] [-w wpm] [-g gain_dB] [-r ramp_ms]\n");
344 if (!freopen(argv[i], "r", stdin))
345 perror(argv[i]), exit(1);
349 //------------------------------------------------------------
351 gen = newCWToneGen(gain, freq, ramp, ramp, TONE_SIZE, 48000.0);
353 //------------------------------------------------------------
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
365 //------------------------------------------------------------
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,
375 JACK_DEFAULT_AUDIO_TYPE,
378 rport = jack_port_register(client,
380 JACK_DEFAULT_AUDIO_TYPE,
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));
388 //------------------------------------------------------------
391 if ((fdser = open(serialdev, O_WRONLY)) == -1) {
392 fprintf(stderr, "cannot open serial device %s", serialdev);
395 if (ioctl(fdser, TIOCMGET, &serstatus) == -1) {
397 fprintf(stderr, "cannot get serial device status");
400 serstatus |= TIOCM_DTR;
401 if (ioctl(fdser, TIOCMSET, &serstatus) == -1) {
403 fprintf(stderr, "cannot set serial device status");
408 if ((fdrtc = open(clockdev, O_RDONLY)) == -1) {
412 if (ioctl(fdrtc, RTC_IRQP_SET, RTC_RATE) == -1) {
413 perror("ioctl irqp");
416 if (ioctl(fdrtc, RTC_PIE_ON, 0) == -1) {
417 perror("ioctl pie on");
421 //------------------------------------------------------------
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);
430 //------------------------------------------------------------
432 jack_activate(client);
435 if (!(ports = jack_get_ports(client, 0, 0, JackPortIsPhysical | JackPortIsInput))) {
436 fprintf(stderr, "can't find any physical playback ports\n");
439 if (jack_connect(client, jack_port_name(lport), ports[0])) {
440 fprintf(stderr, "can't connect left output\n");
443 if (jack_connect(client, jack_port_name(rport), ports[1])) {
444 fprintf(stderr, "can't connect left output\n");
449 sem_post(&keyer_started);
451 pthread_join(play, 0);
452 pthread_join(key, 0);
453 pthread_join(update, 0);
454 jack_client_close(client);
456 //------------------------------------------------------------
458 if (ioctl(fdrtc, RTC_PIE_OFF, 0) == -1) {
459 perror("ioctl pie off");
465 jack_ringbuffer_free(lring);
466 jack_ringbuffer_free(rring);
468 sem_destroy(&clock_fired);
469 sem_destroy(&keyer_started);
475 //------------------------------------------------------------