diff --git a/.bluetooth.m.swo b/.bluetooth.m.swo deleted file mode 100644 index 601f0a9..0000000 Binary files a/.bluetooth.m.swo and /dev/null differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c63cd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +octave-workspace diff --git a/bluetooth.m b/bluetooth.m index ffbf98b..061cad8 100755 --- a/bluetooth.m +++ b/bluetooth.m @@ -269,7 +269,7 @@ function setPDUField_4() tS2.payload = cat(2, tS2.advA, tS2.scanRspData); [tS2.advA_hexStr, tS1.advA_hexStr] = mat2hexStr(tS2.advA); - [tS2.scanRspData_hexStr, tS1.scapRspData_hexStr] = mat2hexStr(tS2.advscanRspData); + [tS2.scanRspData_hexStr, tS1.scapRspData_hexStr] = mat2hexStr(tS2.scanRspData); payload_struct(1) = tS1; payload_struct(2) = tS2 diff --git a/octave-workspace b/octave-workspace deleted file mode 100644 index df4b51f..0000000 Binary files a/octave-workspace and /dev/null differ diff --git a/physicalLayer/channel37_3.dat b/physicalLayer/channel37_3.dat new file mode 100644 index 0000000..b3e40df Binary files /dev/null and b/physicalLayer/channel37_3.dat differ diff --git a/physicalLayer/createPrototype.m b/physicalLayer/createPrototype.m new file mode 100644 index 0000000..ff0c761 --- /dev/null +++ b/physicalLayer/createPrototype.m @@ -0,0 +1,14 @@ + % GFSK Parameters (Volume 6, Part A, Section 3.1 of Core_V4.0.pdf) + samp_rate = 2e6; + symb_rate = 1e6; + samps_per_symb = samp_rate/symb_rate; + time_bw_prod = 0.5; % Can vary between 0.45 and 0.55 depending on device. + symb_span = 5; + freq_sep = 360e3; + gauss_taps = gauss_pulse_create(time_bw_prod,symb_span,samps_per_symb); + + % Defined Advertising Channel Parameters + adv_preamble = [0,1,0,1,0,1,0,1]; % (Volume 6, Part B, Section 2.1.1 of Core_V4.0.pdf) + adv_accessaddress = [0,1,1,0,1,0,1,1,0,1,1,1,1,1,0,1,1,0,0,1,0,0,0,1,0,1,1,1,0,0,0,1]; % (Volume 6, Part B, Section 2.1.1 of Core_V4.0.pdf) + + preamble_proto = conv(fskmod(([adv_preamble,adv_accessaddress]),2,freq_sep,samps_per_symb,samp_rate),gauss_taps); \ No newline at end of file diff --git a/physicalLayer/fskdemod.m b/physicalLayer/fskdemod.m new file mode 100644 index 0000000..bdb0776 --- /dev/null +++ b/physicalLayer/fskdemod.m @@ -0,0 +1,179 @@ +function z = fskdemod(y,M,freq_sep,nSamp,varargin) +%FSKDEMOD Frequency shift keying demodulation +% Z = FSKDEMOD(Y,M,FREQ_SEP,NSAMP) noncoherently demodulates the complex +% envelope Y of a signal using the frequency shift keying method. M is the +% alphabet size and must be an integer power of 2. FREQ_SEP is the frequency +% separation, and must be positive. NSAMP is the required samples per symbol +% and must be an integer greater than 1. For two dimensional signals, the +% function treats each column of data as one channel. +% +% Z = FSKDEMOD(Y,M,FREQ_SEP,NSAMP,Fs) specifies the sampling frequency (Hz). +% The default sampling frequency is 1. +% +% Z = FSKDEMOD(Y,M,FREQ_SEP,NSAMP,Fs,SYMBOL_ORDER) specifies how the +% function assigns binary words to corresponding integers. If SYMBOL_ORDER +% is set to 'bin' (default), then the function uses a natural binary-coded +% ordering. If SYMBOL_ORDER is set to 'gray', then the function uses a +% Gray-coded ordering. +% +% See also FSKMOD, PSKDEMOD, QAMDEMOD, PAMDEMOD, OQPSKDEMOD. + +% Copyright 1996-2012 The MathWorks, Inc. + + +% Error checks ----------------------------------------------------------------- +if (nargin < 4) + error(message('comm:fskdemod:numarg1')); +end + +if (nargin > 6) + error(message('comm:fskdemod:numarg2')); +end + +% Check that M is a positive integer +if (~isreal(M) || ~isscalar(M) || M<2 || (ceil(M)~=M) || ~isnumeric(M)) + error(message('comm:fskdemod:Mreal')); +end + +% Check that M is of the form 2^K +if(~isnumeric(M) || ceil(log2(M)) ~= log2(M)) + error(message('comm:fskdemod:Mpow2')); +end + +% Check that the FREQ_SEP is greater than 0 +if( ~isnumeric(freq_sep) || ~isscalar(freq_sep) || freq_sep<=0 ) + error(message('comm:fskdemod:freqSep')); +end + +% Check that NSAMP is an integer greater than 1 +if((~isnumeric(nSamp) || (ceil(nSamp) ~= nSamp)) || (nSamp <= 1)) + error(message('comm:fskdemod:nSampPos')); +end + +% Check Fs +if (nargin >= 5) +Fs = varargin{1}; + if (isempty(Fs)) + Fs = 1; + elseif (~isreal(Fs) || ~isscalar(Fs) || ~isnumeric(Fs) || Fs<=0 ) + error(message('comm:fskdemod:FsReal')); + end +else + Fs = 1; +end + +% Check that the maximum transmitted frequency does not exceed Fs/2 +maxFreq = ((M-1)/2) * freq_sep; +if (maxFreq > Fs/2) + error(message('comm:fskdemod:maxFreq')); +end + +% Check SYMBOL_ORDER +if(nargin==4 || nargin==5 ) + Symbol_Ordering = 'bin'; %default +else + Symbol_Ordering = varargin{2}; + if (~ischar(Symbol_Ordering)) || (~strcmpi(Symbol_Ordering,'GRAY')) && (~strcmpi(Symbol_Ordering,'BIN')) + error(message('comm:fskdemod:SymbolOrder')); + end +end + +% End of error checks ---------------------------------------------------------- + + +% Assure that Y, if one dimensional, has the correct orientation +wid = size(y,1); +if(wid ==1) + y = y(:); +end +[nRows, nChan] = size(y); + +% Preallocate memory +z = zeros(nRows/nSamp, nChan); + +% Define the frequencies used for the demodulator. +freqs = (-(M-1)/2 : (M-1)/2) * freq_sep; + +% Use the frequencies to generate M complex tones which will be multiplied with +% each received FSK symbol. The tones run down the columns of the "tones" +% matrix. +t = (0 : 1/Fs : nSamp/Fs - 1/Fs)'; +phase = 2*pi*t*freqs; +tones = exp(-1i*phase); + +% For each FSK channel, multiply the complex received signal with the M complex +% tones. Then perform an integrate and dump over each symbol period, find the +% magnitude, and choose the transmitted symbol corresponding to the maximum +% magnitude. +for iChan = 1 : nChan % loop for each FSK channel + + for iSym = 1 : nRows/nSamp + + % Load the samples for the current symbol + yTemp = y( (iSym-1)*nSamp+1 : iSym*nSamp, iChan); + + % Replicate the received FSK signal to multiply with the M tones + yTemp = yTemp(:, ones(M,1)); + + % Multiply against the M tones + yTemp = yTemp .* tones; + + % Perform the integrate and dump, then get the magnitude. Use a + % subfunction for the integrate and dump, to omit the error checking. + yMag = abs(intanddump(yTemp, nSamp)); + + % Choose the maximum and assign an integer value to it. Subtract 1 from the + % output of MAX because the integer outputs are zero-based, not one-based. + [~, maxIdx] = max(yMag, [], 2); + + z(iSym,iChan) = maxIdx - 1; + + end +end + +% Restore the output signal to the original orientation +if(wid == 1) + z = z'; +end + +% Gray decode if necessary +if (strcmpi(Symbol_Ordering,'GRAY')) + [~,gray_map] = gray2bin(z,'fsk',M); % Gray decode + % --- Assure that X, if one dimensional, has the correct orientation --- % + if(size(z,1) == 1) + temp = zeros(size(z)); + temp(:) = gray_map(z+1); + z(:) = temp(:); + else + z = gray_map(z+1); + end +end + +% EOF -- fskdemod.m + + +% ------------------------------------------------------------------------------ +function y = intanddump(x, Nsamp) +%INTANDDUMP Integrate and dump. +% Y = INTANDDUMP(X, NSAMP) integrates the signal X for 1 symbol period, then +% outputs one value into Y. NSAMP is the number of samples per symbol. +% For two-dimensional signals, the function treats each column as 1 +% channel. +% + +% --- Assure that X, if one dimensional, has the correct orientation --- % +wid = size(x,1); +if(wid ==1) + x = x(:); +end + +[xRow, xCol] = size(x); +x = mean(reshape(x, Nsamp, xRow*xCol/Nsamp), 1); +y = reshape(x, xRow/Nsamp, xCol); + +% --- restore the output signal to the original orientation --- % +if(wid == 1) + y = y.'; +end + +% EOF --- intanddump.m diff --git a/physicalLayer/fskmod.m b/physicalLayer/fskmod.m new file mode 100644 index 0000000..979e229 --- /dev/null +++ b/physicalLayer/fskmod.m @@ -0,0 +1,192 @@ +function y = fskmod(x,M,freq_sep,nSamp,varargin) +%FSKMOD Frequency shift keying modulation +% Y = FSKMOD(X,M,FREQ_SEP,NSAMP) outputs the complex envelope of the +% modulation of the message signal X using frequency shift keying modulation. M +% is the alphabet size and must be an integer power of two. The message +% signal must consist of integers between 0 and M-1. FREQ_SEP is the desired +% separation between successive frequencies, in Hz. NSAMP denotes the number +% of samples per symbol and must be an integer greater than 1. For two +% dimensional signals, the function treats each column as one channel. +% +% Y = FSKMOD(X,M,FREQ_SEP,NSAMP,FS) specifies the sampling frequency (Hz). +% The default sampling frequency is 1. +% +% Y = FSKMOD(X,M,FREQ_SEP,NSAMP,FS,PHASE_CONT) specifies the phase continuity +% across FSK symbols. PHASE_CONT can be either 'cont' for continuous phase, +% or 'discont' for discontinuous phase. The default is 'cont'. +% +% Y = FSKMOD(X,M,FREQ_SEP,NSAMP,Fs,PHASE_CONT,SYMBOL_ORDER) specifies how the +% function assigns binary words to corresponding integers. If SYMBOL_ORDER is +% set to 'bin' (default), then the function uses a natural binary-coded +% ordering. If SYMBOL_ORDER is set to 'gray', then the function uses a +% Gray-coded ordering. +% +% See also FSKDEMOD, PSKMOD, QAMMOD, PAMMOD, OQPSKMOD. + +% Copyright 1996-2012 The MathWorks, Inc. + + +% Error checks ----------------------------------------------------------------- +if (nargin < 4) + error(message('comm:fskmod:numarg1')); +end + +if (nargin > 7) + error(message('comm:fskmod:numarg2')); +end + +% Check X +if (~isreal(x) || any(any(ceil(x) ~= x)) || ~isnumeric(x)) + error(message('comm:fskmod:xreal1')); +end + +% Check that M is a positive integer +if (~isreal(M) || ~isscalar(M) || M<2 || (ceil(M)~=M) || ~isnumeric(M)) + error(message('comm:fskmod:Mreal')); +end + +% Check that M is of the form 2^K +if(~isnumeric(M) || ceil(log2(M)) ~= log2(M)) + error(message('comm:fskmod:Mpow2')); +end + +%Check that all X are integers within [0,M-1] +if ((min(min(x)) < 0) || (max(max(x)) > (M-1))) + error(message('comm:fskmod:xreal2')); +end + +% Check that the FREQ_SEP is greater than 0 +if( ~isnumeric(freq_sep) || ~isscalar(freq_sep) || freq_sep<=0 ) + error(message('comm:fskmod:freqSep')); +end + +% Check that NSAMP is an integer greater than 1 +if((~isnumeric(nSamp) || (ceil(nSamp) ~= nSamp)) || (nSamp <= 1)) + error(message('comm:fskmod:nSampPos')); +end + +% Check Fs +if (nargin >= 5) +Fs = varargin{1}; + if (isempty(Fs)) + Fs = 1; + elseif (~isreal(Fs) || ~isscalar(Fs) || ~isnumeric(Fs) || Fs<=0) + error(message('comm:fskmod:FsReal')); + end +else + Fs = 1; +end +samptime = 1/Fs; + +% Check that the maximum transmitted frequency does not exceed Fs/2 +maxFreq = ((M-1)/2) * freq_sep; +if (maxFreq > Fs/2) + error(message('comm:fskmod:maxFreq')); +end + +% Check if the phase is continuous or discontinuous +if (nargin >= 6) + phase_type = varargin{2}; + %check the phase_type string + if ~( strcmpi(phase_type,'cont') || strcmpi(phase_type,'discont') ) + error(message('comm:fskmod:phaseCont')); + end + +else + phase_type = 'cont'; +end + +if (strcmpi(phase_type, 'cont')) + phase_cont = 1; +else + phase_cont = 0; +end + +% Check SYMBOL_ORDER +if(nargin >= 4 && nargin <= 6 ) + Symbol_Ordering = 'bin'; % default +else + Symbol_Ordering = varargin{3}; + if (~ischar(Symbol_Ordering)) || (~strcmpi(Symbol_Ordering,'GRAY')) && (~strcmpi(Symbol_Ordering,'BIN')) + error(message('comm:fskmod:SymbolOrder')); + end +end + +% End of error checks ---------------------------------------------------------- + + +% Assure that X, if one dimensional, has the correct orientation +wid = size(x,1); +if (wid == 1) + x = x(:); +end + +% Gray encode if necessary +if (strcmpi(Symbol_Ordering,'GRAY')) + [~,gray_map] = bin2gray(x,'fsk',M); % Gray encode + [~,index]=ismember(x,gray_map); + x=index-1; +end + +% Obtain the total number of channels +[nRows, nChan] = size(x); + +% Initialize the phase increments and the oscillator phase for modulator with +% discontinuous phase. +phaseIncr = (0:nSamp-1)' * (-(M-1):2:(M-1)) * 2*pi * freq_sep/2 * samptime; +% phIncrSym is the incremental phase over one symbol, across all M tones. +phIncrSym = phaseIncr(end,:); +% phIncrSamp is the incremental phase over one sample, across all M tones. +phIncrSamp = phaseIncr(2,:); % recall that phaseIncr(1,:) = 0 +OscPhase = zeros(nChan, M); + +% phase = nSamp*# of symbols x # of channels +Phase = zeros(nSamp*nRows, nChan); + +% Special case for discontinuous-phase FSK: can use a table look-up for speed +if ( (~phase_cont) && ... + ( floor(nSamp*freq_sep/2 * samptime) == nSamp*freq_sep/2 * samptime ) ) + exp_phaseIncr = exp(1i*phaseIncr); + y = reshape(exp_phaseIncr(:,x+1),nRows*nSamp,nChan); +else + for iChan = 1:nChan + prevPhase = 0; + for iSym = 1:nRows + % Get the initial phase for the current symbol + if (phase_cont) + ph1 = prevPhase; + else + ph1 = OscPhase(iChan, x(iSym,iChan)+1); + end + + % Compute the phase of the current symbol by summing the initial phase + % with the per-symbol phase trajectory associated with the given M-ary + % data element. + Phase(nSamp*(iSym-1)+1:nSamp*iSym,iChan) = ... + ph1*ones(nSamp,1) + phaseIncr(:,x(iSym,iChan)+1); + + % Update the oscillator for a modulator with discontinuous phase. + % Calculate the phase modulo 2*pi so that the phase doesn't grow too + % large. + if (~phase_cont) + OscPhase(iChan,:) = ... + rem(OscPhase(iChan,:) + phIncrSym + phIncrSamp, 2*pi); + end + + % If in continuous mode, the starting phase for the next symbol is the + % ending phase of the current symbol plus the phase increment over one + % sample. + prevPhase = Phase(nSamp*iSym,iChan) + phIncrSamp(x(iSym,iChan)+1); + end + end + y = exp(1i*Phase); +end + +% Restore the output signal to the original orientation +if(wid == 1) + y = y.'; +end + +% EOF --- fskmod.m + + diff --git a/physicalLayer/gauss_pulse_create.m b/physicalLayer/gauss_pulse_create.m new file mode 100644 index 0000000..809294b --- /dev/null +++ b/physicalLayer/gauss_pulse_create.m @@ -0,0 +1,28 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Purpose: Creates the filter taps for the gaussian pulse shaping +% filter. +% +% Inputs: * bt - time bandwidth product of gaussian pulse +% shape +% * symb_span - pulse overlap (integer number of +% symbols) +% * samps_per_symb - number of samples per symbol +% +% Output: * filt_taps - the filter taps for the created gaussian +% pulse shape +% +% Notes: * Error checking not yet implemented. +% +% Author: William C. Headley +% +% Last Updated: 8/18/2016 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function filt_taps = gauss_pulse_create(bt, symb_span, samps_per_symb) + range = samps_per_symb*symb_span/2; + alpha = sqrt(log(2))/sqrt(2) * (1/bt); + + indices = -range:range; + + filt_taps = sqrt(pi)/alpha * exp(-(pi/alpha * indices/samps_per_symb).^2); + filt_taps = filt_taps/sum(filt_taps); +end \ No newline at end of file diff --git a/physicalLayer/read_complex_binary.m b/physicalLayer/read_complex_binary.m new file mode 100644 index 0000000..b5bf3d3 --- /dev/null +++ b/physicalLayer/read_complex_binary.m @@ -0,0 +1,39 @@ +% +% Copyright 2001 Free Software Foundation, Inc. +% +% This file is part of GNU Radio +% +% GNU Radio is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; either version 3, or (at your option) +% any later version. +% +% GNU Radio 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 General Public License +% along with GNU Radio; see the file COPYING. If not, write to +% the Free Software Foundation, Inc., 51 Franklin Street, +% Boston, MA 02110-1301, USA. +% + +function v = read_complex_binary (filename, count) + narginchk (1,2); + + if (nargin < 2) + count = Inf; + end + + f = fopen (filename, 'rb'); + if (f < 0) + v = 0; + else + t = fread (f, [2, count], 'float'); + fclose (f); + v = t(1,:) + t(2,:)*1i; + [r, c] = size (v); + v = reshape (v, c, r); + end +end \ No newline at end of file