aboutsummaryrefslogtreecommitdiff
path: root/octave/fsk4_dmr.m
diff options
context:
space:
mode:
Diffstat (limited to 'octave/fsk4_dmr.m')
-rw-r--r--octave/fsk4_dmr.m540
1 files changed, 0 insertions, 540 deletions
diff --git a/octave/fsk4_dmr.m b/octave/fsk4_dmr.m
deleted file mode 100644
index e6ccb7a..0000000
--- a/octave/fsk4_dmr.m
+++ /dev/null
@@ -1,540 +0,0 @@
-% fsk4.m
-%
-% Brady O'Brien October 2015
-%
-% 4FSK modem attempt from the DMR spec
-
-graphics_toolkit("gnuplot");
-
-fm; % analog FM modulator functions
-
-pkg load signal;
-
-% Init function for modem ------------------------------------------------------------
-
-function fsk4_states = fsk4_init(fsk4_states,fsk4_info)
- Fs = fsk4_states.Fs = 48000; %Sample rate
- Rs = fsk4_states.Rs = fsk4_info.rs; %Symbol rate
- M = fsk4_states.M = fsk4_states.Fs/fsk4_states.Rs; %Samples per symbol
-
- % Set up 4FSK raised cosine filter. This probably screws up perf if we were using
- % optimal mod and dmeods but helps performance when using nasty old analog FM mods
- % and demods
-
- empty_filter = [zeros(1,99) 1];
-
- rf = (0:(Fs/2));
- %If there's no filter with this modem configuration, don't bother generating one
- if fsk4_info.no_filter
- fsk4_states.tx_filter = empty_filter;
- fsk4_states.rx_filter = empty_filter;
- else
- fsk4_states.tx_filter = fir2(400 ,rf/(Fs/2),fsk4_info.tx_filt_resp(rf));
- fsk4_states.rx_filter = fir2(400 ,rf/(Fs/2),fsk4_info.rx_filt_resp(rf));
- endif
-
- %fsk4_states.tx_filter = fsk4_states.rx_filter = [zeros(1,99) 1];
- %Set up the 4FSK symbols
- fsk4_states.symmap = fsk4_info.syms / fsk4_info.max_dev;
-
- fm_states.Ts = M;
- fm_states.Fs = Fs;
- fm_states.fc = 0;
- fm_states.fm_max = fsk4_info.max_dev*2;
- fm_states.fd = fsk4_info.max_dev;
- fm_states.pre_emp = fm_states.de_emp = 0;
- fm_states.output_filter = 0;
- fm_states.ph_dont_limit = 1;
- fsk4_states.fm_states = analog_fm_init(fm_states);
- fsk4_states.modinfo = fsk4_info;
- fsk4_states.verbose = 0;
-endfunction
-
-%Integrate over data and dump every M samples
-function d = idmp(data, M)
- d = zeros(1,length(data)/M);
- for i = 1:length(d)
- d(i) = sum(data(1+(i-1)*M:i*M));
- end
-endfunction
-
-
-% DMR modulator ----------------------------------------------------------
-
-function [tx, tx_filt, tx_stream] = fsk4_mod(fsk4_states, tx_bits)
- verbose = fsk4_states.verbose
-
- hbits = tx_bits(1:2:length(tx_bits));
- lbits = tx_bits(2:2:length(tx_bits));
- %Pad odd bit lengths
- if(length(hbits)!=length(lbits))
- lbits = [lbits 0];
- end
- tx_symbols = lbits + hbits*2 + 1;
- M = fsk4_states.M;
- nsym = length(tx_symbols);
- nsam = nsym*M;
-
- tx_stream = zeros(1,nsam);
- for i=1:nsym
- tx_stream(1+(i-1)*M:i*M) = fsk4_states.symmap(tx_symbols(i));
- end
- tx_filt = filter(fsk4_states.tx_filter, 1, tx_stream);
- tx = analog_fm_mod(fsk4_states.fm_states, tx_filt);
-
- if verbose
- figure(10);
- plot(20*log10(abs(fft(tx))))
- title("Spectrum of modulated 4FSK")
- endif
-
-endfunction
-
-
-% Integrate and Dump 4FSK demod ----------------------------------------------------
-
-function bits = fsk4_demod_thing(fsk4_states, rx)
-
- M = fsk4_states.M;
- Fs = fsk4_states.Fs;
- verbose = fsk4_states.verbose;
- t = (0:length(rx)-1);
- symup = fsk4_states.modinfo.syms;
-
- % Integrator is like an FIR filter with rectangular window coeffs.
- % This has some nasty side lobes so lets limit the overall amount
- % of noise getting in. tx filter just happens to work, but I imagine
- % other LPF would as well.
-
- Fs = fsk4_states.Fs;
- rf = (0:(Fs/2));
- rx_filter_a = fir1(100 ,.2);
- rx_filter_b = fsk4_states.rx_filter;
- rx_filter_n = [zeros(1,99) 1];
-
- rx = filter(rx_filter_b, 1, rx);
-
- sym1m = exp(-j*2*pi*(symup(1)/Fs)*t).*rx;
- sym2m = exp(-j*2*pi*(symup(2)/Fs)*t).*rx;
- sym3m = exp(-j*2*pi*(symup(3)/Fs)*t).*rx;
- sym4m = exp(-j*2*pi*(symup(4)/Fs)*t).*rx;
-
- % this puppy found by experiment between 1 and M. Will vary with different
- % filter impulse responses, as delay will vary. f you add M to it coarse
- % timing will adjust by 1.
-
- fine_timing = 54;
-
- sym1m = idmp(sym1m(fine_timing:length(sym1m)),M); sym1m = (real(sym1m).^2+imag(sym1m).^2);
- sym2m = idmp(sym2m(fine_timing:length(sym2m)),M); sym2m = (real(sym2m).^2+imag(sym2m).^2);
- sym3m = idmp(sym3m(fine_timing:length(sym3m)),M); sym3m = (real(sym3m).^2+imag(sym3m).^2);
- sym4m = idmp(sym4m(fine_timing:length(sym4m)),M); sym4m = (real(sym4m).^2+imag(sym4m).^2);
-
-
- figure(2);
- nsym = 500;
- %subplot(411); plot(sym1m(1:nsym))
- %subplot(412); plot(sym2m(1:nsym))
- %subplot(413); plot(sym3m(1:nsym))
- %subplot(414); plot(sym4m(1:nsym))
- plot((1:nsym),sym1m(1:nsym),(1:nsym),sym2m(1:nsym),(1:nsym),sym3m(1:nsym),(1:nsym),sym4m(1:nsym))
-
- [x iv] = max([sym1m; sym2m; sym3m; sym4m;]);
- bits = zeros(1,length(iv*2));
- figure(3);
- hist(iv);
- for i=1:length(iv)
- bits(1+(i-1)*2:i*2) = [[0 0];[0 1];[1 0];[1 1]](iv(i),(1:2));
- end
-endfunction
-
-function dat = bitreps(in,M)
- dat = zeros(1,length(in)*M);
- for i=1:length(in)
- dat(1+(i-1)*M:i*M) = in(i);
- end
-endfunction
-
-% Minimal Running Disparity, 4 symbol encoder
-% This is a simple 1 bit to 1 symbol encoding for 4fsk modems built
-% on old fashoned FM radios.
-function syms = mrd4(bits)
- syms = zeros(1,length(bits));
- rd=0;
- lastsym=0;
- for n = (1:length(bits))
- bit = bits(n);
- sp = [1 3](bit+1); %Map a bit to a +1 or +3
- [x,v] = min(abs([rd+sp rd-sp])); %Select +n or -n, whichever minimizes disparity
- ssel = [sp -sp](v);
- if(ssel == lastsym)ssel = -ssel;endif %never run 2 of the same syms in a row
- syms(n) = ssel; %emit the symbol
- rd = rd + ssel; %update running disparity
- lastsym = ssel; %remember this symbol for next time
- end
-endfunction
-
-% Minimal Running Disparity, 8 symbol encoder
-% This is a simple 2 bit to 1 symbol encoding for 8fsk modems built
-% on old fashoned FM radios.
-function syms = mrd8(bits)
- bitlen = length(bits);
- if mod(bitlen,2) == 1
- bits = [bits 0]
- endif
-
- syms = zeros(1,length(bits)*.5);
- rd=0;
- lastsym=0;
- for n = (1:2:length(bits))
- bit = (bits(n)*2)+bits(n+1);
- sp = [1 3 7 5](bit+1); %Map a bit to a +1 or +3
- [x,v] = min(abs([rd+sp rd-sp])); %Select +n or -n, whichever minimizes disparity
- ssel = [sp -sp](v);
- if(ssel == lastsym)ssel = -ssel;endif %never run 2 of the same syms in a row
- syms((n+1)/2) = ssel; %emit the symbol
- rd = rd + ssel; %update running disparity
- lastsym = ssel; %remember this symbol for next time
- end
-endfunction
-
-% "Manchester 4" encoding
-function syms = mane4(bits)
- syms = zeros(1,floor(bits/2)*2);
- for n = (1:2:length(bits))
- bit0 = bits(n);
- bit1 = bits(n+1);
- sel = 2*bit0+bit1+1;
- syms(n:n+1) = [[3 -3];[-3 3];[1 -1];[-1 1]]( sel,(1:2) );
- end
-endfunction
-
-function out = fold_sum(in,l)
- sublen = floor(length(in)/l);
- out = zeros(1,l);
- for i=(1:sublen)
- v = in(1+(i-1)*l:i*l);
- out = out + v;
- end
-endfunction
-
-function [bits err rxphi] = fsk4_demod_fmrid(fsk4_states, rx, enable_fine_timing = 0)
- %Demodulate fsk signal with an analog fm demod
- rxd = analog_fm_demod(fsk4_states.fm_states,rx);
-
- M = fsk4_states.M;
- verbose = fsk4_states.verbose;
- %This is the ideal fine timing, assuming the same offset in nfbert
- fine_timing = 61;
-
- %This is meant to be adjusted by the fine timing estimator. comment out for
- %ideal timing
- %fine_timing = 59;
-
- %RRC filter to get rid of some of the noise
- rxd = filter(fsk4_states.rx_filter, 1, rxd);
-
- %Try and figure out where sampling should happen over 30 symbol periods
- diffsel = fold_sum(abs(diff( rxd(3001:3001+(M*30)) )),10);
-
- if verbose
- figure(11);
- plot(diffsel);
- title("Fine timing estimation");
- endif
-
- %adjust fine timing
- [v iv] = min(diffsel);
- if enable_fine_timing
- fine_timing = 59 + iv;
- endif
- rxphi = iv;
-
- %sample symbols
- sym = rxd(fine_timing:M:length(rxd));
-
- if verbose
- figure(4)
- plot(sym(1:1000));
- title("Sampled symbols")
- endif
- %eyediagram(afsym,2);
- % Demod symbol map. I should probably figure a better way to do this.
- % After sampling, the furthest symbols tend to be distributed about .80
-
- % A little cheating to demap the symbols
- % Take a histogram of the sampled symbols, find the center of the largest distribution,
- % and correct the symbol map to match it
- [a b] = hist(abs(sym),50);
- [a ii] = max(a);
- %grmax = abs(b(ii));
- %grmax = (grmax<.65)*.65 + (grmax>=.65)*grmax;
- grmax = .84;
- dmsyms = rot90(fsk4_states.symmap*grmax)
- (dmsyms(2)+dmsyms(1))/2
-
- if verbose
- figure(2)
- hist(abs(sym),200);
- title("Sampled symbol histogram")
- endif
-
- %demap the symbols
- [err, symout] = min(abs(sym-dmsyms));
-
- if verbose
- figure(3)
- hist(symout);
- title("De-mapped symbols")
- endif
-
- bits = zeros(1,length(symout)*2);
- %Translate symbols back into bits
-
- for i=1:length(symout)
- bits(1+(i-1)*2:i*2) = [[1 1];[1 0];[0 1];[0 0]](symout(i),(1:2));
- end
-endfunction
-
-% Frequency response of the DMR raised cosine filter
-% from ETSI TS 102 361-1 V2.2.1 page 111
-dmr.tx_filt_resp = @(f) sqrt(1.0*(f<=1920) - cos((pi*f)/1920).*1.0.*(f>1920 & f<=2880));
-dmr.rx_filt_resp = dmr.tx_filt_resp;
-dmr.max_dev = 1944;
-dmr.syms = [-1944 -648 1944 648];
-dmr.rs = 4800;
-dmr.no_filter = 0;
-dmr.demod_fx = @fsk4_demod_fmrid;
-global dmr_info = dmr;
-
-
-% No-filter 4FSK 'ideal' parameters
-nfl.tx_filt_resp = @(f) 1;
-nfl.rx_filt_resp = nfl.tx_filt_resp;
-nfl.max_dev = 7200;
-%nfl.syms = [-3600 -1200 1200 3600];
-nfl.syms = [-7200,-2400,2400,7200];
-nfl.rs = 4800;
-nfl.no_filter = 1;
-nfl.demod_fx = @fsk4_demod_thing;
-global nflt_info = nfl;
-
-%Some parameters for the NXDN filters
-nxdn_al = .2;
-nxdn_T = 416.7e-6;
-nxdn_fl = ((1-nxdn_al)/(2*nxdn_T));
-nxdn_fh = ((1+nxdn_al)/(2*nxdn_T));
-
-%Frequency response of the NXDN filters
-% from NXDN TS 1-A V1.3 page 13
-% Please note : NXDN not fully implemented or tested
-nxdn_H = @(f) 1.0*(f<nxdn_fl) + cos( (nxdn_T/(4*nxdn_al))*(2*pi*f-(pi*(1-nxdn_al)/nxdn_T)) ).*(f<=nxdn_fh & f>nxdn_fl);
-nxdn_P = @(f) (f<=nxdn_fh & f>0).*((sin(pi*f*nxdn_T))./(.00001+(pi*f*nxdn_T))) + 1.0*(f==0);
-nxdn_D = @(f) (f<=nxdn_fh & f>0).*((pi*f*nxdn_T)./(.00001+sin(pi*f*nxdn_T))) + 1.0*(f==0);
-
-nxdn.tx_filt_resp = @(f) nxdn_H(f).*nxdn_P(f);
-nxdn.rx_filt_resp = @(f) nxdn_H(f).*nxdn_D(f);
-nxdn.rs = 4800;
-nxdn.max_dev = 1050;
-nxdn.no_filter = 0;
-nxdn.syms = [-1050,-350,350,1050];
-nxdn.demod_fx = @fsk4_demod_fmrid;
-global nxdn_info = nxdn;
-
-% Bit error rate test ----------------------------------------------------------
-% Params - aEsNodB - EbNo in decibels
-% - timing_offset - how far the fine timing is offset
-% - bitcnt - how many bits to check
-% - demod_fx - demodulator function
-% Returns - ber - the measured BER
-% - thrcoh - theory BER of a coherent demod
-% - thrncoh - theory BER of non-coherent demod
-function [ber thrcoh thrncoh] = nfbert(aEsNodB,modem_config, bitcnt=100000, timing_offset = 10)
-
- rand('state',1);
- randn('state',1);
-
- %How many bits should this test run?
- bitcnt = 120000;
-
- test_bits = [zeros(1,100) rand(1,bitcnt)>.5]; %Random bits. Pad with zeros to prime the filters
- fsk4_states.M = 1;
- fsk4_states = fsk4_init(fsk4_states,modem_config);
-
- %Set this to 0 to cut down on the plotting
- fsk4_states.verbose = 1;
- Fs = fsk4_states.Fs;
- Rb = fsk4_states.Rs * 2; % Multiply symbol rate by 2, since we have 2 bits per symbol
-
- tx = fsk4_mod(fsk4_states,test_bits);
-
- %add noise here
- %shamelessly copied from gmsk.m
- EsNo = 10^(aEsNodB/10);
- EbNo = EsNo
- variance = Fs/(Rb*EbNo);
- nsam = length(tx);
- noise = sqrt(variance/2)*(randn(1,nsam) + j*randn(1,nsam));
- rx = tx*exp(j*pi/2) + noise;
-
- rx = rx(timing_offset:length(rx));
-
- rx_bits = modem_config.demod_fx(fsk4_states,rx);
- ber = 1;
-
- %thing to account for offset from input data to output data
- %No preamble detection yet
- ox = 1;
- for offset = (1:100)
- nerr = sum(xor(rx_bits(offset:length(rx_bits)),test_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;
- printf("\ncoarse timing: %d nerr: %d\n", offset, best_nerr);
-
- % Coherent BER theory
- thrcoh = erfc(sqrt(EbNo));
-
- % non-coherent BER theory calculation
- % It was complicated, so I broke it up
-
- ms = 4;
- ns = (1:ms-1);
- as = (-1).^(ns+1);
- bs = (as./(ns+1));
-
- cs = ((ms-1)./ns);
-
- ds = ns.*log2(ms);
- es = ns+1;
- fs = exp( -(ds./es)*EbNo );
-
- thrncoh = ((ms/2)/(ms-1)) * sum(bs.*((ms-1)./ns).*exp( -(ds./es)*EbNo ));
-
-endfunction
-
-% RX fine timing estimation playground
-function rxphi = fine_ex(timing_offset = 1)
- global dmr_info;
- global nxdn_info;
- global nflt_info;
-
- rand('state',1);
- randn('state',1);
-
- bitcnt = 12051;
- test_bits = [zeros(1,100) rand(1,bitcnt)>.5]; %Random bits. Pad with zeros to prime the filters
- t_vec = [0 0 1 1];
- %test_bits = repmat(t_vec,1,ceil(24000/length(t_vec)));
-
-
- fsk4_states.M = 1;
- fsk4_states = fsk4_init(fsk4_states,dmr_info);
- Fs = fsk4_states.Fs;
- Rb = fsk4_states.Rs * 2; %Multiply symbol rate by 2, since we have 2 bits per symbol
-
- tx = fsk4_mod(fsk4_states,test_bits);
-
- %add noise here
- %shamelessly copied from gmsk.m
- %EsNo = 10^(aEsNodB/10);
- %EbNo = EsNo
- %variance = Fs/(Rb*EbNo);
- %nsam = length(tx);
- %noise = sqrt(variance/2)*(randn(1,nsam) + j*randn(1,nsam));
- %rx = tx*exp(j*pi/2) + noise;
- rx = tx;
- rx = rx(timing_offset:length(rx));
-
- [rx_bits biterr rxphi] = fsk4_demod_fmrid(fsk4_states,rx);
- ber = 1;
-
- %thing to account for offset from input data to output data
- %No preamble detection yet
- ox = 1;
- for offset = (1:100)
- nerr = sum(xor(rx_bits(offset:length(rx_bits)),test_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;
- printf("\ncoarse timing: %d nerr: %d\n", offset, best_nerr);
-
-endfunction
-
-%Run over a wide range of offsets and make sure fine timing makes sense
-function fsk4_rx_phi(socket)
- %pkg load parallel
- offrange = [100:200];
- [a b c phi] = pararrayfun(1.25*nproc(),@nfbert,10*length(offrange),offrange);
-
- close all;
- figure(1);
- clf;
- plot(offrange,phi);
-endfunction
-
-
-% Run this function to compare the theoretical 4FSK modem performance
-% with our DMR modem simulation
-
-function fsk4_ber_curves
- global dmr_info;
- global nxdn_info;
- global nflt_info;
-
- EbNodB = 1:20;
- bers_tco = bers_real = bers_tnco = bers_idealsim = ones(1,length(EbNodB));
-
- %vectors of the same param to pass into pararrayfun
- dmr_infos = repmat(dmr_info,1,length(EbNodB));
- nflt_infos = repmat(nflt_info,1,length(EbNodB));
- thing = @fsk4_demod_thing;
-
- % Lovely innovation by Brady to use all cores and really speed up the simulation
-
- %try
- pkg load parallel
- bers_idealsim = pararrayfun(floor(1.25*nproc()),@nfbert,EbNodB,nflt_infos);
- [bers_real,bers_tco,bers_tnco] = pararrayfun(floor(1.25*nproc()),@nfbert,EbNodB,dmr_infos);
- %catch
- % printf("You should install package parallel. It'll make this run way faster\n");
- % for ii=(1:length(EbNodB));
- %[bers_real(ii),bers,tco(ii),bers_tnco(ii)] = nfbert(EbNodB(ii));
- % end
- %end_try_catch
-
- close all
- figure(1);
- clf;
- semilogy(EbNodB, bers_tnco,'r;4FSK non-coherent theory;')
- hold on;
-
- semilogy(EbNodB, bers_tco,'b;4FSK coherent theory;')
- semilogy(EbNodB, bers_real ,'g;4FSK DMR simulation;')
- semilogy(EbNodB, bers_idealsim, 'v;FSK4 Ideal Non-coherent simulation;')
- hold off;
- grid("minor");
- axis([min(EbNodB) max(EbNodB) 1E-5 1])
- legend("boxoff");
- xlabel("Eb/No (dB)");
- ylabel("Bit Error Rate (BER)")
-
-endfunction
-
-
-
-
-
-
-
-