display("functions")
source("settings.m");

function files = getDataFiles(clsName, trainsetPerClass)

  global setDataDir;
  
  dir = strcat(setDataDir, clsName, "/");
  lst = readdir(dir);
  files = {};
  cnt = 0;
  for i = 1:numel(lst)
    filename = lst{i};
    if (regexp(filename, ".m$"))         # use only files ending with .m
      file = strcat(dir, filename);
      files = [files file];
      ++cnt;
      if (cnt >= trainsetPerClass)           # use only x input files
        break;
      endif
    endif
  end  
  
end

function samples = getSamplesForClass(clsName, trainsetPerClass, start, percent)
  files = getDataFiles(clsName, trainsetPerClass); #load Data files - thanks franke
  samples = {};
  start = start / 10; #start is given in ms and our raw input data has an fixed update-intervall of 10ms
  for i = 1 : numel(files)
    source(files{i});
    lengthSamples = length(Magnet); # length/number of all available samples
    pEnd = lengthSamples * percent; # how many samples do we want?
    raw = {Gyro(start:pEnd,:), Accel(start:pEnd,:), Magnet(start:pEnd,:)}; # create cell array of the wanted samples
    samples{i}.raw = raw; # write them into cell array for each Class
  end
  disp(strcat("training data for '", clsName, "': ", num2str(length(samples)), " samples"));
end

function data = getRawTrainData()
  
  #global trainsetPerClass;
  global signalStart;
  global signalEnd;

  data = {};
  data{1}.samples = getSamplesForClass("forwardbend", 9, signalStart, signalEnd);
  data{2}.samples = getSamplesForClass("kneebend", 11, signalStart, signalEnd);
  data{3}.samples = getSamplesForClass("pushups", 11, signalStart, signalEnd);
  data{4}.samples = getSamplesForClass("situps", 8, signalStart, signalEnd);
  data{5}.samples = getSamplesForClass("jumpingjack", 13, signalStart, signalEnd);
end

function plotData(data,outPath)
  
  for k = 1:numel(data)
    for j = 1:numel(data{k}.samples)
      for i = 1:numel(data{k}.samples{j}.raw)
        mat = data{k}.samples{j}.raw{i};
        #subplot(6, 3, idx);
        plot(mat);
        legend("x","y","z");
        print( gcf, '-dpng', fullfile(outPath, sprintf('img_%d_%d_%d.png', k, j, i) ) ); 
      end
    end
  end
end

function filteredData = filterData(data)
  pkg load signal;
  filteredData = {};
  for k = 1:numel(data)
    for j = 1:numel(data{k}.samples)
      for i = 1:numel(data{k}.samples{j}.raw)
        mat = data{k}.samples{j}.raw{i};
        [b, a] = butter(16, 0.2);
        filteredData{k}.samples{j}.raw{i} = filtfilt(b, a, mat); #filtfilt extends the signal at beginning so we have no phaseshift
      end
    end
  end        
end

function win = window(rawVec, posMS)
  
  global setWindowSize;
  pos = posMS / 10;
  
  #only full windows are accepted. 
  if length(rawVec) <= pos+(setWindowSize/2)-1
    win = [];
  else  
    win = rawVec(pos-(setWindowSize/2):pos+(setWindowSize/2)-1,:); #100*10 ms is half the window size. 
  endif
end

function out = getMagnitude(data)

  out = [];
  for i = 1:length(data) 
    tmp = sqrt(data(i,1)^2 + data(i,2)^2 + data(i,3)^2);
    out = [out; tmp];
  end

end

function windowedData = windowData(data)

  global setWindowSize;
  global setWindowSliding;
  windowedData = {};
  
  global setMagnet;
  global setAccel;
  global setGyro;
  global useSignalPCA;
  global useSignalMG;

  for k = 1:numel(data)
    for j = 1:numel(data{k}.samples)
    
      #init
      winsAccelX = {};
      winsAccelY = {};
      winsAccelZ = {};
      winsGyroX = {};
      winsGyroY = {};
      winsGyroZ = {};
      winsMagnetX = {};
      winsMagnetY = {};
      winsMagnetZ = {};
      
      winsAccelPCA = {};
      winsGyroPCA = {};
      winsMagnetPCA = {};
      
      winsAccelMG = {};
      winsGyroMG = {};
      winsMagnetMG = {};
      
      winEnd = length(data{k}.samples{j}.raw{1}) * 10; # *10ms 
      winBuffer = ((setWindowSize/2)+1) * 10;
      
      for i = winBuffer:setWindowSliding:winEnd-winBuffer #steps for sliding/overlapping windows 
        
        if setAccel == true
          #accel
          winAccel = window(data{k}.samples{j}.raw{1}, i);
          winsAccelX = [winsAccelX winAccel(:,1)];
          winsAccelY = [winsAccelY winAccel(:,2)];
          winsAccelZ = [winsAccelZ winAccel(:,3)];
          
          if useSignalPCA == true
            [coeff1, score1] = princomp(winAccel');
            winsAccelPCA = [winsAccelPCA score1(:,1)];  #choose the first axis (eigvec with the biggest eigvalue)
          endif
          
          if useSignalMG == true
            winAccelMG = getMagnitude(winAccel);
            winsAccelMG = [winsAccelMG winAccelMG];
          endif
          
        endif
        
        if setGyro == true
          #gyro
          winGyro = window(data{k}.samples{j}.raw{2}, i);
          winsGyroX = [winsGyroX winGyro(:,1)];
          winsGyroY = [winsGyroY winGyro(:,2)];
          winsGyroZ = [winsGyroZ winGyro(:,3)];
          
          if useSignalPCA == true
            [coeff2, score2] = princomp(winGyro');
            winsGyroPCA = [winsGyroPCA score2(:,1)];
          endif
          
          if useSignalMG == true
            winGyroMG = getMagnitude(winGyro);
            winsGyroMG = [winsGyroMG winGyroMG];
          endif
            
        endif
        
        if setMagnet == true  
          #magnet
          winMagnet = window(data{k}.samples{j}.raw{3}, i);
          winsMagnetX = [winsMagnetX winMagnet(:,1)];
          winsMagnetY = [winsMagnetY winMagnet(:,2)];
          winsMagnetZ = [winsMagnetZ winMagnet(:,3)];        
          
          if useSignalPCA == true
            [coeff3, score3] = princomp(winMagnet');
            winsMagnetPCA = [winsMagnetPCA score3(:,1)];
          endif
          
          if useSignalMG == true
            winMagnetMG = getMagnitude(winMagnet);
            winsMagnetMG = [winsMagnetMG winMagnetMG];
          endif
          
        endif
                
      end
       
       #write back data      
      windowedData{k}.samples{j}.raw = [winsAccelX; winsAccelY; winsAccelZ; winsGyroX; winsGyroY; winsGyroZ; winsMagnetX; winsMagnetY; winsMagnetZ; winsAccelPCA; winsGyroPCA; winsMagnetPCA; winsAccelMG; winsGyroMG; winsMagnetMG];
   
    end
  end  
end

function features = featureCalculation(data)

  global setAutocorrelationBinSize;
  global setPSDBinSize;
  features = [];
  
  global setAutoCorr;
  global setRMS;
  global setPSD;
  global setStatistic;
  global setPCA;

  for k = 1:numel(data)
    for j = 1:numel(data{k}.samples)
      for i = 1:size(data{k}.samples{j}.raw, 1)
        for m = 1:size(data{k}.samples{j}.raw, 2)
          currentWindow = data{k}.samples{j}.raw{i, m};
          
          if setAutoCorr == true
            #autocorrelation on window. split into 5 evenly spaced bins (amplitudes are evenly spaced, not number of values ;) ) and calculate mean of bin. 
            [autoCorr] = xcorr(currentWindow);
            [binNum, binCenter] = hist(autoCorr, setAutocorrelationBinSize); #define bins for the data.
            binSize = abs(binCenter(end-1) - binCenter(end));
            binEdges = linspace(binCenter(1)-(binSize/2), binCenter(end)+(binSize/2), setAutocorrelationBinSize+1);
            [binNumc, binIdx] = histc(autoCorr, binEdges);
            binMeans = getBinMean(autoCorr, binIdx, setAutocorrelationBinSize);
            
          endif
          
          if setRMS == true           
            #calculate the root-mean-square (RMS) of the signal
            rms = sqrt(mean(currentWindow.^2));
          endif
            
          if setPSD == true
            #power bands 0.5 to 25hz (useful if the windows are greater then 4s and window sizes to 256, 512..) 
            [powerBand, w] = periodogram(currentWindow); #fills up fft with zeros            
            powerEdges = logspace(log10(0.5), log10(25), setPSDBinSize + 2);  #logarithmic bin spaces for 10 bins
            triFilter = getTriangularFunction(powerEdges, length(powerBand)*2 - 2);    
            for l = 1:numel(triFilter)
              filteredBand = triFilter{l} .* powerBand;
              psd(l) = sum(filteredBand); #sum freq (no log and no dct)
            end
          endif
           
          if setStatistic == true
            #statistical features
            windowMean = mean(currentWindow);
            windowSTD = std(currentWindow); #standard deviation
            windowVariance = var(currentWindow);
            windowKurtosis = kurtosis(currentWindow); #(ger. Wölbung)
            windowIQR = iqr(currentWindow); #interquartile range
          endif
          
          pcaFeature = [];
          if setPCA == true 
            #pca on windows
            [coeff, score] = princomp(currentWindow');
            pcaFeature = currentWindow' * coeff(:, 1:10); #10 pca feature           
          endif
          
          #put everything together
          classLabel = k; #what class?
          sampleLabel = j; #what sample?
          features = [features; sampleLabel, classLabel, binMeans, rms, psd, windowMean, windowSTD, windowVariance, windowKurtosis, windowIQR, pcaFeature];
        end
      end 
    end
  end
end

function value = getBinMean(data, idx, numBins)

  #search for an idx == numBins+1. this index occurs if a value == lastEdge, thanks histc... 
  isSix = find(idx == numBins+1);
  if isSix != 0 
    idx(isSix) = numBins;
  endif

  value = [];
  for i = 1:numBins
    flagBinMembers = (idx == i);
    binMembers = data(flagBinMembers);
    
    if length(binMembers) == 0
      value(i) = 0;
    else
      value(i) = sum(binMembers);
    endif
  end  
end

#triangular functions. (edges of the triangles; num fft values -> nfft.)
function triFilter = getTriangularFunction(edges, nfft)

  global samplerateHZ;

  #get idx of the edges within the samples. thanks to fft each sample represents a frequency. 
  # idx * samplerate / nfft = hertz of that idx
  for i = 1:length(edges)
    edgesByIdx(i) = floor((nfft + 1) * edges(i)/samplerateHZ); 
  end
  
  #generate the triangle filters
  triFilter = {};
  for i = 1:length(edgesByIdx)-2
    diffCnt = edgesByIdx(i+2) - edgesByIdx(i) + 1;
    tmp = zeros(nfft/2 + 1, 1);
    triVec = triang(diffCnt);
    tmp(edgesByIdx(i):edgesByIdx(i+2)) = triVec;
    triFilter{i} = tmp;
  end
end
