1%
2% fmfsk.m
3% Author: Brady O'Brien 3 Feb 2016
4%   Copyright 2016 David Rowe
5%
6%  All rights reserved.
7%
8%  This program is free software; you can redistribute it and/or modify
9%  it under the terms of the GNU Lesser General Public License veRbion 2, as
10%  published by the Free Software Foundation.  This program is
11%  distributed in the hope that it will be useful, but WITHOUT ANY
12%  WARRANTY; without even the implied warranty of MERCHANTABILITY or
13%  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
14%  License for more details.
15%
16%  You should have received a copy of the GNU Lesser General Public License
17%  along with this program; if not, see <http://www.gnu.org/licenses/>.
18
19% mancyfsk.m modem, extracted and made suitable for C implementation
20
21fm;
22pkg load signal;
23pkg load parallel;
241;
25
26% Init fmfsk modem
27%Fs is sample frequency
28%Rb is pre-manchester bit rate
29function states = fmfsk_init(Fs,Rb)
30    assert(mod(Fs,Rb*2)==0);
31
32    %Current fixed processing buffer size, in non-ME bits
33    nbit = 96;
34
35    states.Rb = Rb;
36    states.Rs = Rb*2;   % Manchester-encoded bitrate
37    states.Fs = Fs;
38    states.Ts = Fs/states.Rs;
39    states.N = nbit*2*states.Ts;
40    states.nin = states.N;          % Samples in the next demod cycle
41    states.nstash = states.Ts*2;    % How many samples to stash away between proc cycles for timing adjust
42    states.nmem =  states.N+(4*states.Ts);
43    states.nsym = nbit*2;
44    states.nbit = nbit;
45
46    %tates.nsym = floor(states.Rs*.080);
47    %states.nbit = floor(states.Rb*.080)
48    %Old sample memory
49
50    states.oldsamps = zeros(1,states.nmem);
51
52    %Last sampled-stream output, for odd bitstream generation
53    states.lastint = 0;
54
55    %Some stats
56    states.norm_rx_timing = 0;
57
58endfunction
59
60%Generate a stream of manchester-coded bits to be sent
61% to any ordinary FM modulator or VCO or something
62function tx = fmfsk_mod(states,inbits)
63    Ts = states.Ts;
64    tx = zeros(1,length(inbits)*2);
65    for ii = 1:length(inbits)
66        st = 1 + (ii-1)*Ts*2;
67        md = st+Ts-1;
68        en = md+Ts;
69        if inbits(ii)==0
70            tx(st:md)   = -ones(1,Ts);
71            tx(md+1:en) =  ones(1,Ts);
72        else
73            tx(st:md)   =  ones(1,Ts);
74            tx(md+1:en) = -ones(1,Ts);
75        end
76    end
77endfunction
78
79%Demodulate a bag of bits from the output of an FM demodulator
80% This function produces nbits output bits and takes states.nin samples
81function [rx_bits states] = fmfsk_demod(states,rx)
82    Ts = states.Ts;
83    Fs = states.Fs;
84    Rs = states.Rs;
85    nin = states.nin;
86    N = states.N;
87    nsym = states.nsym;
88    nbits = states.nsym/2;
89    nmem = states.nmem;
90    nstash = states.nstash;
91
92    nold = nmem-nin;
93    ssamps = states.oldsamps;
94
95
96    %Shift in nin samples
97    ssamps(1:nold) = ssamps(nmem-nold+1:nmem);
98    ssamps(nold+1:nmem) = rx;
99    states.oldsamps = ssamps;
100
101    rx_filt = zeros(1,(nsym+1)*Ts);
102    %Integrate Ts input samples at every offset
103    %This is the same thing as filtering with a filter of all ones
104    % out to Ts.
105    % It's implemented like this for ease of C-porting
106    for ii=(1:(nsym+1)*Ts)
107        st = ii;
108        en = st+Ts-1;
109        rx_filt(ii) = sum(ssamps(st:en));
110    end
111    states.rx_filt = rx_filt;
112    % Fine timing estimation ------------------------------------------------------
113
114    % Estimate fine timing using line at Rs/2 that Manchester encoding provides
115    % We need this to sync up to Manchester codewords.
116    Np = length(rx_filt);
117    w = 2*pi*(Rs)/Fs;
118    x = (rx_filt .^ 2) * exp(-j*w*(0:Np-1))';
119    norm_rx_timing = angle(x)/(2*pi)-.42;
120
121    rx_timing = round(norm_rx_timing*Ts);
122
123    %If rx timing is too far out, ask for more or less sample the next time
124    % around to even it all out
125    next_nin = N;
126    if norm_rx_timing > -.2;
127       next_nin += Ts/2;
128    end
129    if norm_rx_timing < -.65;
130       next_nin -= Ts/2;
131    end
132
133    states.nin = next_nin;
134    states.norm_rx_timing = norm_rx_timing;
135    %'Even' and 'Odd' manchester bitstream.
136    % We'll figure out which to produce later
137    rx_even = zeros(1,nbits);
138    rx_odd = zeros(1,nbits);
139    apeven = 0;
140    apodd = 0;
141
142    sample_offset = (Ts/2)+Ts+rx_timing-1;
143
144    symsamp = zeros(1,nsym);
145
146    % Figure out the bits of the 'even' and 'odd' ME streams
147    % Also sample rx_filt offset by what fine timing determined along the way
148    % Note: ii is a zero-indexed array pointer, for less mind-breaking c portage
149    lastv = states.lastint;
150    for ii = (0:nsym-1)
151        currv = rx_filt(sample_offset+(ii*Ts)+1);
152        mdiff = lastv-currv;
153        lastv = currv;
154        mbit = mdiff>0;
155        symsamp(ii+1) = currv;
156        if mod(ii,2)==1
157            apeven += abs(mdiff);
158            rx_even( floor(ii/2)+1 ) = mbit;
159        else
160            apodd  += abs(mdiff);
161            rx_odd(  floor(ii/2)+1 ) = mbit;
162        end
163    end
164    states.symsamp = symsamp;
165    % Decide on the correct ME alignment
166    if(apeven>apodd)
167        rx_bits = rx_even;
168    else
169        rx_bits = rx_odd;
170    end
171
172    states.lastint = lastv;
173endfunction
174
175% run_sim copypasted from fsk_horus.m
176% simulation of tx and rx side, add noise, channel impairments ----------------------
177
178function fmfsk_run_sim(EbNodB,timing_offset=0,de=0,of=0,hpf=0)
179  test_frame_mode = 2;
180  frames = 70;
181  %EbNodB = 3;
182  %timing_offset = 0.0; % see resample() for clock offset below
183  %fading = 0;          % modulates tx power at 2Hz with 20dB fade depth,
184                       % to simulate balloon rotating at end of mission
185  df     = 0;          % tx tone freq drift in Hz/s
186  dA     = 1;          % amplitude imbalance of tones (note this affects Eb so not a gd idea)
187
188  more off
189  rand('state',1);
190  randn('state',1);
191
192  Fs = 48000;
193  Rbit = 2400;
194
195  % ----------------------------------------------------------------------
196
197  fm_states.pre_emp = 0;
198  fm_states.de_emp  = de;
199  fm_states.Ts      = Fs/(Rbit*2);
200  fm_states.Fs      = Fs;
201  fm_states.fc      = Fs/4;
202  fm_states.fm_max  = 3E3;
203  fm_states.fd      = 5E3;
204  fm_states.output_filter = of;
205  fm_states = analog_fm_init(fm_states);
206
207  % ----------------------------------------------------------------------
208
209  states = fmfsk_init(Fs,Rbit);
210
211  states.verbose = 0x1;
212  Rs = states.Rs;
213  nsym = states.nsym;
214  Fs = states.Fs;
215  nbit = states.nbit;
216
217  EbNo = 10^(EbNodB/10);
218  variance = states.Fs/(states.Rb*EbNo);
219
220  % set up tx signal with payload bits based on test mode
221
222  if test_frame_mode == 1
223     % test frame of bits, which we repeat for convenience when BER testing
224    test_frame = round(rand(1, states.nbit));
225    tx_bits = [];
226    for i=1:frames+1
227      tx_bits = [tx_bits test_frame];
228    end
229  end
230  if test_frame_mode == 2
231    % random bits, just to make sure sync algs work on random data
232    tx_bits = round(rand(1, states.nbit*(frames+1)));
233  end
234  if test_frame_mode == 3
235    % repeating sequence of all symbols
236    % great for initial test of demod if nothing else works,
237    % look for this pattern in rx_bits
238
239       % ...10101...
240      tx_bits = zeros(1, states.nbit*(frames+1));
241      tx_bits(1:2:length(tx_bits)) = 1;
242
243  end
244
245  load fm_radio_filt_model.txt
246
247  [b, a] = cheby1(4, 1, 300/Fs, 'high');   % 300Hz HPF to simulate FM radios
248
249  tx_pmod = fmfsk_mod(states, tx_bits);
250  tx = analog_fm_mod(fm_states, tx_pmod);
251
252  tx = tx(10:length(tx));
253
254  if(timing_offset>0)
255    tx = resample(tx, 1000,1001); % simulated 1000ppm sample clock offset
256  end
257
258
259  noise = sqrt(variance)*randn(length(tx),1);
260  rx    = tx + noise';
261
262  %Demod by analog fm
263  rx    = analog_fm_demod(fm_states, rx);
264
265  %High-pass filter to simulate the FM radios
266  if hpf>0
267    printf("high-pass filtering!\n")
268    rx = filter(b,a,rx);
269  end
270  rx = filter(filt,1,rx);
271
272  figure(4)
273  title("Spectrum of rx-ed signal after FM demod and FM radio channel");
274  plot(20*log10(abs(fft(rx))))
275  figure(5)
276  title("Time domain of rx-ed signal after FM demod and FM radio channel");
277  plot(rx)
278  %rx = real(rx);
279  %b1 = fir2(100, [0 4000 5200 48000]/48000, [1 1 0.5 0.5]);
280  %rx = filter(b1,1,rx);
281  %[b a] = cheby2(6,40,[3000 6000]/(Fs/2));
282  %rx = filter(b,a,rx);
283  %rx = sign(rx);
284  %rx(find (rx > 1)) = 1;
285  %rx(find (rx < -1)) = -1;
286
287  % dump simulated rx file
288
289  timing_offset_samples = round(timing_offset*states.Ts);
290  st = 1 + timing_offset_samples;
291  rx_bits_buf = zeros(1,2*nbit);
292  x_log = [];
293  timing_nl_log = [];
294  norm_rx_timing_log = [];
295  f_int_resample_log = [];
296  f_log = [];
297  EbNodB_log = [];
298  rx_bits_log = [];
299  rx_bits_sd_log = [];
300
301  for f=1:frames
302
303    % extract nin samples from input stream
304
305    nin = states.nin;
306    en = st + states.nin - 1;
307    sf = rx(st:en);
308    st += nin;
309
310    % demodulate to stream of bits
311
312    [rx_bits states] = fmfsk_demod(states, sf);
313
314    rx_bits_buf(1:nbit) = rx_bits_buf(nbit+1:2*nbit);
315    rx_bits_buf(nbit+1:2*nbit) = rx_bits;
316    rx_bits_log = [rx_bits_log rx_bits];
317
318  end
319
320  ber = 1;
321  ox = 1;
322  rx_bits = rx_bits_log;
323  bitcnt = length(tx_bits);
324  for offset = (1:100)
325    nerr = sum(xor(rx_bits(offset:length(rx_bits)),tx_bits(1:length(rx_bits)+1-offset)));
326    bern = nerr/(bitcnt-offset);
327    if(bern < ber)
328      ox = offset;
329      best_nerr = nerr;
330    end
331    ber = min([ber bern]);
332  end
333  offset = ox;
334  figure(3);
335  plot(xor(rx_bits(ox:length(rx_bits)),tx_bits(1:length(rx_bits)+1-ox)))
336
337  printf("BER: %f Errors: %d Bits:%d\n",ber,best_nerr,bitcnt-offset);
338
339 endfunction
340
341
342% demodulate a file of 8kHz 16bit short samples --------------------------------
343
344
345
346
347