From: dttsp Date: Tue, 26 Apr 2005 15:49:24 +0000 (+0000) Subject: Bug fixes to jsdr, keyer X-Git-Url: https://git.rkrishnan.org/pf/content/en/seg/about//%22%22?a=commitdiff_plain;h=47feea048caddbf954f5fa6cf8b28f1fe659b1c7;p=dttsp.git Bug fixes to jsdr, keyer --- diff --git a/jDttSP/Makefile b/jDttSP/Makefile index 4c39051..b13744d 100644 --- a/jDttSP/Makefile +++ b/jDttSP/Makefile @@ -1,7 +1,7 @@ CFLAGS = -g -O3 -I. -I/usr/local/include #CFLAGS = -g -I. -I/usr/local/include -LIBS = -L/usr/local/lib -ljack -lpthread -lfftw -lm -#LIBS = -lefence -L/usr/local/lib -ljack -lpthread -lfftw -lm +LIBS = -L/usr/local/lib -ljack -lpthread -lgsl -lgslcblas -lfftw -lm +#LIBS = -lefence -L/usr/local/lib -ljack -lpthread -lgsl -lgslcblas -lfftw -lm staticlibname=libDttSP.a @@ -31,9 +31,20 @@ OBJ = am_demod.o\ window.o\ update.o +KOBJ = oscillator.o cwtones.o chan.o ringb.o banal.o bufvec.o splitfields.o cxops.o + jsdr: main.o $(OBJ) $(CC) -o jsdr main.o $(OBJ) $(LIBS) +all: jsdr mkchan ipc metermon keyd keyb + +keyd: keyd.o keyer.o $(KOBJ) + $(CC) -o keyd keyd.o keyer.o $(KOBJ) $(LIBS) + +keyb: keyb.o keyer.o $(KOBJ) + $(CC) -o keyb keyb.o keyer.o $(KOBJ) $(LIBS) + + $(OBJ): sdrexport.h metermon: metermon.o chan.o ringb.o bufvec.o cxops.o banal.o @@ -42,13 +53,14 @@ metermon: metermon.o chan.o ringb.o bufvec.o cxops.o banal.o mkchan: mkchan.o bufvec.o banal.o cxops.o $(CC) -o mkchan mkchan.o bufvec.o banal.o cxops.o $(LIBS) -ipc: +ipc: mkchan ./setup-ipc obj: $(OBJ) clean: - /bin/rm *.o jsdr mkchan metermon $(staticlibname) + /bin/rm *.o jsdr mkchan metermon keyd keyb #$(staticlibname) + #/bin/rm IPC/* staticlib: $(OBJ) ar rcs $(staticlibname) $(OBJ) @@ -60,3 +72,4 @@ staticlib: $(OBJ) # sharedlib=$(sharedlibname).$(sharedlibvers) # sharedlib: $(OBJ) # gcc -shared -Wl,-soname,$(sharedlib) -o $(sharedlib) $(OBJ) -lc + diff --git a/jDttSP/banal.c b/jDttSP/banal.c index a5e8447..8bc08f1 100644 --- a/jDttSP/banal.c +++ b/jDttSP/banal.c @@ -195,3 +195,5 @@ find_rcfile(char *base) { return 0; } +//------------------------------------------------------------------------ + diff --git a/jDttSP/banal.h b/jDttSP/banal.h index 1ca3ffe..3aca035 100644 --- a/jDttSP/banal.h +++ b/jDttSP/banal.h @@ -54,7 +54,7 @@ #define FALSE 0 extern void nilfunc(void); -extern double sqr(double); +extern INLINE double sqr(double); extern int popcnt(int); extern int npoof2(int); extern int nblock2(int); diff --git a/jDttSP/command-vocabulary b/jDttSP/command-vocabulary index f927b5d..3f479be 100644 --- a/jDttSP/command-vocabulary +++ b/jDttSP/command-vocabulary @@ -1,54 +1,53 @@ [TRX] indicates optional arg (RX or TX), RX default T|F indicates TRUE or FALSE -(see enums.m4) -setANF T|F // on/off, RX only -setANFvals taps delay gain leak // int, int, float, float, RX only -setATTOffset val // float, RX only, appears only in squelch calc -setBIN T|F // binaural mode, on/off, RX only setFilter low-freq high-freq TRX -setFinished // shutdown gracefully -setGainOffset // float, RX only, appears only in squelch calc -setMeterOffset lev // float, RX only, appears only in squelch calc setMode mode [TRX] // mode = USB, LSB, CWL, CWU, etc. +setOsc freq [TRX] // freq in Hz (float) +setSampleRate rate // Hz (float) +setNR T|F // on/off, RX only +setANF T|F // on/off, RX only setNB T|F // on/off, RX only +setBIN T|F // binaural mode, on/off, RX only setNBvals thresh // float, RX only -setNR T|F // on/off, RX only -setNRvals taps delay gain leak // int, int, float, float; RX only -setOsc freq [TRX] // freq in Hz (float) +setfixedAGC gain [TRX] // float setRXAGC T|F // on/off -setRXAGC mode // mode = agcOFF, agcSLOW, etc. setRXAGCCompression lev // float setRXAGCHang dur // float setRXAGCLimit lim // float -setRXEQ // f0 dB0 f1 dB1 f2 dB2 ... fN (see setTXEQ) -setRXPostScl T|F // on/off -setRXPostSclVal valQ // dB -setRXPreScl T|F // on/off -setRXPreSclVal valQ // dB -setRunState state // RUN_MUTE, RUN_PASS, RUN_PLAY -setSWCH trx [zap] // trx = RX|TX, int (always zaps at least 1) -setSampleRate rate // Hz (float) -setSpotTone T|F // turn on, off -setSpotToneVals gain freq rise fall // dB, Hz, msec, msec [-12, 700, 5, 5] -setSquelch lev // float, gain, RX only; default -30dB -setSquelchSt T|F // on/off, RX only -setTRX trx // trx = RX|TX setTXAGC T|F // on/off setTXAGCCompression lev // float setTXAGCHang dur // float setTXAGCLimit lim // float +setTXSpeechCompression T|F // on/off +setTXSpeechCompressionGain gain // float +setRXEQ // f0 dB0 f1 dB1 f2 dB2 ... fN setTXEQ // f0 dB0 f1 dB1 f2 dB2 ... fN // typical: // 0 dB1 75 dB2 150 dB3 300 dB4 600 dB5 1200 dB6 2000 dB7 2800 dB8 3600 // approximates W2IHY bandcenters -setTXPostScl T|F // on/off -setTXPostSclVal valQ // dB -setTXPreScl T|F // on/off -setTXPreSclVal valQ // dB -setTXSpeechCompression T|F // on/off -setTXSpeechCompressionGain gain // float +setRXAGC mode // mode = agcOFF, agcSLOW, etc. +setANFvals taps delay gain leak // int, int, float, float, RX only +setNRvals taps delay gain leak // int, int, float, float, RX only setcorrectIQ phase gain // int, int -setcorrectIQgain gain // int setcorrectIQphase phase // int -setfixedAGC gain [TRX] // float +setcorrectIQgain gain // int +setSquelch lev // float, gain, RX only; default -30dB +setMeterOffset lev // float, RX only, appears only in squelch calc +setATTOffset val // float, RX only, appears only in squelch calc +setGainOffset // float, RX only, appears only in squelch calc +setSquelchSt T|F // on/off, RX only +setTRX trx // trx = RX|TX +setRunState state // RUN_MUTE, RUN_PASS, RUN_PLAY +setRXPreScl T|F // on/off +setRXPreSclVal valQ // dB +setTXPreScl T|F // on/off +setTXPreSclVal valQ // dB +setRXPostScl T|F // on/off +setRXPostSclVal valQ // dB +setTXPostScl T|F // on/off +setTXPostSclVal valQ // dB +setSWCH trx [zap] // trx = RX|TX, int (always zaps at least 1) +setSpotToneVals gain freq rise fall // dB, Hz, msec, msec [-12, 700, 5, 5] +setSpotTone T|F // turn on, off +setFinished // shutdown gracefully \ No newline at end of file diff --git a/jDttSP/cwtones.c b/jDttSP/cwtones.c new file mode 100644 index 0000000..012694e --- /dev/null +++ b/jDttSP/cwtones.c @@ -0,0 +1,183 @@ +/* cwtones.c */ +/* +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2005 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +//------------------------------------------------------------------------ +// An ASR envelope on a complex phasor, +// with asynchronous trigger for R stage. +// A/R use sine shaping. +//------------------------------------------------------------------------ + +BOOLEAN +CWTone(CWToneGen cwt) { + int i, n = cwt->size; + + ComplexOSC(cwt->osc.gen); + + for (i = 0; i < n; i++) { + + // in an envelope stage? + + if (cwt->stage == CWTone_RISE) { + + // still going? + if (cwt->rise.have++ < cwt->rise.want) { + cwt->curr += cwt->rise.incr; + cwt->mul = cwt->scl * sin(cwt->curr * M_PI / 2.0); + } else { + // no, assert steady-state, force level + cwt->curr = 1.0; + cwt->mul = cwt->scl; + cwt->stage = CWTone_STDY; + // won't come back into envelopes + // until FALL asserted from outside + } + + } else if (cwt->stage == CWTone_FALL) { + + // still going? + if (cwt->fall.have++ < cwt->fall.want) { + cwt->curr -= cwt->fall.incr; + cwt->mul = cwt->scl * sin(cwt->curr * M_PI / 2.0); + } else { + // no, assert trailing, force level + cwt->curr = 0.0; + cwt->mul = 0.0; + cwt->stage = CWTone_HOLD; + // won't come back into envelopes hereafter + } + } + + // apply envelope + // (same base as osc.gen internal buf) + CXBdata(cwt->buf, i) = Cscl(CXBdata(cwt->buf, i), cwt->mul); + } + + // indicate whether it's turned itself off + // sometime during this pass + + return cwt->stage != CWTone_HOLD; +} + +//------------------------------------------------------------------------ +// turn tone on with current settings + +void +CWToneOn(CWToneGen cwt) { + + // gain is in dB + + cwt->scl = pow(10.0, cwt->gain / 20.0); + cwt->curr = cwt->mul = 0.0; + + // A/R times are in msec + + cwt->rise.want = (int) (0.5 + cwt->sr * (cwt->rise.dur / 1e3)); + cwt->rise.have = 0; + if (cwt->rise.want <= 1) + cwt->rise.incr = 1.0; + else + cwt->rise.incr = 1.0 / (cwt->rise.want - 1); + + cwt->fall.want = (int) (0.5 + cwt->sr * (cwt->fall.dur / 1e3)); + cwt->fall.have = 0; + if (cwt->fall.want <= 1) + cwt->fall.incr = 1.0; + else + cwt->fall.incr = 1.0 / (cwt->fall.want - 1); + + // freq is in Hz + + OSCfreq(cwt->osc.gen) = 2.0 * M_PI * cwt->osc.freq / cwt->sr; + OSCphase(cwt->osc.gen) = 0.0; + + cwt->stage = CWTone_RISE; +} + +//------------------------------------------------------------------------ +// initiate turn-off + +void +CWToneOff(CWToneGen cwt) { cwt->stage = CWTone_FALL; } + +//------------------------------------------------------------------------ + +void +setCWToneGenVals(CWToneGen cwt, + REAL gain, + REAL freq, + REAL rise, + REAL fall) { + cwt->gain = gain; + cwt->osc.freq = freq; + cwt->rise.dur = rise; + cwt->fall.dur = fall; +} + +CWToneGen +newCWToneGen(REAL gain, // dB + REAL freq, + REAL rise, // ms + REAL fall, // ms + int size, + REAL samplerate) { + + CWToneGen cwt = (CWToneGen) safealloc(1, sizeof(CWToneGenDesc), + "CWToneGenDesc"); + + setCWToneGenVals(cwt, gain, freq, rise, fall); + cwt->size = size; + cwt->sr = samplerate; + + cwt->osc.gen = newOSC(cwt->size, + ComplexTone, + cwt->osc.freq, + 0.0, + cwt->sr, + "CWTone osc"); + + // overload oscillator buf + cwt->buf = newCXB(cwt->size, OSCCbase(cwt->osc.gen), "CWToneGen buf"); + + return cwt; +} + +void +delCWToneGen(CWToneGen cwt) { + if (cwt) { + delCXB(cwt->buf); + delOSC(cwt->osc.gen); + safefree((char *) cwt); + } +} diff --git a/jDttSP/cwtones.h b/jDttSP/cwtones.h new file mode 100644 index 0000000..fe3bafd --- /dev/null +++ b/jDttSP/cwtones.h @@ -0,0 +1,81 @@ +/* spottone.h */ +/* +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2005 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _spottone_h +#define _spottone_h + +#include +#include +#include +#include +#include +#include + +#define CWTone_IDLE (0) +#define CWTone_WAIT (1) +#define CWTone_RISE (2) +#define CWTone_STDY (3) +#define CWTone_FALL (4) +#define CWTone_HOLD (5) + +typedef struct _spot_tone_gen { + REAL curr, gain, mul, scl, sr; + struct { + REAL freq; + OSC gen; + } osc; + struct { + REAL dur, incr; + int want, have; + } rise, fall; + int size, stage; + CXB buf; +} CWToneGenDesc, *CWToneGen; + +extern CWToneGen newCWToneGen(REAL gain, // dB + REAL freq, // Hz + REAL rise, // msec + REAL fall, // msec + int size, // buflen + REAL samplerate); +extern void delCWToneGen(CWToneGen gen); +extern void setCWToneGenVals(CWToneGen gen, + REAL gain, + REAL freq, + REAL rise, + REAL fall); +extern void CWToneOn(CWToneGen gen); +extern void CWToneOff(CWToneGen gen); +extern BOOLEAN CWTone(CWToneGen gen); + +#endif diff --git a/jDttSP/cxops.c b/jDttSP/cxops.c index b54dbef..4927de7 100644 --- a/jDttSP/cxops.c +++ b/jDttSP/cxops.c @@ -107,3 +107,13 @@ Cexp(COMPLEX z) { REAL r = exp(z.re); return Cmplx(r * cos(z.im), r * sin(z.im)); } + +COMPLEX +Cp2r(COMPLEX z) { + return Cmplx(z.re * cos(z.im), z.re * sin(z.im)); +} + +COMPLEX +Cr2p(COMPLEX z) { + return Cmplx(sqrt(sqr(z.re) + sqr(z.im)), ATAN2(z.im, z.re)); +} diff --git a/jDttSP/cxops.h b/jDttSP/cxops.h index 83d2e26..00ebae8 100644 --- a/jDttSP/cxops.h +++ b/jDttSP/cxops.h @@ -35,6 +35,7 @@ Bridgewater, NJ 08807 #define _cxops_h #include +#include extern COMPLEX cxzero; extern COMPLEX cxone; @@ -54,5 +55,8 @@ extern INLINE COMPLEX Cmplx(REAL, IMAG); extern INLINE COMPLEX Conjg(COMPLEX); extern INLINE COMPLEX Cexp(COMPLEX); +extern INLINE COMPLEX Cp2r(COMPLEX); +extern INLINE COMPLEX Cr2p(COMPLEX); + #endif diff --git a/jDttSP/digitalagc.c b/jDttSP/digitalagc.c index 5d559a1..352ebb2 100644 --- a/jDttSP/digitalagc.c +++ b/jDttSP/digitalagc.c @@ -106,7 +106,7 @@ DigitalAgc(DIGITALAGC a, int tick) { a->size = hang; a->hist[k] = a->gain.lim / peak; for (i = 1, a->gain.now = a->hist[0]; i < hang; i++) - a->gain.now = min(a->hist[i], a->gain.now); + a->gain.now = max(a->hist[i], a->gain.now); } a->gain.now = min(a->gain.now, a->gain.top); diff --git a/jDttSP/fastrig.c b/jDttSP/fastrig.c index 5c4d8ff..52c77b1 100644 --- a/jDttSP/fastrig.c +++ b/jDttSP/fastrig.c @@ -203,10 +203,14 @@ fast_atan2(REAL y, REAL x) { /* normalize to +/- 45 degree range */ y_abs = fabs(y); x_abs = fabs(x); - z = (y_abs < x_abs ? y_abs / x_abs : x_abs / y_abs); + //z = (y_abs < x_abs ? y_abs / x_abs : x_abs / y_abs); + if (y_abs < x_abs) + z = y_abs / x_abs; + else + z = x_abs / y_abs; /* when ratio approaches the table resolution, the angle is */ /* best approximated with the argument itself... */ - if (z < TAN_MAP_RES) base_angle = z; + if (z < TAN_MAP_RES) base_angle = z; else { /* find index and interpolation value */ alpha = z * (REAL) TAN_MAP_SIZE - .5; diff --git a/jDttSP/fastrig.h b/jDttSP/fastrig.h index ae8485b..b1162f4 100644 --- a/jDttSP/fastrig.h +++ b/jDttSP/fastrig.h @@ -38,7 +38,10 @@ Bridgewater, NJ 08807 #include #include #include + +#ifdef notdef #include +#endif #define SIN_TABLE_SIZE 4096 #define SIN_TABLE_SIZE_M1 4095 diff --git a/jDttSP/fromsys.h b/jDttSP/fromsys.h index 56e6a8f..12b7498 100644 --- a/jDttSP/fromsys.h +++ b/jDttSP/fromsys.h @@ -62,4 +62,11 @@ Bridgewater, NJ 08807 #include #include +#include +#include +#include +#include +#include +#include + #endif diff --git a/jDttSP/keyb.c b/jDttSP/keyb.c new file mode 100644 index 0000000..591fbb6 --- /dev/null +++ b/jDttSP/keyb.c @@ -0,0 +1,509 @@ +/* keyb.c */ +/* +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include +#include +#include +#include +#include +#include + +#define SAMP_RATE (48000) +#define HUGE_PHASE 1256637061.43593 + +#define RING_SIZE (01 << 020) + +pthread_t input, play; +sem_t ready, reader, writer; + +jack_client_t *client; +jack_port_t *lport, *rport; +jack_ringbuffer_t *lring, *rring; +jack_nframes_t size; + +BOOLEAN playing = FALSE; +double wpm = 18.0, freq = 750.0, gain = -6.0; + +COMPLEX *zout = 0; + +// basic mapping, chars -> morse strings +char *morse_table[128]; + +// CW tone segments +#define ME_EOF (-1) +#define ME_ZERO (0) +#define ME_RAMP (1) +#define ME_STDY (2) + +struct { + double wpm, rise, fall, curr, incr, rate; + int type, size; +} morsel; + +int ditspacesize, dahspacesize, + ditstdysize, dahstdysize, + charspacesize, wordspacesize, + risesize, fallsize; +double riseincr, fallincr; + +void jack_ringbuffer_clear(jack_ringbuffer_t *, int); +void jack_ringbuffer_restart(jack_ringbuffer_t *, int); +void send_sound(COMPLEX *, int); + +//------------------------------------------------------------ + +// map char -> morse string +char * +get_morse(int c) { return morse_table[c & 0x7F]; } + +void +reader_thread(void) { + BOOLEAN b = TRUE; // we're coming from silence + int c, e; + char *m; + + // keep reading 1 char at a time + while ((c = getchar()) != EOF) { + + // is char mapped to morse? + if (m = get_morse(c)) { + + // yup + // for each sub-element (dit/dah) + while (e = *m++) { + // first segment is slew in... + sem_wait(&reader); + morsel.type = ME_RAMP, morsel.size = risesize; + morsel.curr = 0.0, morsel.incr = riseincr; + sem_post(&writer); + + // ...then steady state... + sem_wait(&reader); + morsel.type = ME_STDY; + morsel.size = e == '.' ? ditstdysize : dahstdysize; + sem_post(&writer); + + // ...then slew out... + sem_wait(&reader); + morsel.type = ME_RAMP, morsel.size = fallsize; + morsel.curr = 1.0, morsel.incr = fallincr; + sem_post(&writer); + + // ...finally, post-element pause + sem_wait(&reader); + morsel.type = ME_ZERO; + morsel.size = ditspacesize; + sem_post(&writer); + } + + // post-character pause + sem_wait(&reader); + morsel.type = ME_ZERO; + // (we already emitted a dit-sized space) + morsel.size = charspacesize - ditspacesize; + sem_post(&writer); + + // wherever we go next, it won't have been from silence + b = FALSE; + + } else { + // anything else treated as interword space + sem_wait(&reader); + morsel.type = ME_ZERO; + // was previous output also interword space? + if (b) + // yes, use full duration + morsel.size = wordspacesize; + else + // no, part of duration already played + morsel.size = wordspacesize - charspacesize; + b = TRUE; + sem_post(&writer); + } + } + + // indicate EOF on input + sem_wait(&reader); + morsel.type = ME_EOF; + sem_post(&writer); + pthread_exit(0); +} + +void +sound_thread(void) { + int i, k = 0; + double ofreq = freq * 2.0 * M_PI / SAMP_RATE, + phase = 0.0, + scale = pow(10.0, gain / 20.0); + COMPLEX z, delta_z; + + // as long as there's been no EOF on the input... + for (;;) { + + // pause for next sub-element + sem_post(&reader); + sem_wait(&writer); + + // no more data? + if (morsel.type == ME_EOF) break; + + // interword space == silence? + if (morsel.type != ME_ZERO) { + // no, set up CORDIC tone generation + if (phase > HUGE_PHASE) phase -= HUGE_PHASE; + z = Cmplx(cos(phase), sin(phase)); + delta_z = Cmplx(cos(ofreq), sin(ofreq)); + } + + // play out this sub-segment + for (i = 0; i < morsel.size; i++) { + + // make silence + if (morsel.type == ME_ZERO) zout[k] = cxzero; + + // make tone + else { + z = Cmul(z, delta_z); + phase += ofreq; + // slewing segment? + if (morsel.type == ME_RAMP) { + morsel.curr += morsel.incr; + zout[k] = Cscl(z, scale * sin(morsel.curr * M_PI / 2.0)); + } else + zout[k] = Cscl(z, scale); + } + + // one jack bufferful yet? + if (++k >= size) { + // yes, send to output + send_sound(zout, k); + // wait until it's been taken away + sem_wait(&ready); + k = 0; + // reset CORDIC + if (morsel.type != ME_ZERO) { + if (phase > HUGE_PHASE) phase -= HUGE_PHASE; + z = Cmplx(cos(phase), sin(phase)); + delta_z = Cmplx(cos(ofreq), sin(ofreq)); + } + } + } + } + + // anything left unsent in buffer? + if (k > 0) send_sound(zout, k); + + pthread_exit(0); +} + +//------------------------------------------------------------------------ + +void +jack_ringbuffer_clear(jack_ringbuffer_t *ring, int nbytes) { + int i; + char zero = 0; + for (i = 0; i < nbytes; i++) + jack_ringbuffer_write(ring, &zero, 1); +} + +void +jack_ringbuffer_restart(jack_ringbuffer_t *ring, int nbytes) { + jack_ringbuffer_reset(ring); + jack_ringbuffer_clear(ring, nbytes); +} + +void +send_sound(COMPLEX *buff, int len) { + if (jack_ringbuffer_write_space(lring) < len * sizeof(float)) { + write(2, "overrun\n", 8); + jack_ringbuffer_restart(lring, size * sizeof(float)); + jack_ringbuffer_restart(rring, size * sizeof(float)); + } else { + int i; + for (i = 0; i < len; i++) { + float l = buff[i].re, r = buff[i].im; + jack_ringbuffer_write(lring, (char *) &l, sizeof(float)); + jack_ringbuffer_write(rring, (char *) &r, sizeof(float)); + } + } +} + +PRIVATE void +jack_xrun(void *arg) { + char *str = "xrun!\n"; + write(2, str, strlen(str)); +} + +PRIVATE void +jack_shutdown(void *arg) {} + +PRIVATE void +jack_callback(jack_nframes_t nframes, void *arg) { + char *lp, *rp; + int nwant = nframes * sizeof(float), + nhave = jack_ringbuffer_read_space(lring); + + lp = jack_port_get_buffer(lport, nframes); + rp = jack_port_get_buffer(rport, nframes); + if (nhave >= nwant) { + jack_ringbuffer_read(lring, lp, nwant); + jack_ringbuffer_read(rring, rp, nwant); + sem_post(&ready); + } else { + memset(lp, 0, nwant); + memset(rp, 0, nwant); + } +} + +int +main(int argc, char **argv) { + int i; + + for (i = 1; i < argc; i++) + if (argv[i][0] == '-') + switch (argv[i][1]) { + case 'f': + freq = atof(argv[++i]); + break; + case 'w': + wpm = atof(argv[++i]); + break; + default: + fprintf(stderr, "keyd [-w wpm] [-f freq] [infile]\n"); + exit(1); + } + else break; + + if (i < argc) { + if (!freopen(argv[i], "r", stdin)) + perror(argv[i]), exit(1); + i++; + } + + //------------------------------------------------------------ + + morsel.wpm = wpm; + morsel.rise = morsel.fall = 5.0; // ms + morsel.rate = SAMP_RATE; + + ditspacesize = SAMP_RATE * 1.2 / morsel.wpm + 0.5; + dahspacesize = 3 * ditspacesize; + charspacesize = dahspacesize; + wordspacesize = 7 * ditspacesize; + + risesize = SAMP_RATE * morsel.rise / 1e3 + 0.5; + if (risesize > 1) + riseincr = 1.0 / (risesize - 1); + else + riseincr = 1.0; + + fallsize = SAMP_RATE * morsel.fall / 1e3 + 0.5; + if (fallsize > 1) + fallincr = -1.0 / (fallsize - 1); + else + fallincr = -1.0; + + ditstdysize = ditspacesize - risesize - fallsize; + dahstdysize = dahspacesize - risesize - fallsize; + + //------------------------------------------------------------ + + if (!(client = jack_client_new("keyb"))) + fprintf(stderr, "can't make client -- jack not running?\n"), exit(1); + jack_set_process_callback(client, (void *) jack_callback, 0); + jack_on_shutdown(client, (void *) jack_shutdown, 0); + jack_set_xrun_callback(client, (void *) jack_xrun, 0); + size = jack_get_buffer_size(client); + + lport = jack_port_register(client, + "ol", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); + rport = jack_port_register(client, + "or", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); + lring = jack_ringbuffer_create(RING_SIZE); + rring = jack_ringbuffer_create(RING_SIZE); + jack_ringbuffer_clear(lring, size * sizeof(float)); + jack_ringbuffer_clear(rring, size * sizeof(float)); + + //------------------------------------------------------------ + + zout = newvec_COMPLEX(size, "keyb sample buffer"); + + //------------------------------------------------------------ + + sem_init(&ready, 0, 0); + sem_init(&reader, 0, 0); + sem_init(&writer, 0, 0); + pthread_create(&input, 0, (void *) reader_thread, 0); + pthread_create(&play, 0, (void *) sound_thread, 0); + + //------------------------------------------------------------ + + jack_activate(client); + { + const char **ports; + if (!(ports = jack_get_ports(client, 0, 0, JackPortIsPhysical | JackPortIsInput))) { + fprintf(stderr, "can't find any physical playback ports\n"); + exit(1); + } + if (jack_connect(client, jack_port_name(lport), ports[0])) { + fprintf(stderr, "can't connect left output\n"); + exit(1); + } + if (jack_connect(client, jack_port_name(rport), ports[1])) { + fprintf(stderr, "can't connect left output\n"); + exit(1); + } + free(ports); + } + + pthread_join(input, 0); + pthread_join(play, 0); + jack_client_close(client); + + //------------------------------------------------------------ + + delvec_COMPLEX(zout); + + //------------------------------------------------------------ + + jack_ringbuffer_free(lring); + jack_ringbuffer_free(rring); + sem_destroy(&ready); + sem_destroy(&reader); + sem_destroy(&writer); + + //------------------------------------------------------------ + + exit(0); +} + +char *morse_table[128] = { + /* 000 NUL */ 0, /* 001 SOH */ 0, /* 002 STX */ 0, /* 003 ETX */ 0, + /* 004 EOT */ 0, /* 005 ENQ */ 0, /* 006 ACK */ 0, /* 007 BEL */ 0, + /* 008 BS */ 0, /* 009 HT */ 0, /* 010 LF */ 0, /* 011 VT */ 0, + /* 012 FF */ 0, /* 013 CR */ 0, /* 014 SO */ 0, /* 015 SI */ 0, + /* 016 DLE */ 0, /* 017 DC1 */ 0, /* 018 DC2 */ 0, /* 019 DC3 */ 0, + /* 020 DC4 */ 0, /* 021 NAK */ 0, /* 022 SYN */ 0, /* 023 ETB */ 0, + /* 024 CAN */ 0, /* 025 EM */ 0, /* 026 SUB */ 0, /* 027 ESC */ 0, + /* 028 FS */ 0, /* 029 GS */ 0, /* 030 RS */ 0, /* 031 US */ 0, + /* 032 SP */ 0, + /* 033 ! */ "...-.", // [SN] + /* 034 " */ 0, /* 035 # */ 0, /* 036 $ */ 0, + /* 037 % */ ".-...", // [AS] + /* 038 & */ 0, /* 039 ' */ 0, + /* 040 ( */ "-.--.", // [KN] + /* 041 ) */ 0, + /* 042 * */ "...-.-", // [SK] + /* 043 + */ ".-.-.", // [AR] + /* 044 , */ "--..--", + /* 045 - */ "-....-", + /* 046 . */ ".-.-.-", + /* 047 / */ "-..-.", + /* 048 0 */ "-----", + /* 049 1 */ ".----", + /* 050 2 */ "..---", + /* 051 3 */ "...--", + /* 052 4 */ "....-", + /* 053 5 */ ".....", + /* 054 6 */ "-....", + /* 055 7 */ "--...", + /* 056 8 */ "---..", + /* 057 9 */ "----.", + /* 058 : */ 0, /* 059 ; */ 0, /* 060 < */ 0, + /* 061 = */ "-...-", // [BT] + /* 062 > */ 0, + /* 063 ? */ "..__..", // [IMI] + /* 064 @ */ ".--.-.", + /* 065 A */ ".-", + /* 066 B */ "-...", + /* 067 C */ "-.-.", + /* 068 D */ "-..", + /* 069 E */ ".", + /* 070 F */ "..-.", + /* 071 G */ "--.", + /* 072 H */ "....", + /* 073 I */ "..", + /* 074 J */ ".---", + /* 075 K */ "-.-", + /* 076 L */ ".-..", + /* 077 M */ "--", + /* 078 N */ "-.", + /* 079 O */ "---", + /* 080 P */ ".--.", + /* 081 Q */ "--.-", + /* 082 R */ ".-.", + /* 083 S */ "...", + /* 084 T */ "-", + /* 085 U */ "..-", + /* 086 V */ "...-", + /* 087 W */ ".--", + /* 088 X */ "-..-", + /* 089 Y */ "-.--", + /* 090 Z */ "--..", + /* 091 [ */ 0, /* 092 \ */ 0, /* 093 ] */ 0, /* 094 ^ */ 0, + /* 095 _ */ 0, /* 096 ` */ 0, + /* 097 a */ ".-", + /* 098 b */ "-...", + /* 099 c */ "-.-.", + /* 100 d */ "-..", + /* 101 e */ ".", + /* 102 f */ "..-.", + /* 103 g */ "--.", + /* 104 h */ "....", + /* 105 i */ "..", + /* 106 j */ ".---", + /* 107 k */ "-.-", + /* 108 l */ ".-..", + /* 109 m */ "--", + /* 110 n */ "-.", + /* 111 o */ "---", + /* 112 p */ ".--.", + /* 113 q */ "--.-", + /* 114 r */ ".-.", + /* 115 s */ "...", + /* 116 t */ "-", + /* 117 u */ "..-", + /* 118 v */ "...-", + /* 119 w */ ".--", + /* 120 x */ "-..-", + /* 121 y */ "-.--", + /* 122 z */ "--..", + /* 123 { */ 0, /* 124 | */ 0, /* 125 } */ 0, /* 126 ~ */ 0, + /* 127 DEL */ 0 +}; diff --git a/jDttSP/keyd.c b/jDttSP/keyd.c new file mode 100644 index 0000000..61d7ac8 --- /dev/null +++ b/jDttSP/keyd.c @@ -0,0 +1,388 @@ +/* keyd.c */ +/* +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SAMP_RATE (48000) + +// # times key is sampled per sec +// > 64 requires root on Linux +//#define RTC_RATE (128) +#define RTC_RATE (64) + +// # samples generated during 1 clock tick at RTC_RATE +#define TONE_SIZE (SAMP_RATE / RTC_RATE) + +// ring buffer size; > 1 sec at this sr +#define RING_SIZE (01 << 020) + +KeyerState ks; +KeyerLogic kl; + +pthread_t play, key; +sem_t clock_fired, keyer_started; + +int fdser, fdrtc; + +jack_client_t *client; +jack_port_t *lport, *rport; +jack_ringbuffer_t *lring, *rring; +jack_nframes_t size; + +CWToneGen gen; +BOOLEAN playing = FALSE, iambic = FALSE; +double wpm = 18.0, freq = 750.0; + +//------------------------------------------------------------ + +void +jack_ringbuffer_clear(jack_ringbuffer_t *ring, int nbytes) { + int i; + char zero = 0; + for (i = 0; i < nbytes; i++) + jack_ringbuffer_write(ring, &zero, 1); +} + +void +jack_ringbuffer_restart(jack_ringbuffer_t *ring, int nbytes) { + jack_ringbuffer_reset(ring); + jack_ringbuffer_clear(ring, nbytes); +} + +//------------------------------------------------------------ + +// generated tone -> output ringbuffer +void +send_tone(void) { + if (jack_ringbuffer_write_space(lring) < TONE_SIZE * sizeof(float)) { + write(2, "overrun tone\n", 13); + jack_ringbuffer_restart(lring, TONE_SIZE * sizeof(float)); + jack_ringbuffer_restart(rring, TONE_SIZE * sizeof(float)); + } else { + int i; + for (i = 0; i < gen->size; i++) { + float l = CXBreal(gen->buf, i), + r = CXBimag(gen->buf, i); + jack_ringbuffer_write(lring, (char *) &l, sizeof(float)); + jack_ringbuffer_write(rring, (char *) &r, sizeof(float)); + } + } +} + +// silence -> output ringbuffer +void +send_silence(void) { + if (jack_ringbuffer_write_space(lring) < TONE_SIZE * sizeof(float)) { + write(2, "overrun zero\n", 13); + jack_ringbuffer_restart(lring, TONE_SIZE * sizeof(float)); + jack_ringbuffer_restart(rring, TONE_SIZE * sizeof(float)); + } else { + int i; + for (i = 0; i < gen->size; i++) { + float zero = 0.0; + jack_ringbuffer_write(lring, (char *) &zero, sizeof(float)); + jack_ringbuffer_write(rring, (char *) &zero, sizeof(float)); + } + } +} + +// sound/silence generation +// tone turned on/off asynchronously +void +sound_thread(void) { + for (;;) { + sem_wait(&clock_fired); + + if (playing) { + // CWTone keeps playing for awhile after it's turned off, + // in order to allow for a decay envelope; + // returns FALSE when it's actually done. + playing = CWTone(gen); + send_tone(); + } else + send_silence(); + } + + pthread_exit(0); +} + +// basic heartbeat +// returns actual dur in msec since last tick; +// uses Linux rtc interrupts. +// other strategies will work too, so long as they +// provide a measurable delay in msec. +double +timed_delay(void) { + double del, std = 1000 / (double) RTC_RATE; + static int cnt = 0; + unsigned long data; + + if (read(fdrtc, &data, sizeof(unsigned long)) == -1) { + perror("read"); + exit(1); + } + // indicate whether an interrupt was missed + // not really important except for performance tweaks + if ((del = (data >> 010) * 1000 / (double) RTC_RATE) != std) + fprintf(stderr, "%d %g ms\n", ++cnt, del); + return del; +} + +// key down? (real or via keyer logic) +BOOLEAN +read_key(double del) { + if (iambic) + return read_iambic_key_serial(ks, fdser, kl, del); + else + return read_straight_key_serial(ks, fdser); +} + +// main keyer loop +// +void +key_thread(void) { + + sem_wait(&keyer_started); + + for (;;) { + // wait for next tick, get actual dur since last one + double del = timed_delay(); + // read key; tell keyer elapsed time since last call + BOOLEAN keydown = read_key(del); + + if (!playing && keydown) + CWToneOn(gen), playing = TRUE; + else if (playing && !keydown) + CWToneOff(gen); + + sem_post(&clock_fired); + } + + pthread_exit(0); +} + +PRIVATE void +jack_xrun(void *arg) { + char *str = "xrun"; + write(2, str, strlen(str)); +} + +PRIVATE void +jack_shutdown(void *arg) {} + +PRIVATE void +jack_callback(jack_nframes_t nframes, void *arg) { + float *lp, *rp; + int nbytes = nframes * sizeof(float); + if (nframes == size) { + // output: copy from ring to port + lp = (float *) jack_port_get_buffer(lport, nframes); + rp = (float *) jack_port_get_buffer(rport, nframes); + if (jack_ringbuffer_read_space(lring) >= nbytes) { + jack_ringbuffer_read(lring, (char *) lp, nbytes); + jack_ringbuffer_read(rring, (char *) rp, nbytes); + } else { // rb pathology + memset((char *) lp, 0, nbytes); + memset((char *) rp, 0, nbytes); + jack_ringbuffer_reset(lring); + jack_ringbuffer_reset(rring); + jack_ringbuffer_clear(lring, nbytes); + jack_ringbuffer_clear(rring, nbytes); + //write(2, "underrun\n", 9); + } + } +} + +int +main(int argc, char **argv) { + int i; + char *serialdev = "/dev/ttyS0", + *clockdev = "/dev/rtc"; + int serstatus; + + for (i = 1; i < argc; i++) + if (argv[i][0] == '-') + switch (argv[i][1]) { + case 'f': + freq = atof(argv[++i]); + break; + case 'i': + iambic = TRUE; + break; + case 'w': + wpm = atof(argv[++i]); + break; + default: + fprintf(stderr, "keyd [-i] [-w wpm]\n"); + exit(1); + } + else break; + + //------------------------------------------------------------ + + gen = newCWToneGen(-3.0, freq, 5.0, 5.0, TONE_SIZE, 48000.0); + + //------------------------------------------------------------ + + kl = newKeyerLogic(); + ks = newKeyerState(); + ks->flag.iambic = TRUE; + ks->flag.revpdl = TRUE; // depends on port wiring + ks->flag.autospace.khar = ks->flag.autospace.word = FALSE; + ks->debounce = 1; // could be more if sampled faster + ks->mode = MODE_B; + ks->weight = 50; + ks->wpm = wpm; + + //------------------------------------------------------------ + + if (!(client = jack_client_new("keyd"))) + fprintf(stderr, "can't make client -- jack not running?\n"), exit(1); + jack_set_process_callback(client, (void *) jack_callback, 0); + jack_on_shutdown(client, (void *) jack_shutdown, 0); + jack_set_xrun_callback(client, (void *) jack_xrun, 0); + size = jack_get_buffer_size(client); + lport = jack_port_register(client, + "ol", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); + rport = jack_port_register(client, + "or", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); + lring = jack_ringbuffer_create(RING_SIZE); + rring = jack_ringbuffer_create(RING_SIZE); + jack_ringbuffer_clear(lring, size * sizeof(float)); + jack_ringbuffer_clear(rring, size * sizeof(float)); + + //------------------------------------------------------------ + + // key + if ((fdser = open(serialdev, O_WRONLY)) == -1) { + fprintf(stderr, "cannot open serial device %s", serialdev); + exit(1); + } + if (ioctl(fdser, TIOCMGET, &serstatus) == -1) { + close(fdser); + fprintf(stderr, "cannot get serial device status"); + exit(1); + } + serstatus |= TIOCM_DTR; + if (ioctl(fdser, TIOCMSET, &serstatus) == -1) { + close(fdser); + fprintf(stderr, "cannot set serial device status"); + exit(1); + } + + // rtc + if ((fdrtc = open(clockdev, O_RDONLY)) == -1) { + perror(clockdev); + exit(1); + } + if (ioctl(fdrtc, RTC_IRQP_SET, RTC_RATE) == -1) { + perror("ioctl irqp"); + exit(1); + } + if (ioctl(fdrtc, RTC_PIE_ON, 0) == -1) { + perror("ioctl pie on"); + exit(1); + } + + //------------------------------------------------------------ + + sem_init(&clock_fired, 0, 0); + sem_init(&keyer_started, 0, 0); + pthread_create(&play, 0, (void *) sound_thread, 0); + pthread_create(&key, 0, (void *) key_thread, 0); + + //------------------------------------------------------------ + + jack_activate(client); + { + const char **ports; + if (!(ports = jack_get_ports(client, 0, 0, JackPortIsPhysical | JackPortIsInput))) { + fprintf(stderr, "can't find any physical playback ports\n"); + exit(1); + } + if (jack_connect(client, jack_port_name(lport), ports[0])) { + fprintf(stderr, "can't connect left output\n"); + exit(1); + } + if (jack_connect(client, jack_port_name(rport), ports[1])) { + fprintf(stderr, "can't connect left output\n"); + exit(1); + } + free(ports); + } + sem_post(&keyer_started); + + pthread_join(play, 0); + pthread_join(key, 0); + jack_client_close(client); + + //------------------------------------------------------------ + + if (ioctl(fdrtc, RTC_PIE_OFF, 0) == -1) { + perror("ioctl pie off"); + exit(1); + } + close(fdrtc); + close(fdser); + + jack_ringbuffer_free(lring); + jack_ringbuffer_free(rring); + + sem_destroy(&clock_fired); + sem_destroy(&keyer_started); + + delCWToneGen(gen); + delKeyerState(ks); + delKeyerLogic(kl); + + //------------------------------------------------------------ + + exit(0); +} diff --git a/jDttSP/keyer.c b/jDttSP/keyer.c new file mode 100644 index 0000000..581e51e --- /dev/null +++ b/jDttSP/keyer.c @@ -0,0 +1,335 @@ +/* keyer.c */ +/* +This file is part of a program that implements a Software-Defined Radio. + +The code in this file is derived from routines originally written by +Pierre-Philippe Coupard for his CWirc X-chat program. That program +is issued under the GPL and is +Copyright (C) Pierre-Philippe Coupard - 18/06/2003 + +This derived version is +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +// see below for places where serial port is read +// and needs replacement for parallel port hookup + +#include + +//======================================================================== +// nothing affected by physical port connection here + +BOOLEAN +klogic(KeyerLogic kl, + BOOLEAN dit, + BOOLEAN dah, + double wpm, + int iambicmode, + BOOLEAN midelementmodeB, + BOOLEAN ditmemory, + BOOLEAN dahmemory, + BOOLEAN autocharspacing, + BOOLEAN autowordspacing, + int weight, + double ticklen) { + + double ditlen = 1200 / wpm; + int set_element_timeouts = NO_TIMEOUTS_SCHED; + + /* Do we need to initialize the keyer? */ + if (!kl->flag.init) { + kl->flag.prev.dit = dit; + kl->flag.prev.dah = dah; + kl->element.last = kl->element.curr = NO_ELEMENT; + kl->element.iamb = NO_PADDLE_SQUEEZE; + kl->element.psqam = 0; + kl->element.invtd = 0; + kl->timeout.midl = kl->timeout.beep = kl->timeout.elem = 0; + kl->timeout.dlay = 0; + kl->dlay_type = NO_DELAY; + kl->flag.init = 1; + } + + /* Decrement the timeouts */ + kl->timeout.dlay -= kl->timeout.dlay > 0 ? ticklen : 0; + if (kl->timeout.dlay <= 0) { + /* If nothing is scheduled to play, and we just did a character + spacing delay, and we do auto word spacing, wait for a word + spacing delay, otherwise resume the normal element timeout + countdowns */ + if (kl->timeout.elem <= 0 && + kl->dlay_type == CHAR_SPACING_DELAY && + autowordspacing) { + kl->timeout.dlay = ditlen * 4; + kl->dlay_type = WORD_SPACING_DELAY; + } else { + kl->dlay_type = NO_DELAY; + kl->timeout.midl -= kl->timeout.midl > 0 ? ticklen : 0; + kl->timeout.beep -= kl->timeout.beep > 0 ? ticklen : 0; + kl->timeout.elem -= kl->timeout.elem > 0 ? ticklen : 0; + } + } + + /* Are both paddles squeezed? */ + if (dit && dah) { + kl->element.iamb = PADDLES_SQUEEZED; + + /* Are the paddles squeezed past the middle of the element? */ + if (kl->timeout.midl <= 0) + kl->element.psqam = 1; + } else + /* Are both paddles released and we had gotten a squeeze in this element? */ + if (!dit && !dah && kl->element.iamb == PADDLES_SQUEEZED) + kl->element.iamb = PADDLES_RELEASED; + + /* Is the current element finished? */ + if (kl->timeout.elem <= 0 && kl->element.curr != NO_ELEMENT) { + kl->element.last = kl->element.curr; + + /* Should we insert an inverted element? */ + if (((dit && dah) || + (kl->element.invtd && + kl->element.iamb != PADDLES_RELEASED) || + (kl->element.iamb == PADDLES_RELEASED && + iambicmode == MODE_B && + (!midelementmodeB || kl->element.psqam)))) { + if (kl->element.last == DAH) + set_element_timeouts = kl->element.curr = DIT; + else + set_element_timeouts = kl->element.curr = DAH; + } else { + /* No more element */ + kl->element.curr = NO_ELEMENT; + + /* Do we do automatic character spacing? */ + if (autocharspacing && !dit && !dah) { + kl->timeout.dlay = ditlen * 2; + kl->dlay_type = CHAR_SPACING_DELAY; + } + } + + kl->element.invtd = 0; + kl->element.iamb = NO_PADDLE_SQUEEZE; + kl->element.psqam = 0; + } + + /* Is an element currently being played? */ + if (kl->element.curr == NO_ELEMENT) { + if (dah) /* Dah paddle down ? */ + set_element_timeouts = kl->element.curr = DAH; + else if (dit) /* Dit paddle down ? */ + set_element_timeouts = kl->element.curr = DIT; + } + + /* Do the dah memory */ + if (kl->element.curr == DIT && + !kl->flag.prev.dah && + dah && + dahmemory) + kl->element.invtd = 1; + + /* Do the dit memory */ + if (kl->element.curr == DAH && + !kl->flag.prev.dit && + dit && + ditmemory) + kl->element.invtd = 1; + + /* If we had a dit (or dah) scheduled to be played after a delay, + and the operator lifted both paddles before the end of the delay, + and we have no dit (or dah) memory, forget it */ + + if (kl->timeout.dlay > 0 && + !dit && + !dah && + ((kl->element.curr == DIT && + !ditmemory) || + (kl->element.curr == DAH + && !dahmemory))) + set_element_timeouts = kl->element.curr = NO_ELEMENT; + + /* Do we need to set the playing timeouts of an element? */ + switch (set_element_timeouts) { + case NO_ELEMENT: /* Cancel any dit or dah */ + kl->timeout.beep = 0; + kl->timeout.midl = 0; + kl->timeout.elem = 0; + break; + + case DIT: /* Schedule a dit? */ + kl->timeout.beep = (ditlen * (double) weight) / 50; + kl->timeout.midl = kl->timeout.beep / 2; + kl->timeout.elem = ditlen * 2; + break; + + case DAH: /* Schedule a dah? */ + kl->timeout.beep = (ditlen * (double) weight) / 50 + ditlen * 2; + kl->timeout.midl = kl->timeout.beep / 2; + kl->timeout.elem = ditlen * 4; + break; + } + + kl->flag.prev.dit = dit; + kl->flag.prev.dah = dah; + + return kl->timeout.beep > 0 && kl->timeout.dlay <= 0; +} + +//======================================================================== + +/* Read a straight key connected to a serial port, do debouncing, then + return the key state */ + +BOOLEAN +read_straight_key_serial(KeyerState ks, int fd) { + int i, j, serstatus; + static BOOLEAN keystate = 0; + static int debounce_buf_i = 0, + debounce_buf[DEBOUNCE_BUF_MAX_SIZE]; + + //*************************************************** + // replace this with parallel port logic if necessary + //*************************************************** + // + /* Read the key state */ + if (ioctl(fd, TIOCMGET, &serstatus) != -1) { + debounce_buf[debounce_buf_i] = + (serstatus & (TIOCM_DSR | TIOCM_CTS)) ? + DSR_LINE_CLOSED_KEY : !DSR_LINE_CLOSED_KEY; + + debounce_buf_i++; + } + // + //*************************************************** + // back to business as usual + //*************************************************** + + /* If the debounce buffer is full, determine the state of the key */ + if (debounce_buf_i >= ks->debounce) { + debounce_buf_i = 0; + + j = 0; + for (i = 0; i < ks->debounce; i++) + if (debounce_buf[i]) + j++; + keystate = (j > ks->debounce / 2) ? 1 : 0; + } + + return keystate; +} + +//------------------------------------------------------------------------ + +/* Read an iambic key connected to a serial port, do debouncing, emulate a + straight key, then return the emulated key state */ + +BOOLEAN +read_iambic_key_serial(KeyerState ks, int fd, KeyerLogic kl, double ticklen) { + int i, j, serstatus; + static BOOLEAN dah_debounce_buf[DEBOUNCE_BUF_MAX_SIZE], + dit_debounce_buf[DEBOUNCE_BUF_MAX_SIZE]; + static int dah = 0, debounce_buf_i = 0, dit = 0; + + //*************************************************** + // replace this with parallel port logic if necessary + //*************************************************** + // + /* Read the key states */ + if (ioctl(fd, TIOCMGET, &serstatus) != -1) { + if (ks->flag.revpdl) { + dah_debounce_buf[debounce_buf_i] = + (serstatus & TIOCM_DSR) ? DSR_LINE_CLOSED_KEY : !DSR_LINE_CLOSED_KEY; + dit_debounce_buf[debounce_buf_i] = + (serstatus & TIOCM_CTS) ? CTS_LINE_CLOSED_KEY : !CTS_LINE_CLOSED_KEY; + } else { + dit_debounce_buf[debounce_buf_i] = + (serstatus & TIOCM_DSR) ? DSR_LINE_CLOSED_KEY : !DSR_LINE_CLOSED_KEY; + dah_debounce_buf[debounce_buf_i] = + (serstatus & TIOCM_CTS) ? CTS_LINE_CLOSED_KEY : !CTS_LINE_CLOSED_KEY; + } + + debounce_buf_i++; + } + // + //*************************************************** + // back to business as usual + //*************************************************** + + /* If the debounce buffer is full, determine the state of the keys */ + if (debounce_buf_i >= ks->debounce) { + debounce_buf_i = 0; + + j = 0; + for (i = 0; i < ks->debounce; i++) + if (dah_debounce_buf[i]) j++; + dah = (j > ks->debounce / 2) ? 1 : 0; + + j = 0; + for (i = 0; i < ks->debounce; i++) + if (dit_debounce_buf[i]) j++; + dit = (j > ks->debounce / 2) ? 1 : 0; + } + + return klogic(kl, + dit, + dah, + ks->wpm, + ks->mode, + ks->flag.mdlmdB, + ks->flag.memory.dit, + ks->flag.memory.dah, + ks->flag.autospace.khar, + ks->flag.autospace.word, + ks->weight, + ticklen); +} + +//======================================================================== + +KeyerState +newKeyerState(void) { + return (KeyerState) safealloc(1, sizeof(KeyerStateInfo), "newKeyerState"); +} + +void +delKeyerState(KeyerState ks) { + safefree((char *) ks); +} + +KeyerLogic +newKeyerLogic(void) { + return (KeyerLogic) safealloc(1, sizeof(KeyerLogicInfo), "newKeyerLogic"); +} + +void +delKeyerLogic(KeyerLogic kl) { + safefree((char *) kl); +} + +//======================================================================== diff --git a/jDttSP/keyer.h b/jDttSP/keyer.h new file mode 100644 index 0000000..16184d0 --- /dev/null +++ b/jDttSP/keyer.h @@ -0,0 +1,154 @@ +/* keyer.h */ +/* +This file is part of a program that implements a Software-Defined Radio. + +The code in this file is derived from routines originally written by +Pierre-Philippe Coupard for his CWirc X-chat program. That program +is issued under the GPL and is +Copyright (C) Pierre-Philippe Coupard - 18/06/2003 + +This derived version is +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#ifndef _keyer_h +#define _keyer_h + +#include +#include +#include +#include + +//======================================================================== + +#define DSR_LINE_CLOSED_KEY (1) +#define CTS_LINE_CLOSED_KEY (1) +#define DTR_LINE_SET (0) +#define RTS_LINE_SET (0) + +#define NO_TIMEOUTS_SCHED (-2) +#define NO_ELEMENT (-1) +#define DIT (0) +#define DAH (1) +#define MODE_A (0) +#define MODE_B (1) +#define NO_PADDLE_SQUEEZE (0) +#define PADDLES_SQUEEZED (1) +#define PADDLES_RELEASED (2) +#define NO_DELAY (0) +#define CHAR_SPACING_DELAY (1) +#define WORD_SPACING_DELAY (2) +#define DEBOUNCE_BUF_MAX_SIZE (30) + +//======================================================================== + +typedef +struct _keyer_state { + + struct { + + BOOLEAN iambic, // iambic or straight + mdlmdB, + revpdl; // paddles reversed + + struct { + BOOLEAN dit, dah; + } memory; + + struct { + BOOLEAN khar, word; + } autospace; + + } flag; + + int debounce, // # seconds to read paddles + mode, // 0 = mode A, 1 = mode B + weight; // 15 -> 85% + + double wpm; // for iambic keyer + +} KeyerStateInfo, *KeyerState; + +extern KeyerState newKeyerState(void); +extern void delKeyerState(KeyerState ks); + +//------------------------------------------------------------------------ + +typedef +struct _keyer_logic { + + struct { + BOOLEAN init; + + struct { + BOOLEAN dit, dah; + } prev; + + } flag; + + struct { + BOOLEAN invtd, // insert inverted element + psqam; // paddles squeezed after mid-element + int curr, // -1 = nothing, 0 = dit, 1 = dah + iamb, // 0 = none, 1 = squeezed, 2 = released + last; // -1 = nothing, 0 = dit, 1 = dah + } element; + + struct { + double beep, dlay, elem, midl; + } timeout; + + int dlay_type; // 0 = none, 1 = interchar, 2 = interword + +} KeyerLogicInfo, *KeyerLogic; + +extern KeyerLogic newKeyerLogic(void); +extern void delKeyerLogic(KeyerLogic kl); + +//======================================================================== + +extern BOOLEAN klogic(KeyerLogic kl, + BOOLEAN dit, + BOOLEAN dah, + double wpm, + int iambicmode, + BOOLEAN midelementmodeB, + BOOLEAN ditmemory, + BOOLEAN dahmemory, + BOOLEAN autocharspacing, + BOOLEAN autowordspacing, + int weight, + double ticklen); + +extern BOOLEAN read_straight_key_serial(KeyerState ks, int fd); +extern BOOLEAN read_iambic_key_serial(KeyerState ks, int fd, KeyerLogic kl, double ticklen); + +//======================================================================== + +#endif diff --git a/jDttSP/main.c b/jDttSP/main.c index a4611e3..4aa5884 100644 --- a/jDttSP/main.c +++ b/jDttSP/main.c @@ -48,6 +48,16 @@ extern void destroy_workspace(void); //======================================================================== +void +clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes) { + int i; + char zero = 0; + for (i = 0; i < nbytes; i++) + jack_ringbuffer_write(rb, &zero, 1); +} + +//======================================================================== + PRIVATE void monitor_thread(void) { while (top.running) { @@ -164,6 +174,9 @@ run_swch(void) { rx.tick = tx.tick = 0; top.state = top.swch.run.last; top.swch.bfct.want = top.swch.bfct.have = 0; + + jack_ringbuffer_reset(top.jack.ring.o.l); + jack_ringbuffer_reset(top.jack.ring.o.r); } process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames); @@ -171,13 +184,6 @@ run_swch(void) { //======================================================================== -void -clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes) { - int i; - char zero = 0; - for (i = 0; i < nbytes; i++) - jack_ringbuffer_write(rb, &zero, 1); -} PRIVATE void audio_callback(jack_nframes_t nframes, void *arg) { @@ -407,8 +413,8 @@ setup_system_audio(void) { top.jack.ring.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring); top.jack.ring.o.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring); top.jack.ring.o.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring); - clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes); - clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes); + clear_jack_ringbuffer(top.jack.ring.o.l, top.jack.size * sizeof(float)); + clear_jack_ringbuffer(top.jack.ring.o.r, top.jack.size * sizeof(float)); } PRIVATE void diff --git a/jDttSP/ovsv.c b/jDttSP/ovsv.c index 3575ff4..dfc2f0f 100644 --- a/jDttSP/ovsv.c +++ b/jDttSP/ovsv.c @@ -92,6 +92,11 @@ filter_OvSv_par(FiltOvSv pflt) { for (i = 0, j = n; i < n; i++, j++) zrvec[i] = zrvec[j]; } +void +reset_OvSv(FiltOvSv pflt) { + memset((char *) pflt->zrvec, 0, pflt->fftlen * sizeof(COMPLEX)); +} + /*------------------------------------------------------------*/ /* info: */ /* NB strategy. This is the address we pass to newCXB as diff --git a/jDttSP/ovsv.h b/jDttSP/ovsv.h index f4baa16..7429d54 100644 --- a/jDttSP/ovsv.h +++ b/jDttSP/ovsv.h @@ -71,4 +71,6 @@ extern COMPLEX *FiltOvSv_storept_par(FiltOvSv pflt, int parity); extern void filter_OvSv(FiltOvSv pflt); extern void filter_OvSv_par(FiltOvSv pflt); +extern void reset_OvSv(FiltOvSv pflt); + #endif diff --git a/jDttSP/sdr.c b/jDttSP/sdr.c index f04d641..a198d21 100644 --- a/jDttSP/sdr.c +++ b/jDttSP/sdr.c @@ -318,6 +318,18 @@ destroy_workspace(void) { // execution ////////////////////////////////////////////////////////////////////////// +//======================================================================== +// util + +PRIVATE REAL +CXBnorm(CXB buff) { + int i; + double sum = 0.0; + for (i = 0; i < CXBhave(buff); i++) + sum += Csqrmag(CXBdata(buff, i)); + return sqrt(sum); +} + //======================================================================== /* all */ @@ -367,15 +379,10 @@ PRIVATE BOOLEAN should_do_rx_squelch(void) { if (rx.squelch.flag) { int i, n = CXBhave(rx.buf.o); - REAL tst; rx.squelch.power = 0.0; for (i = 0; i < n; i++) rx.squelch.power += Csqrmag(CXBdata(rx.buf.o, i)); - tst = (10.0 * log10(rx.squelch.power) - + rx.squelch.offset.meter - + rx.squelch.offset.att - + rx.squelch.offset.gain); - return rx.squelch.thresh > tst; + return rx.squelch.thresh > 10.0 * log10(rx.squelch.power); } else return rx.squelch.set = FALSE; } @@ -439,6 +446,7 @@ do_rx_pre(void) { /* filtering, metering, & AGC */ if (rx.mode != SPEC) { + if (rx.tick == 0) reset_OvSv(rx.filt.ovsv); filter_OvSv(rx.filt.ovsv); CXBhave(rx.buf.o) = CXBhave(rx.buf.i); if (uni.meter.flag) do_meter(CXBbase(rx.buf.o), uni.buflen); @@ -449,7 +457,7 @@ do_rx_pre(void) { PRIVATE void do_rx_post(void) { - int i, n = CXBhave(rx.buf.o);; + int i, n = CXBhave(rx.buf.o); if (!rx.squelch.set) { no_squelch(); @@ -537,6 +545,7 @@ do_rx(void) { PRIVATE void do_tx_pre(void) { + if (tx.scl.pre.flag) { int i, n = CXBhave(tx.buf.i); for (i = 0; i < n; i++) @@ -544,12 +553,14 @@ do_tx_pre(void) { } // - // mix in CW tone here + // mix in CW tone here? // correctIQ(tx.buf.i, tx.iqfix); if (tx.spr.flag) SpeechProcessor(tx.spr.gen); + + if (tx.tick == 0) reset_OvSv(tx.filt.ovsv); filter_OvSv(tx.filt.ovsv); } @@ -576,38 +587,43 @@ do_tx_post(void) { PRIVATE void do_tx_SBCW(void) { - int i, n = min(CXBhave(tx.buf.i), uni.buflen); - for (i = 0; i < n; i++) { - tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.99), - Cscl(CXBdata(tx.buf.o,i), -0.01)); - CXBdata(tx.buf.o, i) = Cadd(CXBdata(tx.buf.o,i), tx.scl.dc); - } + int i, n = min(CXBhave(tx.buf.o), uni.buflen); + + if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0) + for (i = 0; i < n; i++) { + tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.99), + Cscl(CXBdata(tx.buf.o, i), -0.01)); + CXBdata(tx.buf.o, i) = Cadd(CXBdata(tx.buf.o, i), tx.scl.dc); + } } PRIVATE void do_tx_AM(void) { - int i, n = min(CXBhave(tx.buf.i), uni.buflen); - for (i = 0; i < n; i++) { - tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999), - Cscl(CXBdata(tx.buf.o,i), -0.001)); - CXBreal(tx.buf.o, i) = - 0.49995 + 0.49995 * (CXBreal(tx.buf.o,i) - tx.scl.dc.re); - CXBimag(tx.buf.o, i) = 0.0; - } + int i, n = min(CXBhave(tx.buf.o), uni.buflen); + + if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0) + for (i = 0; i < n; i++) { + tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999), + Cscl(CXBdata(tx.buf.o, i), -0.001)); + CXBreal(tx.buf.o, i) = + 0.49995 + 0.49995 * (CXBreal(tx.buf.o, i) - tx.scl.dc.re); + CXBimag(tx.buf.o, i) = 0.0; + } } PRIVATE void do_tx_FM(void) { - int i, n = min(CXBhave(tx.buf.i), uni.buflen); - for (i = 0; i < n; i++) { - tx.scl.dc = Cadd(Cscl(tx.scl.dc,0.999), - Cscl(CXBdata(tx.buf.o,i), 0.001)); - tx.osc.phase += (CXBreal(tx.buf.o, i)- tx.scl.dc.re) * CvtMod2Freq; - if (tx.osc.phase >= TWOPI) tx.osc.phase -= TWOPI; - if (tx.osc.phase < 0.0) tx.osc.phase += TWOPI; - CXBdata(tx.buf.o, i) = - Cscl(Cmplx(cos(tx.osc.phase), sin(tx.osc.phase)), 0.99999); - } + int i, n = min(CXBhave(tx.buf.o), uni.buflen); + if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0) + for (i = 0; i < n; i++) { + tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999), + Cscl(CXBdata(tx.buf.o, i), 0.001)); + tx.osc.phase += (CXBreal(tx.buf.o, i) - tx.scl.dc.re) * CvtMod2Freq; + if (tx.osc.phase >= TWOPI) tx.osc.phase -= TWOPI; + if (tx.osc.phase < 0.0) tx.osc.phase += TWOPI; + CXBdata(tx.buf.o, i) = + Cscl(Cmplx(cos(tx.osc.phase), sin(tx.osc.phase)), 0.99999); + } } PRIVATE void @@ -668,7 +684,7 @@ process_samples(float *bufl, float *bufr, int n) { for (i = 0; i < n; i++) bufl[i] = (float)CXBimag(tx.buf.o, i), bufr[i] = (float)CXBreal(tx.buf.o, i); - CXBhave(rx.buf.o) = n; + CXBhave(tx.buf.o) = n; break; } diff --git a/jDttSP/sdrexport.h b/jDttSP/sdrexport.h index b561bf7..860e3bf 100644 --- a/jDttSP/sdrexport.h +++ b/jDttSP/sdrexport.h @@ -147,15 +147,13 @@ extern struct _rx { } scl; struct { REAL thresh, power; - struct { - REAL meter, att, gain; - } offset; BOOLEAN flag, running, set; int num; } squelch; SDRMODE mode; struct { BOOLEAN flag; } bin; long tick; + REAL norm; } rx; //------------------------------------------------------------------------ @@ -197,6 +195,7 @@ extern struct _tx { } scl; SDRMODE mode; long tick; + REAL norm; } tx; //------------------------------------------------------------------------ diff --git a/jDttSP/spottone.c b/jDttSP/spottone.c index e4b2aa2..713ee13 100644 --- a/jDttSP/spottone.c +++ b/jDttSP/spottone.c @@ -1,5 +1,37 @@ /* spottone.c */ +/* +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + #include //------------------------------------------------------------------------ diff --git a/jDttSP/spottone.h b/jDttSP/spottone.h index 1b46240..8bd02d5 100644 --- a/jDttSP/spottone.h +++ b/jDttSP/spottone.h @@ -1,4 +1,35 @@ /* spottone.h */ +/* +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ #ifndef _spottone_h #define _spottone_h diff --git a/jDttSP/thunk.c b/jDttSP/thunk.c index e52d80a..21762f6 100644 --- a/jDttSP/thunk.c +++ b/jDttSP/thunk.c @@ -21,3 +21,14 @@ Thunk_lookup(CTB ctb, char *key) { } return (Thunk) 0; } + +#ifdef notdef +unsigned long +hash(unsigned char *str) { + unsigned long hash = 5381; + int c; + while (c = *str++) + hash = ((hash << 5) + hash) + c; // (hash * 33 + c) better + return hash; +} +#endif diff --git a/jDttSP/update.c b/jDttSP/update.c index 6d2594a..a090b76 100644 --- a/jDttSP/update.c +++ b/jDttSP/update.c @@ -521,24 +521,6 @@ setSquelch(int n, char **p) { return 0; } -PRIVATE int -setMeterOffset(int n, char **p) { - rx.squelch.offset.meter = atof(p[0]); - return 0; -} - -PRIVATE int -setATTOffset(int n, char **p) { - rx.squelch.offset.att = atof(p[0]); - return 0; -} - -PRIVATE int -setGainOffset(int n, char **p) { - rx.squelch.offset.gain = atof(p[0]); - return 0; -} - PRIVATE int setSquelchSt(int n, char **p) { rx.squelch.flag = atoi(p[0]); @@ -672,12 +654,9 @@ setRingBufferReset(int n, char **p) { CTE update_cmds[] = { {"setANF", setANF}, {"setANFvals", setANFvals}, - {"setATTOffset", setATTOffset}, {"setBIN", setBIN}, {"setFilter", setFilter}, {"setFinished", setFinished}, - {"setGainOffset", setGainOffset}, - {"setMeterOffset", setMeterOffset}, {"setMode", setMode}, {"setNB", setNB}, {"setNBvals", setNBvals},