aboutsummaryrefslogtreecommitdiff
path: root/octave/fmfsk.m
diff options
context:
space:
mode:
Diffstat (limited to 'octave/fmfsk.m')
-rw-r--r--octave/fmfsk.m346
1 files changed, 346 insertions, 0 deletions
diff --git a/octave/fmfsk.m b/octave/fmfsk.m
new file mode 100644
index 0000000..4b3cc91
--- /dev/null
+++ b/octave/fmfsk.m
@@ -0,0 +1,346 @@
+%
+% fmfsk.m
+% Author: Brady O'Brien 3 Feb 2016
+% Copyright 2016 David Rowe
+%
+% All rights reserved.
+%
+% This program is free software; you can redistribute it and/or modify
+% it under the terms of the GNU Lesser General Public License veRbion 2, as
+% published by the Free Software Foundation. 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 Lesser General Public License
+% along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+% mancyfsk.m modem, extracted and made suitable for C implementation
+
+fm;
+pkg load signal;
+pkg load parallel;
+1;
+
+% Init fmfsk modem
+%Fs is sample frequency
+%Rb is pre-manchester bit rate
+function states = fmfsk_init(Fs,Rb)
+ assert(mod(Fs,Rb*2)==0);
+
+ %Current fixed processing buffer size, in non-ME bits
+ nbit = 96;
+
+ states.Rb = Rb;
+ states.Rs = Rb*2; % Manchester-encoded bitrate
+ states.Fs = Fs;
+ states.Ts = Fs/states.Rs;
+ states.N = nbit*2*states.Ts;
+ states.nin = states.N; % Samples in the next demod cycle
+ states.nstash = states.Ts*2; % How many samples to stash away between proc cycles for timing adjust
+ states.nmem = states.N+(4*states.Ts);
+ states.nsym = nbit*2;
+ states.nbit = nbit;
+
+ %tates.nsym = floor(states.Rs*.080);
+ %states.nbit = floor(states.Rb*.080)
+ %Old sample memory
+
+ states.oldsamps = zeros(1,states.nmem);
+
+ %Last sampled-stream output, for odd bitstream generation
+ states.lastint = 0;
+
+ %Some stats
+ states.norm_rx_timing = 0;
+
+endfunction
+
+%Generate a stream of manchester-coded bits to be sent
+% to any ordinary FM modulator or VCO or something
+function tx = fmfsk_mod(states,inbits)
+ Ts = states.Ts;
+ tx = zeros(1,length(inbits)*2);
+ for ii = 1:length(inbits)
+ st = 1 + (ii-1)*Ts*2;
+ md = st+Ts-1;
+ en = md+Ts;
+ if inbits(ii)==0
+ tx(st:md) = -ones(1,Ts);
+ tx(md+1:en) = ones(1,Ts);
+ else
+ tx(st:md) = ones(1,Ts);
+ tx(md+1:en) = -ones(1,Ts);
+ end
+ end
+endfunction
+
+%Demodulate a bag of bits from the output of an FM demodulator
+% This function produces nbits output bits and takes states.nin samples
+function [rx_bits states] = fmfsk_demod(states,rx)
+ Ts = states.Ts;
+ Fs = states.Fs;
+ Rs = states.Rs;
+ nin = states.nin;
+ N = states.N;
+ nsym = states.nsym;
+ nbits = states.nsym/2;
+ nmem = states.nmem;
+ nstash = states.nstash;
+
+ nold = nmem-nin;
+ ssamps = states.oldsamps;
+
+
+ %Shift in nin samples
+ ssamps(1:nold) = ssamps(nmem-nold+1:nmem);
+ ssamps(nold+1:nmem) = rx;
+ states.oldsamps = ssamps;
+
+ rx_filt = zeros(1,(nsym+1)*Ts);
+ %Integrate Ts input samples at every offset
+ %This is the same thing as filtering with a filter of all ones
+ % out to Ts.
+ % It's implemented like this for ease of C-porting
+ for ii=(1:(nsym+1)*Ts)
+ st = ii;
+ en = st+Ts-1;
+ rx_filt(ii) = sum(ssamps(st:en));
+ end
+ states.rx_filt = rx_filt;
+ % Fine timing estimation ------------------------------------------------------
+
+ % Estimate fine timing using line at Rs/2 that Manchester encoding provides
+ % We need this to sync up to Manchester codewords.
+ Np = length(rx_filt);
+ w = 2*pi*(Rs)/Fs;
+ x = (rx_filt .^ 2) * exp(-j*w*(0:Np-1))';
+ norm_rx_timing = angle(x)/(2*pi)-.42;
+
+ rx_timing = round(norm_rx_timing*Ts);
+
+ %If rx timing is too far out, ask for more or less sample the next time
+ % around to even it all out
+ next_nin = N;
+ if norm_rx_timing > -.2;
+ next_nin += Ts/2;
+ end
+ if norm_rx_timing < -.65;
+ next_nin -= Ts/2;
+ end
+
+ states.nin = next_nin;
+ states.norm_rx_timing = norm_rx_timing;
+ %'Even' and 'Odd' manchester bitstream.
+ % We'll figure out which to produce later
+ rx_even = zeros(1,nbits);
+ rx_odd = zeros(1,nbits);
+ apeven = 0;
+ apodd = 0;
+
+ sample_offset = (Ts/2)+Ts+rx_timing-1;
+
+ symsamp = zeros(1,nsym);
+
+ % Figure out the bits of the 'even' and 'odd' ME streams
+ % Also sample rx_filt offset by what fine timing determined along the way
+ % Note: ii is a zero-indexed array pointer, for less mind-breaking c portage
+ lastv = states.lastint;
+ for ii = (0:nsym-1)
+ currv = rx_filt(sample_offset+(ii*Ts)+1);
+ mdiff = lastv-currv;
+ lastv = currv;
+ mbit = mdiff>0;
+ symsamp(ii+1) = currv;
+ if mod(ii,2)==1
+ apeven += abs(mdiff);
+ rx_even( floor(ii/2)+1 ) = mbit;
+ else
+ apodd += abs(mdiff);
+ rx_odd( floor(ii/2)+1 ) = mbit;
+ end
+ end
+ states.symsamp = symsamp;
+ % Decide on the correct ME alignment
+ if(apeven>apodd)
+ rx_bits = rx_even;
+ else
+ rx_bits = rx_odd;
+ end
+
+ states.lastint = lastv;
+endfunction
+
+% run_sim copypasted from fsk_horus.m
+% simulation of tx and rx side, add noise, channel impairments ----------------------
+
+function fmfsk_run_sim(EbNodB,timing_offset=0,de=0,of=0,hpf=0)
+ test_frame_mode = 2;
+ frames = 70;
+ %EbNodB = 3;
+ %timing_offset = 0.0; % see resample() for clock offset below
+ %fading = 0; % modulates tx power at 2Hz with 20dB fade depth,
+ % to simulate balloon rotating at end of mission
+ df = 0; % tx tone freq drift in Hz/s
+ dA = 1; % amplitude imbalance of tones (note this affects Eb so not a gd idea)
+
+ more off
+ rand('state',1);
+ randn('state',1);
+
+ Fs = 48000;
+ Rbit = 2400;
+
+ % ----------------------------------------------------------------------
+
+ fm_states.pre_emp = 0;
+ fm_states.de_emp = de;
+ fm_states.Ts = Fs/(Rbit*2);
+ fm_states.Fs = Fs;
+ fm_states.fc = Fs/4;
+ fm_states.fm_max = 3E3;
+ fm_states.fd = 5E3;
+ fm_states.output_filter = of;
+ fm_states = analog_fm_init(fm_states);
+
+ % ----------------------------------------------------------------------
+
+ states = fmfsk_init(Fs,Rbit);
+
+ states.verbose = 0x1;
+ Rs = states.Rs;
+ nsym = states.nsym;
+ Fs = states.Fs;
+ nbit = states.nbit;
+
+ EbNo = 10^(EbNodB/10);
+ variance = states.Fs/(states.Rb*EbNo);
+
+ % set up tx signal with payload bits based on test mode
+
+ if test_frame_mode == 1
+ % test frame of bits, which we repeat for convenience when BER testing
+ test_frame = round(rand(1, states.nbit));
+ tx_bits = [];
+ for i=1:frames+1
+ tx_bits = [tx_bits test_frame];
+ end
+ end
+ if test_frame_mode == 2
+ % random bits, just to make sure sync algs work on random data
+ tx_bits = round(rand(1, states.nbit*(frames+1)));
+ end
+ if test_frame_mode == 3
+ % repeating sequence of all symbols
+ % great for initial test of demod if nothing else works,
+ % look for this pattern in rx_bits
+
+ % ...10101...
+ tx_bits = zeros(1, states.nbit*(frames+1));
+ tx_bits(1:2:length(tx_bits)) = 1;
+
+ end
+
+ load fm_radio_filt_model.txt
+
+ [b, a] = cheby1(4, 1, 300/Fs, 'high'); % 300Hz HPF to simulate FM radios
+
+ tx_pmod = fmfsk_mod(states, tx_bits);
+ tx = analog_fm_mod(fm_states, tx_pmod);
+
+ tx = tx(10:length(tx));
+
+ if(timing_offset>0)
+ tx = resample(tx, 1000,1001); % simulated 1000ppm sample clock offset
+ end
+
+
+ noise = sqrt(variance)*randn(length(tx),1);
+ rx = tx + noise';
+
+ %Demod by analog fm
+ rx = analog_fm_demod(fm_states, rx);
+
+ %High-pass filter to simulate the FM radios
+ if hpf>0
+ printf("high-pass filtering!\n")
+ rx = filter(b,a,rx);
+ end
+ rx = filter(filt,1,rx);
+
+ figure(4)
+ title("Spectrum of rx-ed signal after FM demod and FM radio channel");
+ plot(20*log10(abs(fft(rx))))
+ figure(5)
+ title("Time domain of rx-ed signal after FM demod and FM radio channel");
+ plot(rx)
+ %rx = real(rx);
+ %b1 = fir2(100, [0 4000 5200 48000]/48000, [1 1 0.5 0.5]);
+ %rx = filter(b1,1,rx);
+ %[b a] = cheby2(6,40,[3000 6000]/(Fs/2));
+ %rx = filter(b,a,rx);
+ %rx = sign(rx);
+ %rx(find (rx > 1)) = 1;
+ %rx(find (rx < -1)) = -1;
+
+ % dump simulated rx file
+
+ timing_offset_samples = round(timing_offset*states.Ts);
+ st = 1 + timing_offset_samples;
+ rx_bits_buf = zeros(1,2*nbit);
+ x_log = [];
+ timing_nl_log = [];
+ norm_rx_timing_log = [];
+ f_int_resample_log = [];
+ f_log = [];
+ EbNodB_log = [];
+ rx_bits_log = [];
+ rx_bits_sd_log = [];
+
+ for f=1:frames
+
+ % extract nin samples from input stream
+
+ nin = states.nin;
+ en = st + states.nin - 1;
+ sf = rx(st:en);
+ st += nin;
+
+ % demodulate to stream of bits
+
+ [rx_bits states] = fmfsk_demod(states, sf);
+
+ rx_bits_buf(1:nbit) = rx_bits_buf(nbit+1:2*nbit);
+ rx_bits_buf(nbit+1:2*nbit) = rx_bits;
+ rx_bits_log = [rx_bits_log rx_bits];
+
+ end
+
+ ber = 1;
+ ox = 1;
+ rx_bits = rx_bits_log;
+ bitcnt = length(tx_bits);
+ for offset = (1:100)
+ nerr = sum(xor(rx_bits(offset:length(rx_bits)),tx_bits(1:length(rx_bits)+1-offset)));
+ bern = nerr/(bitcnt-offset);
+ if(bern < ber)
+ ox = offset;
+ best_nerr = nerr;
+ end
+ ber = min([ber bern]);
+ end
+ offset = ox;
+ figure(3);
+ plot(xor(rx_bits(ox:length(rx_bits)),tx_bits(1:length(rx_bits)+1-ox)))
+
+ printf("BER: %f Errors: %d Bits:%d\n",ber,best_nerr,bitcnt-offset);
+
+ endfunction
+
+
+% demodulate a file of 8kHz 16bit short samples --------------------------------
+
+
+
+