From: drowe67 Date: Tue, 1 Jul 2014 21:49:43 +0000 (+0000) Subject: goertzal and 2m rx code X-Git-Url: http://git.whiteaudio.com/gitweb/?a=commitdiff_plain;h=9799a84dc66cf78f7d3217ac05ef33220007b150;p=freetel-svn-tracking.git goertzal and 2m rx code git-svn-id: https://svn.code.sf.net/p/freetel/code@1724 01035d8c-6547-0410-b346-abe4f91aad63 --- diff --git a/misc/.gitignore b/misc/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/misc/goertzal/goertzal.c b/misc/goertzal/goertzal.c new file mode 100644 index 00000000..22e4d95f --- /dev/null +++ b/misc/goertzal/goertzal.c @@ -0,0 +1,312 @@ +/* + goertzal.c + David Rowe & Matthew Cook + 21 June 2014 + + Step by Step port of Goertzal algorithm to fixed point, and tone + decoder automated testing. + + ref: + http://en.wikipedia.org/wiki/Goertzel_algorithm + + usage: + $ gcc goertzal.c -o goertzal -Wall -lm + $ ./goertzal +*/ + +#include +#include +#include + +#define FS 8000 +#define F 1000 +#define N 768 +#define AMP 512 + +/* Vanilla float version. Note optional printfs to dump state + variables for plotting */ + +float goertzal(short x[], int nmax, float coeff) { + float s, power; + float sprev, sprev2; + int n; + + sprev = 0; + sprev2 = 0; + for(n=0; n>16 = ((x[n] + (coeff * s[n-1]) - s[n-2])<<10)>>16 + s[n]>>6 = (x[n] + (coeff * s[n-1]) - s[n-2])>>6 + s[n]>>6 = x[n]>>6 + (coeff * (s[n-1]>>6)) - s[n-2]>>6 + + Note s>>6, s[n-1]>>6, s[n-2]>>6 must all be in the same format, + as they are just time delayed versions of each other. In + particular the middle term is expressed as: + + (coeff * s[n-1])>>6 = (coeff * (s>[n-1]>.6)) to keep s[n-1]>>6 in + the same format as s[n] and s[n-2]. This is subtle, but important. + + A very neat trick is to use a change of variables: + + z[n] = s[n]>>6 + + Now we have: + + z[n] = x[n]>>6 + (coeff * z[n-1]) - z[n-2] + + Now coeff is stored in Q2.14, so: + + z[n] = x[n]>>6 + ((coeff<<14) * z[n-1])>>14 - z[n-2] + z[n] = x[n]>>6 + (coeff_q14 * z[n-1]) >>14 - z[n-2] + + Note we need the >>14 to balance out the scaling of coeff. We + multiply two 16 bit numbers to get a 32 bit result. This can be + scaled back down to 16 bits before being added to the + other terms. Numbers being added must all have the same format + (same scaling). + + Finally, we can recover s[n]: + + sn = z[n]<<6 + */ + + zprev = 0; + zprev2 = 0; + for(n=0; n>6) + (int)(mult>>14) - zprev2; + zprev2 = zprev; + zprev = z; + } + + sprev = (float)zprev*pow(2,6); + sprev2 = (float)zprev2*pow(2,6); + + power = sprev2*sprev2 + sprev*sprev - coeff*sprev*sprev2; + + return power; +} + +/* lastly, final power calculation in fixed point */ + +float goertzal3(short x[], int nmax, float coeff) { + short coeff_q14; + short z, zprev, zprev2; + int mult, pz; + int n; + + coeff_q14 = (1<<14)*coeff; + + zprev = 0; + zprev2 = 0; + for(n=0; n>6) + (mult>>14) - zprev2; + zprev2 = zprev; + zprev = z; + } + + /* + Lastly, lets convert the power term to fixed point: + + pz = z[n-2]*z[n-2] + z[n-1]*z[n-1] + coeff*z[n-1]*z[n-2] + pz = z[n-2]*z[n-2] + z[n-1]*z[n-1] + (coeff<<14)*z[n-1]*z[n-2]>>14 + + Given z[n] = s[n]>>6, we get: + + pz = (s[n-2]*s[n-2] + s[n-1]*s[n-1] + coeff*s[n-1]*s[n-2]) >> 12 + pz<<12 = s[n-2]*s[n-2] + s[n-1]*s[n-1] + coeff*s[n-1]*s[n-2] + = power + + i.e power = pz<<12 + */ + + mult = (int)coeff_q14*(int)zprev; + pz = zprev2*zprev2 + zprev*zprev - ((short)(mult>>14))*zprev2; + + return (float)pz*pow(2.0,12); +} + +void howclose(float x, float y, float delta) { + if (fabs((x-y)/x) < delta) + printf(" PASS\n"); + else + printf(" FAIL\n"); +} + +/* automated test of for goertzal tone detector */ + +void test_tone(float coeff, float freq, float Fs, float gaindB, int expect_tone) { + float amp, p, e; + short x[N]; + int n, tone; + + amp = AMP*pow(10.0, gaindB/20.0); + e = 0.0; + for(n=0; n e*(N/2)*0.1) + tone = 1; + else + tone = 0; + printf(" p: %e e: %e expect: %d we got: %d ", p, e*N/2, expect_tone, tone); + if (tone == expect_tone) + printf(" PASS\n"); + else + printf(" FAIL\n"); +} + +int main(void) { + float w, coeff, f, Fs, fctss; + float ideal_goertzal_pwr, goertzal_float_pwr; + float goertzal1_pwr, goertzal2_pwr, goertzal3_pwr; + FILE *fpwr; + short x[N]; + int n; + + // Test 1: single tone + + w = 2.0* M_PI * ((float)F/FS); + coeff = 2.0* cos(w); + + //printf("coeff = %f\n", coeff); + + for(n=0; n (num_levels-1))) = num_levels-1; +endfunction + +function value = unquantise_value(index, min_value, max_value, num_levels) + step = (max_value - min_value)/num_levels; + value = min_value + step*(index); +endfunction + +more off; + +% constants ------------------------- + +Fs = 1E6; +fc = 250E3; wc = 2*pi*fc/Fs; +fm = 1E3; wm = 2*pi*fm/Fs; + +f_max_deviation = 5E3; w_max_deviation = 2*pi*f_max_deviation/Fs; +t = 1:1E6; + +fi = fc+50E3; wi = 2*pi*fi/Fs; +Ai = 10; + +Fs2 = 50E3; +method = 1; +M=Fs/Fs2; +[b,a] = cheby1(8, 1, 20E3/Fs); + +clip = 10; +levels = 2^10; + +% simulation ------------------------- + +% signal at input to ADC + +mod = cos(wm*t); +wt = wc*t + w_max_deviation*mod; +tx = cos(wt) + j*sin(wt) + Ai*(cos(wi*t) + j*sin(wi*t)); +%tx = real(tx); + +% simulate ADC (sign bit only) + +rx = real(tx); +indexes = quantise_value(rx, -clip, clip, 1024); +rx = unquantise_value(indexes, -clip, clip, 1024); +%rx = sign(real(tx)) + j*sign(imag(tx)); + +% downconvert to complex baseband and filter/decimate to Fs2 + +rx_bb = rx .* exp(-j*wc*t); +rx_filt = filter(b, a, rx_bb); +rx_bb2 = rx_filt(1:M:length(rx_filt)); +rx_bb2 = sign(real(rx_bb2)) + j*sign(imag(rx_bb2)); + +% demodulate + +if method == 1 + + % atan first, then differentiate. However we need to unwrap + % phase to avoid 0 -> 2*pi jumps which all gets a bit messy + + phase = unwrap(atan2(imag(rx_bb2),real(rx_bb2))); + demod = M*2*pi*diff(phase); +end + +% differentiate phase first in rect coords which avoids trig functions +% and nasty 0 -> 2*pi wrap around and allows approximation to avoid +% trig functions altogether. Much smarter. + +if method == 2 + + % multiplying by complex conjugate subtracts phases + % exp(ja)*exp(jb)' = exp(ja)*exp(jb)' + % = exp(ja)*exp(-jb) + % = exp(j(b-a)) + + l = length(rx_bb2); + rx_bb_diff = rx_bb2(2:l) .* conj(rx_bb2(1:l-1)); + + % for x small, tan(x) = sin(x) = x = imag(exp(jx)) + + demod = 2*pi*M*imag(rx_bb_diff); + +end + +% 300-3000Hz BPF on demodualted audio + +[b_bpf a_bpf] = cheby1(4, 2, [300/Fs2 3000/Fs2]); +audio_out = filter(b_bpf, a_bpf, demod); + +% plot results -------------------------------------- + +figure(1) +clf +subplot(311) +plot(real(tx(1:100))) +ylabel('ADC input') +subplot(312) +plot(real(rx(1:100))) +ylabel('ADC output') +subplot(313) +plot(audio_out(1:400)) +ylabel('Audio Out') +%axis([1 400 -1 1]) + +figure(2) +subplot(311) +[p,f] = pwelch(tx); +plot(f,10*log10(p)); +ylabel('ADC Input'); +subplot(312) +[p,f] = pwelch(rx); +plot(f,10*log10(p)); +ylabel('ADC Output') +subplot(313) +[p,f] = pwelch(audio_out); +plot(f,10*log10(p)); +ylabel('Input to Demod'); + +if 0 +figure(3) +clf; +H=freqz(b,a,Fs/10); +HdB=20*log10(abs(H)); +plot((1:Fs2/10)*10/1000, HdB(1:Fs2/10)); +axis([1 Fs2/1000 -40 0]); +grid +end