1;
-function states = fsk_horus_init(Fs)
+function states = fsk_horus_init(Fs,Rs)
states.Ndft = 2.^ceil(log2(Fs)); % find nearest power of 2 for effcient FFT
states.Fs = Fs;
N = states.N = Fs; % processing buffer size, nice big window for f1,f2 estimation
- Rs = states.Rs = 100;
+ states.Rs = Rs;
Ts = states.Ts = Fs/Rs;
- states.nsym = N/Ts;
- Nmem = states.Nmem = N+2*Ts; % two symbol memory in down converted signals to allow for timing adj
+ assert(Ts == floor(Ts), "Fs/Rs must be an integer");
+ states.nsym = N/Ts; % number of symbols in one procesing frame
+ Nmem = states.Nmem = N+2*Ts; % two symbol memory in down converted signals to allow for timing adj
states.f1_dc = zeros(1,Nmem);
states.f2_dc = zeros(1,Nmem);
states.P = 8; % oversample rate out of filter
- states.nin = N; % can be N +/- Ts/P = N +/- 40 samples to adjust for sample clock offsets
+ assert(Ts/states.P == floor(Ts/states.P), "Ts/P must be an integer");
+
+ states.nin = N; % can be N +/- Ts/P samples to adjust for sample clock offsets
states.verbose = 0;
states.phi1 = 0; % keep down converter osc phase continuous
states.phi2 = 0;
+ printf("Fs: %d Rs: %d Ts: %d nsym: %d\n", states.Fs, states.Rs, states.Ts, states.nsym);
+
states.ber_state = 0;
states.Tbits = 0;
states.Terrs = 0;
tx_phase = 0;
Ts = states.Ts;
Fs = states.Fs;
- f1 = 1000; f2 = 1400;
+ f1 = states.f1_tx; f2 = states.f2_tx;
df = states.df; % tone freq change in Hz/s
for i=1:length(tx_bits)
Sf = fft(sf .* h, Ndft);
[m1 m1_index] = max(Sf(1:Ndft/2));
- % zero out region around max so we can find second highest peak
+ % zero out region 100Hz either side of max so we can find second highest peak
Sf2 = Sf;
- st = m1_index - 100;
+ st = m1_index - floor(100*Ndft/Fs);
if st < 1
st = 1;
end
- en = m1_index + 100;
+ en = m1_index + floor(100*Ndft/Fs);
if en > Ndft/2
en = Ndft/2;
end
states.f1 = f1;
states.f2 = f2;
- % printf("f1: %4.0f Hz f2: %4.0f Hz a1: %f a2: %f\n", f1, f2, 2.0*abs(m1)/Ndft, 2.0*abs(m2)/Ndft);
-
+ if bitand(verbose,0x1)
+ printf("f1: %4.0f Hz f2: %4.0f Hz a1: %f a2: %f\n", f1, f2, 2.0*abs(m1)/Ndft, 2.0*abs(m2)/Ndft);
+ end
+
% down convert and filter at rate P ------------------------------
% update filter (integrator) memory by shifting in nin samples
fract = rx_timing - low_sample;
high_sample = ceil(rx_timing);
- if verbose
+ if bitand(verbose,0x2)
printf("rx_timing: %3.2f low_sample: %d high_sample: %d fract: %3.3f nin_next: %d\n", rx_timing, low_sample, high_sample, fract, next_nin);
end
states.coarse_offset = i;
end
end
- if nerrs_min < 3
+ if nerrs_min/nsym < 0.05
next_state = 1;
- %printf("%d %d\n", states.coarse_offset, nerrs_min);
+ end
+ if bitand(states.verbose,0x4)
+ printf("coarse offset: %d nerrs_min: %d next_state: %d\n", states.coarse_offset, nerrs_min, next_state);
end
end
% simulation of tx and rx side, add noise, channel impairments ----------------------
function run_sim
- frames = 100;
+ frames = 60;
EbNodB = 8;
timing_offset = 0.0; % see resample() for clock offset below
test_frame_mode = 1;
more off
rand('state',1);
randn('state',1);
- states = fsk_horus_init(96000);
+ states = fsk_horus_init(96000, 1200);
+ states.f1_tx = 1200;
+ states.f2_tx = 2400;
+ states.verbose = 0x4;
N = states.N;
P = states.P;
Rs = states.Rs;
noise = sqrt(variance)*randn(length(tx),1);
rx = tx + noise;
-
+
% dump simulated rx file
- ftx=fopen("fsk_horus_rx.raw","wb"); rxg = rx*1000; fwrite(ftx, rxg, "short"); fclose(ftx);
+ ftx=fopen("fsk_horus_rx_1200_96k.raw","wb"); rxg = rx*1000; fwrite(ftx, rxg, "short"); fclose(ftx);
timing_offset_samples = round(timing_offset*states.Ts);
st = 1 + timing_offset_samples;
% demodulate a file of 8kHz 16bit short samples --------------------------------
function rx_bits_log = demod_file(filename, test_frame_mode)
- rx = load_raw(filename);
+ rx = load_raw(filename);
more off;
- states = fsk_horus_init(96000);
+ states = fsk_horus_init(96000, 1200);
+ states.verbose = 0x1 + 0x4;
N = states.N;
P = states.P;
Rs = states.Rs;
[rx_bits states] = fsk_horus_demod(states, sf);
rx_bits_buf(1:nsym) = rx_bits_buf(nsym+1:2*nsym);
- rx_bits_buf(nsym+1:2*nsym) = rx_bits;
+ rx_bits_buf(nsym+1:2*nsym) = rx_bits; % xor(rx_bits,ones(1,nsym));
rx_bits_log = [rx_bits_log rx_bits];
rx_bits_sd_log = [rx_bits_sd_log states.rx_bits_sd];
norm_rx_timing_log = [norm_rx_timing_log states.norm_rx_timing];
figure(2)
clf
+ subplot(211)
plot(norm_rx_timing_log)
axis([1 frames -0.5 0.5])
title('norm fine timing')
grid
-
+ subplot(212)
+ plot(states.nerr_log)
+ title('num bit errors each frame')
+
figure(3)
clf
plot(EbNodB_log);
figure(4)
clf
+ subplot(211)
plot(rx);
title('input signal to demod')
+ subplot(212)
+ plot(20*log10(abs(fft(rx(1:states.Fs)))))
- figure(6);
+ figure(5);
clf
plot(ppm_log)
title('Sample clock (baud rate) offset in PPM');
printf("frame sync and data extraction...\n");
if test_frame_mode == 1
- printf("frames: %d Tbits: %d Terrs: %d BER %4.3f\n", frames, states.Tbits,states. Terrs, states.Terrs/states.Tbits);
+ printf("frames: %d Tbits: %d Terrs: %d BER %4.3f EbNo: %3.2f\n", frames, states.Tbits,states. Terrs, states.Terrs/states.Tbits, mean(EbNodB_log));
end
if test_frame_mode == 4
% run test functions from here during development
if exist("fsk_horus_as_a_lib") == 0
- %run_sim;
+ run_sim;
%rx_bits = demod_file("~/Desktop/vk5arg-3-1.wav",4);
%rx_bits = demod_file("~/Desktop/fsk_horus_10dB_1000ppm.wav",4);
%rx_bits = demod_file("~/Desktop/fsk_horus_6dB_0ppm.wav",4);
- rx_bits = demod_file("fsk_horus_rx.raw",1);
+ %rx_bits = demod_file("../stm32/test1.raw",1);
+ %rx_bits = demod_file("fsk_horus_rx_1200_96k.raw",1);
%rx_bits = demod_file("mp.raw",4);
%rx_bits = demod_file("~/Desktop/launchbox_v2_landing_8KHz_final.wav",4);
end
--- /dev/null
+% hackrf_uc.m
+%
+% David Rowe Nov 2015
+%
+% Upconverts a real baseband sample file to a file suitable for input into a HackRF
+%
+% To play file at 10.7MHz used:
+% octave:25> hackrf_uc("fsk_10M.iq","fsk_horus_rx_1200_96k.raw")
+% $ hackrf_transfer -t ../octave/fsk_10M.iq -f 10000000 -a 0 -a 1 -x 40
+
+function hackrf_uc(outfilename, infilename)
+ Fs1 = 96E3; % input sample rate
+ Fs2 = 10E6; % output sample rate to HackRF
+ fc = 700E3; % offset to shift to, HackRF doesn't like signals in the centre
+ A = 100; % amplitude of signal after upc-nversion (max 127)
+ N = Fs1*20;
+
+ fin = fopen(infilename,"rb");
+ s1 = fread(fin,Inf,"short");
+ fclose(fin);
+ ls1 = length(s1);
+
+ % limit noise to first 4 kHz. Otherwise noise dominates signals and
+ % gain control below pushes wanted signal amplitude down so far we
+ % use only a few DAC/ADC bits
+
+ [b a] = cheby2(6,40,[500 4000]/(Fs1/2));
+ %s1 = filter(b,a,s1);
+
+ % single sided freq shifts, we don't want DSB
+
+ s1 = hilbert(s1(1:N));
+
+ % upsample to Fs2
+
+ M = Fs2/Fs1;
+ s2 = resample(s1(1:N),Fs2,Fs1);
+ ls2 = length(s2);
+ mx = max(abs(s2));
+ t = 0:ls2-1;
+
+ % shift up to Fc, not sure of rot90 rather than trasnpose operator '
+ % as we don't want complex conj, that would shift down in freq
+
+ sout = rot90((A/mx)*s2) .* exp(j*2*pi*t*fc/Fs2);
+
+ save_hackrf(outfilename,sout);
+end