From 3694c103774ea36bea6cbded5c0e1fc74ca07fa1 Mon Sep 17 00:00:00 2001 From: Max Ortiz Catalan Date: Thu, 6 Aug 2015 13:53:03 +0200 Subject: [PATCH] Update to BioPatRec TRE --- BioPatRec.m | 64 +- Comm/AFE/Acquire_tWs.m | 68 + Comm/AFE/ConnectDevice.m | 88 ++ Comm/AFE/NI_USB6009/InitNI.m | 216 ++-- Comm/AFE/NI_USB6009/Init_NI_AI.m | 208 ++-- Comm/AFE/NI_USB6009/NI_DataShow.m | 474 +++---- Comm/AFE/SBI/DAQShow_SBI.m | 610 ++++----- Comm/AFE/SBI/InitSBI_NI.m | 246 ++-- Comm/AFE/SBI/TestSBI_NI_USB6009.m | 200 +-- Comm/AFE/SetDeviceStartAcquisition.m | 101 ++ Comm/AFE/StopAcquisition.m | 46 + Comm/ALC/VCP_Piccolo/Connect_ALC.m | 78 +- Comm/ALC/VCP_Piccolo/TestConnectionALC.m | 74 +- Comm/ALC/VCP_Piccolo/TestPWMusingSCIbyCycling.m | 254 ++-- Comm/ALC/VCP_Piccolo/Update2PWMusingSCI.m | 108 +- Comm/ALC/VCP_Piccolo/UpdateAllPWMusingSCI.m | 122 +- Comm/ALC/VCP_Piccolo/UpdatePWMusingSCI_PanTilt.m | 158 +-- {Control => Comm}/ConnectVRE.m | 97 +- {Control => Comm}/DisconnectVRE.m | 95 +- Comm/GetMovementCombination.m | 194 +-- Comm/Keys/GUI_SendKeys.fig | Bin 4782 -> 7063 bytes Comm/Keys/GUI_SendKeys.m | 186 ++- {Control => Comm}/ResetVRE.m | 68 +- Comm/SendKeys.m | 130 +- Comm/VRE/GUI_Test_VRE.fig | Bin 4562 -> 5401 bytes Comm/VRE/GUI_Test_VRE.m | 103 +- Comm/VRE/Ogre.log | 62 +- Comm/limitMovements.def | 12 +- Comm/motors.def | 8 - Comm/movements.def | 25 - Control/ActivateMotors.m | 148 +-- Control/ApplyControl.m | 154 +-- Control/ApplyProportionalControl.m | 156 +-- Control/BayesianFusion.m | 62 + Control/BufferOutput.m | 38 + Control/CalculateDesiredSpeedPercent.m | 176 +-- Control/CombinedControl.m | 13 + Control/GUI_ControlParameters.m | 356 +++--- Control/GUI_ProportionalControl.fig | Bin 0 -> 10596 bytes Control/GUI_ProportionalControl.m | 496 ++++++++ Control/GUI_Sensors.fig | Bin 0 -> 4950 bytes Control/GUI_Sensors.m | 242 ++++ Control/GUI_SingleMotorTest.fig | Bin 0 -> 30671 bytes Control/GUI_SingleMotorTest.m | 1016 +++++++++++++++ Control/InitBayesianFusion.m | 44 + Control/InitCombinedControl.m | 50 + Control/InitControl.m | 74 +- Control/InitControl_new.m | 204 +-- Control/InitMF_Hand_DC_Hardcoded.m | 57 + Control/InitMajorityVoteSimultaneous.m | 32 + {Comm => Control}/InitMotors.m | 76 +- {Comm => Control}/InitMovements.m | 94 +- Control/InitOutBuffer.m | 186 +-- Control/InitPropControl.m | 98 +- Control/InitRamp.m | 80 +- Control/InitRampModified.m | 40 + Control/InitRampModified2.m | 4 + Control/InitRampModified3.m | 3 + Control/InitSensors.m | 46 + Control/MajorityVote.m | 84 +- Control/MajorityVoteSimultaneous.m | 65 + Control/MotorsOff.m | 142 +-- Control/MotorsOn.m | 160 +-- Control/MoveMotor.m | 206 +-- Control/MoveMotorWifi.m | 49 + Control/Ramp.m | 196 +-- Control/RampModified.m | 131 ++ Control/RampModified2.m | 61 + Control/RampModified3.m | 44 + Control/ReInitControl.m | 110 +- Control/ReadSensors.m | 44 + Control/ReadValidControlAlgs.m | 200 +-- Control/SPC/motors.def | 3 + Control/SPC/movements.def | 25 + Control/SendMotorCommand.m | 51 + Control/ShortMotorActivation.m | 186 +-- Control/StopMotor.m | 66 +- Control/TestConnection.m | 17 + Control/VREActivation.m | 118 +- Control/ValidControlAlgs.txt | 116 +- {Comm => Control}/motor.m | 78 +- {Comm => Control}/movement.m | 108 +- Control/sensor.m | 35 + GUI_BioPatRec.fig | Bin 4535 -> 4703 bytes GUI_BioPatRec.m | 27 +- LICENSE.txt | 40 +- PatRec/ANN/ANN_Accuracy.m | 146 +-- PatRec/ANN/ANN_Perceptron.m | 433 +++---- PatRec/ANN/Backp/Backpropagation.m | 182 +-- PatRec/ANN/Eval/EvaluateANN.m | 158 +-- PatRec/ANN/Eval/FastTestANN.m | 128 +- PatRec/ANN/Eval/FitnessANN.m | 100 +- PatRec/ANN/Eval/FullTestANN.m | 130 +- PatRec/ANN/Eval/ValidateANN.m | 148 +-- PatRec/ANN/InitANN_Perceptron.m | 128 +- PatRec/ANN/MLPTest.m | 80 +- PatRec/ANN/PSO/InitPSO_MLP.m | 138 +- PatRec/ANN/PSO/PSO_MLP.m | 186 +-- PatRec/ANN/PSO/Pos2W.m | 98 +- PatRec/Accuracy_patRec.m | 335 +++-- PatRec/ContainsMovementNames.m | 14 +- PatRec/GUI_PatRec.fig | Bin 16064 -> 21703 bytes PatRec/GUI_PatRec.m | 1137 ++++++++++++++--- PatRec/GUI_TacTest.fig | Bin 6080 -> 6049 bytes PatRec/GUI_TestPatRec_Mov2Mov.fig | Bin 50764 -> 54472 bytes PatRec/GUI_TestPatRec_Mov2Mov.m | 610 +++++++-- PatRec/LDA/DiscriminantAccuracy.m | 232 ++-- PatRec/LDA/DiscriminantAnalysis.m | 142 +-- PatRec/LDA/DiscriminantTest.m | 120 +- PatRec/Load_patRec.m | 284 ++--- PatRec/Load_recSession.m | 167 +-- PatRec/Load_sigFeatures.m | 100 +- PatRec/Load_treated_data.m | 106 +- PatRec/MotionTest.m | 938 +++++++------- PatRec/MotionTestResults.m | 236 ++-- PatRec/MotionTest_Legacy.m | 328 ++--- PatRec/Normalization/NormalizeSet.m | 112 +- PatRec/Normalization/NormalizeSets_Mean0Std1.m | 72 +- PatRec/Normalization/NormalizeSets_Mean0Var1.m | 98 +- .../Normalization/NormalizeSets_Midrange0Range2.m | 90 +- PatRec/Normalization/NormalizeSets_TrV.m | 134 +- PatRec/Normalization/NormalizeSets_UnitaryRange.m | 144 +-- PatRec/Normalization/NormalizeSets_normLog.m | 71 +- PatRec/OfflinePatRec.m | 435 ++++--- PatRec/OfflinePatRecTraining.m | 190 ++- PatRec/OneShotPatRec.m | 135 +- PatRec/OneShotPatRecClassifier.m | 132 +- PatRec/OneShotRealtimePatRec.m | 193 +-- PatRec/PCA/ApplyFeatureReduction.m | 36 + PatRec/PCA/FeatureReduction.m | 36 + PatRec/PCA/PCAFeatureReduction.m | 78 ++ PatRec/PCA/PCATest.m | 31 + PatRec/PSO/InitVelocities.m | 78 +- PatRec/PSO/UpdateVel.m | 96 +- PatRec/RFN/Get_connMat_TrV.m | 122 +- PatRec/RFN/Get_connMat_eMean.m | 128 +- PatRec/RFN/PSO/DecodeConnMat.m | 62 +- PatRec/RFN/PSO/InitPSO_RFN.m | 164 +-- PatRec/RFN/PSO/PSO_RFN.m | 204 +-- PatRec/RFN/RFN.m | 142 +-- PatRec/RFN/RegulationFeedback.m | 106 +- PatRec/RFN/RegulationFeedbackAcc.m | 204 +-- PatRec/RFN/RegulationFeedbackFit.m | 132 +- PatRec/RFN/RegulationFeedbackTest.m | 190 +-- PatRec/RFN/RegulationFeedback_thOut.m | 118 +- PatRec/Rand_TrainingData.m | 92 +- PatRec/Rand_sigFeatures.m | 94 +- PatRec/RealtimePatRec.m | 751 +++++------ PatRec/RealtimePatRec_Legacy.m | 336 ++--- PatRec/SOM/Batch Training/BatchNeighborFunction.m | 106 +- PatRec/SOM/Batch Training/BatchTrainig.m | 160 +-- PatRec/SOM/Batch Training/NeuronCoordinates.m | 102 +- PatRec/SOM/Batch Training/NeuronDistances.m | 80 +- PatRec/SOM/EvaluateSOM.m | 257 ++-- PatRec/SOM/FastTestSOM.m | 190 +-- PatRec/SOM/FullTestSOM.m | 210 ++-- PatRec/SOM/GetNeuronLabel.m | 84 +- PatRec/SOM/InitSOM.m | 238 ++-- PatRec/SOM/RandomWeights.m | 64 +- PatRec/SOM/SOMTest.m | 192 +-- PatRec/SOM/SOM_Mapping.m | 154 +-- PatRec/SOM/Stochastic Training/Eta.m | 60 +- PatRec/SOM/Stochastic Training/FindClosest.m | 70 +- PatRec/SOM/Stochastic Training/Sigma.m | 56 +- .../StochasticNeighborFunction.m | 104 +- .../SOM/Stochastic Training/StochasticTraining.m | 118 +- PatRec/SOM/Stochastic Training/UpdateWeights.m | 102 +- PatRec/SOM/VectorDistance.m | 52 +- PatRec/SOM/Visiulaize U-matrix/CreateUDMat.m | 184 +-- PatRec/SOM/Visiulaize U-matrix/GetColor.m | 200 +-- PatRec/SOM/Visiulaize U-matrix/PlotUDMatrix.m | 102 +- PatRec/SOM/Visiulaize U-matrix/ShowUDMatrix.m | 186 +-- PatRec/SOM/Visiulaize U-matrix/SyntaxNeuron.m | 92 +- PatRec/SOM/Visiulaize U-matrix/UDMatCoords.m | 112 +- PatRec/SSOM/EvaluateSSOM.m | 259 ++-- PatRec/SSOM/FastTestSSOM.m | 120 +- PatRec/SSOM/FullTestSSOM.m | 156 +-- PatRec/SSOM/InitSSOM.m | 240 ++-- PatRec/SSOM/SSOM Batch Training/SSOMBatchTrainig.m | 170 +-- .../SSOMStochasticTraining.m | 122 +- PatRec/SSOM/SSOMTest.m | 128 +- PatRec/SSOM/SSOM_Mapping.m | 164 +-- PatRec/SignalProcessing_RealtimePatRec.m | 99 +- PatRec/SoftMax.m | 94 +- PatRec/TacTestResults.m | 311 ++--- PatRec/Topologies/PatRec_AgoAntagonist.m | 176 +-- PatRec/Topologies/PatRec_AgoAntagonistAndMixed.m | 185 +-- PatRec/Topologies/PatRec_AllAndOne.m | 118 +- PatRec/Topologies/PatRec_OneVsAll.m | 96 +- PatRec/Topologies/PatRec_OneVsAllT.m | 48 + PatRec/Topologies/PatRec_OneVsOne.m | 144 +-- PatRec/Topologies/PatRec_OneVsOneDoF.m | 114 ++ PatRec/Topologies/PatRec_SingleClassifier.m | 66 +- PatRec/UseDefaults.m | 168 +++ SigFeatures/ExtractSets_Stack.m | 156 +-- SigFeatures/ExtractSets_Stack_MixRest.m | 230 ++-- SigFeatures/ExtractSets_Stack_MixRestEqual.m | 350 +++--- SigFeatures/ExtractSigFeature.m | 80 +- SigFeatures/ExtractSigFeatureVar.m | 41 + SigFeatures/GetAllSigFeatures.m | 203 +-- SigFeatures/GetFFT.m | 111 +- SigFeatures/GetSetsLables_Stack.m | 114 +- SigFeatures/GetSets_Stack.m | 198 +-- SigFeatures/GetSets_Stack_IndvMov.m | 322 ++--- SigFeatures/GetSets_Stack_MixedOut.m | 360 +++--- SigFeatures/GetSigFeatures.m | 986 +++++++-------- SigFeatures/GetSigFeatures_tcard.m | 39 + SigFeatures/GetSigFeatures_tren.m | 102 +- SigFeatures/LoadFeaturesIDs.m | 90 +- SigFeatures/features.def | 110 +- SigRecordings/Compatibility_recSession.m | 88 +- SigRecordings/DataShow.m | 180 ++- SigRecordings/FastRecordingSession.m | 213 ++++ SigRecordings/GUI_AFEselection.fig | Bin 14102 -> 10786 bytes SigRecordings/GUI_AFEselection.m | 501 +++----- SigRecordings/GUI_RecordingSession.fig | Bin 10102 -> 10634 bytes SigRecordings/GUI_RecordingSession.m | 25 +- SigRecordings/GUI_RecordingSessionShow.m | 15 +- SigRecordings/GUI_Recordings.fig | Bin 64565 -> 28922 bytes SigRecordings/GUI_Recordings.m | 741 ++++++----- SigRecordings/ObtainRampMax.m | 350 ++++++ SigRecordings/ObtainRampMin.m | 265 ++++ SigRecordings/RecordingSession.m | 1255 ++++++++----------- SigRecordings/RecordingSession_Legacy.m | 1312 ++++++++++---------- SigRecordings/RecordingSession_ShowData.m | 177 +++ SigRecordings/Split_recSession_Ch_Independent.m | 104 +- SigTreatment/AddNoise_recSession.m | 86 ++ SigTreatment/AddRestAsMovement.m | 184 +-- SigTreatment/ApplySignalSeparation.m | 36 + SigTreatment/Compatibility_treated_data.m | 110 +- SigTreatment/ComputeSignalSeparation.m | 36 + SigTreatment/Downsample_recSession.m | 41 + SigTreatment/Frequency Filters/ApplyButterFilter.m | 64 +- SigTreatment/Frequency Filters/ApplyFilters.m | 124 +- .../Frequency Filters/BSbutterPLHarmonics.m | 76 +- SigTreatment/Frequency Filters/Filter50hz.m | 76 +- SigTreatment/Frequency Filters/FilterBP.m | 72 +- SigTreatment/Frequency Filters/FilterBP_EMG.m | 76 +- SigTreatment/Frequency Filters/FilterData.m | 84 +- SigTreatment/Frequency Filters/FilterHP80hz.m | 78 +- SigTreatment/GUI_SigTreatment.fig | Bin 15619 -> 18127 bytes SigTreatment/GUI_SigTreatment.m | 135 +- SigTreatment/GetData.m | 232 ++-- SigTreatment/ICA/ICA.m | 141 +++ SigTreatment/ICA/ICAPreprocess.m | 64 + SigTreatment/PlotFFT.m | 67 + SigTreatment/PreProcessing.m | 181 +-- SigTreatment/RemoveTransient_cTp.m | 130 +- SigTreatment/Scale_recSession.m | 66 + SigTreatment/SignalSeparationRealtime.m | 33 + SigTreatment/Spatial Filters/SpatialFilterDDF.m | 104 +- SigTreatment/Spatial Filters/SpatialFilterDDFAbs.m | 120 +- SigTreatment/Spatial Filters/SpatialFilterSDF.m | 96 +- SigTreatment/Spatial Filters/SpatialFilter_1.m | 73 ++ SigTreatment/Split_recSession_Ch.m | 58 +- SigTreatment/Split_recSession_Mov.m | 64 +- SigTreatment/TreatData.m | 158 +-- VRE/CalculatePathLength.m | 160 +-- VRE/CreateMovMatrix.m | 222 ++-- VRE/CurrentPosition.m | 88 ++ VRE/MoveSS.m | 128 +- VRE/TACTest.m | 847 +++++++------ VRE/TrackStateSpace.m | 290 ++--- VRE/UpdateTAC/CalculateTacPath.m | 47 + VRE/UpdateTAC/GetMovementFromName.m | 11 + VRE/UpdateTAC/ShortToName.m | 23 + VRE/bin/Release/ogre.cfg | 26 +- VRE/bin/Release/plugins.cfg | 34 +- VRE/bin/Release/quakemap.cfg | 4 +- VRE/bin/Release/resources.cfg | 32 +- VRE/bin/Release/samples.cfg | 74 +- VRE/bin/media/materials/scripts/Arm.material | 32 +- VRE/bin/media/materials/scripts/tacArm.material | 36 +- 273 files changed, 24586 insertions(+), 17516 deletions(-) create mode 100644 Comm/AFE/Acquire_tWs.m create mode 100644 Comm/AFE/ConnectDevice.m create mode 100644 Comm/AFE/SetDeviceStartAcquisition.m create mode 100644 Comm/AFE/StopAcquisition.m rename {Control => Comm}/ConnectVRE.m (95%) rename {Control => Comm}/DisconnectVRE.m (95%) rename {Control => Comm}/ResetVRE.m (97%) delete mode 100644 Comm/motors.def delete mode 100644 Comm/movements.def create mode 100644 Control/BayesianFusion.m create mode 100644 Control/BufferOutput.m create mode 100644 Control/CombinedControl.m create mode 100644 Control/GUI_ProportionalControl.fig create mode 100644 Control/GUI_ProportionalControl.m create mode 100644 Control/GUI_Sensors.fig create mode 100644 Control/GUI_Sensors.m create mode 100644 Control/GUI_SingleMotorTest.fig create mode 100644 Control/GUI_SingleMotorTest.m create mode 100644 Control/InitBayesianFusion.m create mode 100644 Control/InitCombinedControl.m create mode 100644 Control/InitMF_Hand_DC_Hardcoded.m create mode 100644 Control/InitMajorityVoteSimultaneous.m rename {Comm => Control}/InitMotors.m (97%) rename {Comm => Control}/InitMovements.m (79%) create mode 100644 Control/InitRampModified.m create mode 100644 Control/InitRampModified2.m create mode 100644 Control/InitRampModified3.m create mode 100644 Control/InitSensors.m create mode 100644 Control/MajorityVoteSimultaneous.m create mode 100644 Control/MoveMotorWifi.m create mode 100644 Control/RampModified.m create mode 100644 Control/RampModified2.m create mode 100644 Control/RampModified3.m create mode 100644 Control/ReadSensors.m create mode 100644 Control/SPC/motors.def create mode 100644 Control/SPC/movements.def create mode 100644 Control/SendMotorCommand.m create mode 100644 Control/TestConnection.m rename {Comm => Control}/motor.m (97%) rename {Comm => Control}/movement.m (97%) create mode 100644 Control/sensor.m create mode 100644 PatRec/PCA/ApplyFeatureReduction.m create mode 100644 PatRec/PCA/FeatureReduction.m create mode 100644 PatRec/PCA/PCAFeatureReduction.m create mode 100644 PatRec/PCA/PCATest.m create mode 100644 PatRec/Topologies/PatRec_OneVsAllT.m create mode 100644 PatRec/Topologies/PatRec_OneVsOneDoF.m create mode 100644 PatRec/UseDefaults.m create mode 100644 SigFeatures/ExtractSigFeatureVar.m create mode 100644 SigFeatures/GetSigFeatures_tcard.m create mode 100644 SigRecordings/FastRecordingSession.m create mode 100644 SigRecordings/ObtainRampMax.m create mode 100644 SigRecordings/ObtainRampMin.m create mode 100644 SigRecordings/RecordingSession_ShowData.m create mode 100644 SigTreatment/AddNoise_recSession.m create mode 100644 SigTreatment/ApplySignalSeparation.m create mode 100644 SigTreatment/ComputeSignalSeparation.m create mode 100644 SigTreatment/Downsample_recSession.m create mode 100644 SigTreatment/ICA/ICA.m create mode 100644 SigTreatment/ICA/ICAPreprocess.m create mode 100644 SigTreatment/PlotFFT.m create mode 100644 SigTreatment/Scale_recSession.m create mode 100644 SigTreatment/SignalSeparationRealtime.m create mode 100644 SigTreatment/Spatial Filters/SpatialFilter_1.m create mode 100644 VRE/CurrentPosition.m create mode 100644 VRE/UpdateTAC/CalculateTacPath.m create mode 100644 VRE/UpdateTAC/GetMovementFromName.m create mode 100644 VRE/UpdateTAC/ShortToName.m diff --git a/BioPatRec.m b/BioPatRec.m index a212506..b393c9c 100644 --- a/BioPatRec.m +++ b/BioPatRec.m @@ -1,32 +1,32 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% BioPatRec is a research platform for testing and development of -% algorithms for prosthetic control. This function call the main GUI. -% For more information and documention visit: -% http://code.google.com/p/biopatrec/ -% -% ------------------------- Updates & Contributors ------------------------ -% 2009-04-02 / Max Ortiz / Creation of EMG_AQ -% 2011-22-06 / Max Ortiz / Software name changed from EMG_AQ to BioPatRec - -close all; -clear all; - -%EMG_AQ -GUI_BioPatRec; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% BioPatRec is a research platform for testing and development of +% algorithms for prosthetic control. This function call the main GUI. +% For more information and documention visit: +% http://code.google.com/p/biopatrec/ +% +% ------------------------- Updates & Contributors ------------------------ +% 2009-04-02 / Max Ortiz / Creation of EMG_AQ +% 2011-22-06 / Max Ortiz / Software name changed from EMG_AQ to BioPatRec + +close all; +clear all; + +%EMG_AQ +GUI_BioPatRec; diff --git a/Comm/AFE/Acquire_tWs.m b/Comm/AFE/Acquire_tWs.m new file mode 100644 index 0000000..f706ea5 --- /dev/null +++ b/Comm/AFE/Acquire_tWs.m @@ -0,0 +1,68 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% ------------------- Function Description ------------------ +% Function to Record Exc Sessions +% +% --------------------------Updates-------------------------- +% 2015-1-12 / Enzo Mastinu / Divided the RecordingSession function into + % several functions: ConnectDevice(), + % SetDeviceStartAcquisition(), + % Acquire_tWs(), StopAcquisition(). This functions + % has been moved to COMM/AFE folder, into this new script. + +% 20xx-xx-xx / Author / Comment + + + +% It acquire tWs samples from the selected device +function cData = Acquire_tWs(deviceName, obj, nCh, tWs) + + cData = zeros(tWs,nCh); % this is the data structure that the function must return + ampPP = 0.0005; + offVector = 0:nCh-1; + offVector = offVector .* ampPP; + + + %%%%% ADS1299 %%%%% + if strcmp(deviceName, 'ADS1299') + LSBweight = double(4.5/(24*8388607)); % ADS1299: if gain is set on 24 V/V + for sampleNr = 1:tWs + % 27bytes package mode + byteData = fread(obj,27,'char'); % Acquire 27 bytes packet from Tiva (and from ADS1299), 3 status bytes + 3 byte (24bit) for each channel + value = [65536 256 1]*reshape(byteData(4:end), 3, 8); % all channels data are now available on value vector, byteData(4:end) means throw away status bytes + for k = 1:nCh + if value(k) > 8388607 % the data must be converted from 2's complement + value(k) = value(k) - 2^24; + end + cData(sampleNr,k) = value(k) * LSBweight; + end + end + end + + %%%%% INTAN RHA2216 %%%%% + if strcmp(deviceName, 'RHA2216') + LSBweight = double(2.5/(200*65535)); % Intan differential gain is 200 V/V + for sampleNr = 1:tWs + value16 = fread(obj,nCh,'uint16'); + for k = 1:nCh +% cData(sampleNr,k) = value16(k) - 16384; % Centers data and scales it to fit the graphs + cData(sampleNr,k) = value16(k)*LSBweight; % Convert data into volt + end + end + end + +end diff --git a/Comm/AFE/ConnectDevice.m b/Comm/AFE/ConnectDevice.m new file mode 100644 index 0000000..a5fddc1 --- /dev/null +++ b/Comm/AFE/ConnectDevice.m @@ -0,0 +1,88 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% ------------------- Function Description ------------------ +% Function to Record Exc Sessions +% +% --------------------------Updates-------------------------- +% 2015-1-12 / Enzo Mastinu / Divided the RecordingSession function into + % several functions: ConnectDevice(), + % SetDeviceStartAcquisition(), + % Acquire_tWs(), StopAcquisition(). This functions + % has been moved to COMM/AFE folder, into this new script. + + +% 20xx-xx-xx / Author / Comment + + + +% it creates IP object and sets the buffersize depending on the device that has been chose +function obj = ConnectDevice(handles) + + deviceName = handles.deviceName; + ComPortType = handles.ComPortType; + if strcmp(ComPortType, 'COM') + ComPortName = handles.ComPortName; + end + sF = handles.sF; + sT = handles.sT; + nCh = handles.nCh; + sTall = handles.sTall; + + % Delete previous connection objects + if exist('obj') + fclose(obj); + delete(obj); + end + + %%%%% WiFi %%%%% + if strcmp(ComPortType, 'WiFi') + %%%%% TI ADS1299 %%%%% + if strcmp(deviceName, 'ADS1299') + obj = tcpip('192.168.100.10',65100,'NetworkRole','client'); % WIICOM + obj.InputBufferSize = sTall*sF*27; % 27bytes data package + end + %%%%% INTAN RHA2216 %%%%% + if strcmp(deviceName, 'RHA2216') + obj = tcpip('192.168.100.10',65100,'NetworkRole','client'); % WIICOM + obj.InputBufferSize = sT*sF*nCh*2; + end + end + + %%%%% COM %%%%% + if strcmp(ComPortType, 'COM') + %%%%% TI ADS1299 %%%%% + if strcmp(deviceName, 'ADS1299') + obj = serial (ComPortName, 'baudrate', 2500000, 'databits', 8, 'byteorder', 'bigEndian'); + obj.InputBufferSize = sTall*sF*27; % 27bytes data package + end + %%%%% INTAN RHA2216 %%%%% + if strcmp(deviceName, 'RHA2216') + obj = serial (ComPortName, 'baudrate', 1250000, 'databits', 8, 'byteorder', 'bigEndian'); + obj.InputBufferSize = sT*sF*nCh*2; + end + end + + % Open the connection + fopen(obj); + + % Read available data and discard it + if obj.BytesAvailable > 1 + fread(obj,obj.BytesAvailable,'uint8'); + end +% disp(obj); + +end diff --git a/Comm/AFE/NI_USB6009/InitNI.m b/Comm/AFE/NI_USB6009/InitNI.m index 5ae28d6..857a0cb 100644 --- a/Comm/AFE/NI_USB6009/InitNI.m +++ b/Comm/AFE/NI_USB6009/InitNI.m @@ -1,108 +1,108 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to Initialize the NI-6009 -% ch Channels, binary string -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-07-27 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - - -function [ai, ao, dio] = InitNI(sF, sT, chAI) - -% Close possible daq objects running -if (~isempty(daqfind)) - stop(daqfind) -end - -% Use this command to determine Board IDs in system, if needed -hw = daqhwinfo('nidaq'); -hw.InstalledBoardIds; -hw.BoardNames - -%% Analog Inputs -% Create an analog input object using Board ID. -ai = analoginput('nidaq','Dev1'); - -% Set the sample rate and samples per trigger -ai.SampleRate = sF; -ai.SamplesPerTrigger = sF*sT; - -% Set Inputy Type and Ranges -%set(ai,'InputType','Differential'); -set(ai,'InputType','SingleEnde'); - -% Add chanels -if chAI(1) - addchannel(ai, 0,'ch0'); - set(ai.ch0,'InputRange',[-10 10]); -end -if chAI(2) - addchannel(ai, 1,'ch1'); - set(ai.ch1,'InputRange',[-10 10]); -end -if chAI(3) - addchannel(ai, 2,'ch2'); - set(ai.ch2,'InputRange',[-10 10]); -end -if chAI(4) - addchannel(ai, 3,'ch3'); - set(ai.ch3,'InputRange',[-10 10]); -end -if chAI(5) - addchannel(ai, 4,'ch4'); - set(ai.ch4,'InputRange',[-10 10]); -end -if chAI(6) - addchannel(ai, 5,'ch5'); - set(ai.ch5,'InputRange',[-10 10]); -end -if chAI(7) - addchannel(ai, 6,'ch6'); - set(ai.ch6,'InputRange',[-10 10]); -end -if chAI(8) - addchannel(ai, 7,'ch7'); - set(ai.ch7,'InputRange',[-10 10]); -end - -disp(ai); -% %% Analog Outputs -% % Create an analog input object using Board ID "Dev1". -% ao = analogoutput('nidaq','Dev1'); -% -% % Add channels -% addchannel(ao, 0, 'ch0'); -% -% % Security -% putsample(ao,0); % Zero exit -% -% %% Digital Input Outputs -% %Create an analog input object using Board ID "Dev1". -% dio = digitalio('nidaq', 'Dev1'); -% -% % Add channels -% addline(dio, 0:3, 1, 'Out'); % Port 1 as Output -% addline(dio, 0:7, 0, 'In'); % Port 0 as Inputs -% -% % Security -% putvalue(dio.Line([1 2]), [1 1]); % Stop motor - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to Initialize the NI-6009 +% ch Channels, binary string +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-27 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + + +function [ai, ao, dio] = InitNI(sF, sT, chAI) + +% Close possible daq objects running +if (~isempty(daqfind)) + stop(daqfind) +end + +% Use this command to determine Board IDs in system, if needed +hw = daqhwinfo('nidaq'); +hw.InstalledBoardIds; +hw.BoardNames + +%% Analog Inputs +% Create an analog input object using Board ID. +ai = analoginput('nidaq','Dev1'); + +% Set the sample rate and samples per trigger +ai.SampleRate = sF; +ai.SamplesPerTrigger = sF*sT; + +% Set Inputy Type and Ranges +%set(ai,'InputType','Differential'); +set(ai,'InputType','SingleEnde'); + +% Add chanels +if chAI(1) + addchannel(ai, 0,'ch0'); + set(ai.ch0,'InputRange',[-10 10]); +end +if chAI(2) + addchannel(ai, 1,'ch1'); + set(ai.ch1,'InputRange',[-10 10]); +end +if chAI(3) + addchannel(ai, 2,'ch2'); + set(ai.ch2,'InputRange',[-10 10]); +end +if chAI(4) + addchannel(ai, 3,'ch3'); + set(ai.ch3,'InputRange',[-10 10]); +end +if chAI(5) + addchannel(ai, 4,'ch4'); + set(ai.ch4,'InputRange',[-10 10]); +end +if chAI(6) + addchannel(ai, 5,'ch5'); + set(ai.ch5,'InputRange',[-10 10]); +end +if chAI(7) + addchannel(ai, 6,'ch6'); + set(ai.ch6,'InputRange',[-10 10]); +end +if chAI(8) + addchannel(ai, 7,'ch7'); + set(ai.ch7,'InputRange',[-10 10]); +end + +disp(ai); +% %% Analog Outputs +% % Create an analog input object using Board ID "Dev1". +% ao = analogoutput('nidaq','Dev1'); +% +% % Add channels +% addchannel(ao, 0, 'ch0'); +% +% % Security +% putsample(ao,0); % Zero exit +% +% %% Digital Input Outputs +% %Create an analog input object using Board ID "Dev1". +% dio = digitalio('nidaq', 'Dev1'); +% +% % Add channels +% addline(dio, 0:3, 1, 'Out'); % Port 1 as Output +% addline(dio, 0:7, 0, 'In'); % Port 0 as Inputs +% +% % Security +% putvalue(dio.Line([1 2]), [1 1]); % Stop motor + + diff --git a/Comm/AFE/NI_USB6009/Init_NI_AI.m b/Comm/AFE/NI_USB6009/Init_NI_AI.m index d9da6ee..07e4678 100644 --- a/Comm/AFE/NI_USB6009/Init_NI_AI.m +++ b/Comm/AFE/NI_USB6009/Init_NI_AI.m @@ -1,104 +1,104 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to Initialize the NI-6009 Analog Inputs -% Input = Handles -% Output = ai object -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-03-31 / Max Ortiz / Creation -% 2011-06-19 / Max Ortiz /Modified for general numer of channels -% 20xx-xx-xx / Author / Comment on update - - -function [ai,chp] = Init_NI_AI(handles,sF,sT,nCh) - -% Close possible daq objects running -if (~isempty(daqfind)) - stop(daqfind) -end - -% Use this command to determine Board IDs in system, if needed -hw = daqhwinfo('nidaq'); -hw.InstalledBoardIds; -hw.BoardNames - -% Create an analog input object using Board ID "Dev6". -ai = analoginput('nidaq','Dev1'); - -% Set the sample rate and samples per trigger -ai.SampleRate = sF; -ai.SamplesPerTrigger = sF*sT; - -% Set Inputy Type and Ranges -%set(ai,'InputType','Differential'); -set(ai,'InputType','SingleEnde'); - -% Add all chanels -if get(handles.cb_ch0,'Value') == 1 && nCh > 0 - addchannel(ai, 0,'ch0'); - set(ai.ch0,'InputRange',[-10 10]); - chp(1)=1; - nCh = nCh - 1; -end -if get(handles.cb_ch1,'Value') == 1 && nCh > 0 - addchannel(ai, 1,'ch1'); - set(ai.ch1,'InputRange',[-10 10]); - chp(2)=1; - nCh = nCh - 1; -end -if get(handles.cb_ch2,'Value') == 1 && nCh > 0 - addchannel(ai, 2,'ch2'); - set(ai.ch2,'InputRange',[-10 10]); - chp(3)=1; - nCh = nCh - 1; -end -if get(handles.cb_ch3,'Value') == 1 && nCh > 0 - addchannel(ai, 3,'ch3'); - set(ai.ch3,'InputRange',[-10 10]); - chp(4)=1; - nCh = nCh - 1; -end -if get(handles.cb_ch4,'Value') == 1 && nCh > 0 - addchannel(ai, 4,'ch4'); - set(ai.ch4,'InputRange',[-10 10]); - chp(5)=1; - nCh = nCh - 1; -end -if get(handles.cb_ch5,'Value') == 1 && nCh > 0 - addchannel(ai, 5,'ch5'); - set(ai.ch5,'InputRange',[-10 10]); - chp(6)=1; - nCh = nCh - 1; -end -if get(handles.cb_ch6,'Value') == 1 && nCh > 0 - addchannel(ai, 6,'ch6'); - set(ai.ch6,'InputRange',[-10 10]); - chp(7)=1; - nCh = nCh - 1; -end -if get(handles.cb_ch7,'Value') == 1 && nCh > 0 - addchannel(ai, 7,'ch7'); - set(ai.ch7,'InputRange',[-10 10]); - chp(8)=1; -end - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to Initialize the NI-6009 Analog Inputs +% Input = Handles +% Output = ai object +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-03-31 / Max Ortiz / Creation +% 2011-06-19 / Max Ortiz /Modified for general numer of channels +% 20xx-xx-xx / Author / Comment on update + + +function [ai,chp] = Init_NI_AI(handles,sF,sT,nCh) + +% Close possible daq objects running +if (~isempty(daqfind)) + stop(daqfind) +end + +% Use this command to determine Board IDs in system, if needed +hw = daqhwinfo('nidaq'); +hw.InstalledBoardIds; +hw.BoardNames + +% Create an analog input object using Board ID "Dev6". +ai = analoginput('nidaq','Dev1'); + +% Set the sample rate and samples per trigger +ai.SampleRate = sF; +ai.SamplesPerTrigger = sF*sT; + +% Set Inputy Type and Ranges +%set(ai,'InputType','Differential'); +set(ai,'InputType','SingleEnde'); + +% Add all chanels +if get(handles.cb_ch0,'Value') == 1 && nCh > 0 + addchannel(ai, 0,'ch0'); + set(ai.ch0,'InputRange',[-10 10]); + chp(1)=1; + nCh = nCh - 1; +end +if get(handles.cb_ch1,'Value') == 1 && nCh > 0 + addchannel(ai, 1,'ch1'); + set(ai.ch1,'InputRange',[-10 10]); + chp(2)=1; + nCh = nCh - 1; +end +if get(handles.cb_ch2,'Value') == 1 && nCh > 0 + addchannel(ai, 2,'ch2'); + set(ai.ch2,'InputRange',[-10 10]); + chp(3)=1; + nCh = nCh - 1; +end +if get(handles.cb_ch3,'Value') == 1 && nCh > 0 + addchannel(ai, 3,'ch3'); + set(ai.ch3,'InputRange',[-10 10]); + chp(4)=1; + nCh = nCh - 1; +end +if get(handles.cb_ch4,'Value') == 1 && nCh > 0 + addchannel(ai, 4,'ch4'); + set(ai.ch4,'InputRange',[-10 10]); + chp(5)=1; + nCh = nCh - 1; +end +if get(handles.cb_ch5,'Value') == 1 && nCh > 0 + addchannel(ai, 5,'ch5'); + set(ai.ch5,'InputRange',[-10 10]); + chp(6)=1; + nCh = nCh - 1; +end +if get(handles.cb_ch6,'Value') == 1 && nCh > 0 + addchannel(ai, 6,'ch6'); + set(ai.ch6,'InputRange',[-10 10]); + chp(7)=1; + nCh = nCh - 1; +end +if get(handles.cb_ch7,'Value') == 1 && nCh > 0 + addchannel(ai, 7,'ch7'); + set(ai.ch7,'InputRange',[-10 10]); + chp(8)=1; +end + + + + diff --git a/Comm/AFE/NI_USB6009/NI_DataShow.m b/Comm/AFE/NI_USB6009/NI_DataShow.m index 35c1d2e..226d815 100644 --- a/Comm/AFE/NI_USB6009/NI_DataShow.m +++ b/Comm/AFE/NI_USB6009/NI_DataShow.m @@ -1,237 +1,237 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to Show data on the GUI -% Input = ai object, sCh channels pressences -% Output = data and time -% Max J. Ortiz C. -% 09-04-15 -% hGUI_Rec = handles from the GUI_Recordings -% ai = analog input "object" -% sCh = Selected channels, binary string to indicate which channels have been selected -% sF = Sample frequency -% sT = Samople time -% pT = peek time -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - - -function cdata = NI_DataShow(handles, ai, sCh, sF, sT, pT) - -% Setting for data peeking -tt = 0:1/sF:pT-1/sF; % Create vector of time -data = zeros(length(tt),8); % Current data - -% Create handles for the plots -% this is faster than creating the plot everytime -if sCh(1) - axes(handles.a_t0); - p_t0 = plot(tt,data(:,1)); - axes(handles.a_f0); - p_f0 = plot(1,1); -end -if sCh(2) - axes(handles.a_t1); - p_t1 = plot(tt,data(:,2)); - axes(handles.a_f1); - p_f1 = plot(1,1); -end -if sCh(3) - axes(handles.a_t2); - p_t2 = plot(tt,data(:,3)); - axes(handles.a_f2); - p_f2 = plot(1,1); -end - -if sCh(4) - axes(handles.a_t3); - p_t3 = plot(tt,data(:,4)); - axes(handles.a_f3); - p_f3 = plot(1,1); -end - -if length(sCh) > 4 % Conditional added to keep compatibility with previous versions - if sCh(5) - axes(handles.a_t4); - p_t4 = plot(tt,data(:,5)); - %axes(handles.a_f4); - %p_f4 = plot(1,1); - end - - if sCh(6) - axes(handles.a_t5); - p_t5 = plot(tt,data(:,6)); - %axes(handles.a_f5); - %p_f5 = plot(1,1); - end - - if sCh(7) - axes(handles.a_t6); - p_t6 = plot(tt,data(:,7)); - %axes(handles.a_f6); - %p_f6 = plot(1,1); - end - - if sCh(8) - axes(handles.a_t7); - p_t7 = plot(tt,data(:,8)); - %axes(handles.a_f7); - %p_f7 = plot(1,1); - end -end - -%% Start DAQ -start(ai); -%ao = init_ao(); test of ao, hardware connections must be done - -% Wait until the first samples are aquired -while ai.SamplesAcquired < sF*pT -end -% Peek Data -while ai.SamplesAcquired < sT*sF - - %putsample(ao,5); test of ao - - % Peek data - set(handles.t_msg,'String',['Peek at: ' num2str(ai.SamplesAcquired)]) % Show message about acquisition - data = peekdata(ai,pT*sF); - data = FilterData(data, handles, sF); %Filter the data - % Fast Fourier Transform - aNs = length(data(:,1)); - NFFT = 2^nextpow2(aNs); % Next power of 2 from number of samples - f = sF/2*linspace(0,1,NFFT/2); - dataf = fft(data(1:aNs,:),NFFT)/aNs; - m = 2*abs(dataf((1:NFFT/2),:)); - - chi = 1; %Channel Index for map data - if sCh(1) - set(p_t0,'YData',data(:,chi)); - set(p_f0,'XData',f); - set(p_f0,'YData',m(:,chi)); - chi=chi+1; - end - if sCh(2) - set(p_t1,'YData',data(:,chi)); - set(p_f1,'XData',f); - set(p_f1,'YData',m(:,chi)); - chi=chi+1; - end - if sCh(3) - set(p_t2,'YData',data(:,chi)); - set(p_f2,'XData',f); - set(p_f2,'YData',m(:,chi)); - chi=chi+1; - end - if sCh(4) - set(p_t3,'YData',data(:,chi)); - set(p_f3,'XData',f); - set(p_f3,'YData',m(:,chi)); - chi=chi+1; - end - if length(sCh) > 4 - if sCh(5) - set(p_t4,'YData',data(:,chi)); - chi=chi+1; - %set(p_f4,'XData',f); - %set(p_f4,'YData',m(:,chi)); - end - if sCh(6) - set(p_t5,'YData',data(:,chi)); - chi=chi+1; - %set(p_f5,'XData',f); - %set(p_f5,'YData',m(:,chi)); - end - if sCh(7) - set(p_t6,'YData',data(:,chi)); - chi=chi+1; - %set(p_f6,'XData',f); - %set(p_f6,'YData',m(:,chi)); - end - if sCh(8) - set(p_t7,'YData',data(:,chi)); - %set(p_f7,'XData',f); - %set(p_f7,'YData',m(:,chi)); - end - end - - drawnow -end - -wait(ai,sT+1); %Wait until the daq is over or until sT+1 is reached -[data,time,abstime] = getdata(ai); -abstime = fix(abstime); -set(handles.t_msg,'String',['DAQ Done ' num2str(abstime(4)) ':' num2str(abstime(5))]) % Show message about acquisition - - -% Save acquired data into cdata -% Settings for total data -tt = time; % Create vector of time -chi=1; -if sCh(1) == 1 - cdata(:,1) = data(:,chi); - set(p_t0,'XData',tt); - set(p_t0,'YData',cdata(:,1)); - chi=chi+1; -end -if sCh(2) == 1 - cdata(:,2) = data(:,chi); - set(p_t1,'XData',tt); - set(p_t1,'YData',cdata(:,2)); - chi=chi+1; -end -if sCh(3) == 1 - cdata(:,3) = data(:,chi); - set(p_t2,'XData',tt); - set(p_t2,'YData',cdata(:,3)); - chi=chi+1; -end -if sCh(4) == 1 - cdata(:,4) = data(:,chi); - set(p_t3,'XData',tt); - set(p_t3,'YData',cdata(:,4)); - chi=chi+1; -end -if length(sCh) > 4 - if sCh(5) == 1 - cdata(:,5) = data(:,chi); - set(p_t4,'XData',tt); - set(p_t4,'YData',cdata(:,5)); - chi=chi+1; - end - if sCh(6) == 1 - cdata(:,6) = data(:,chi); - set(p_t5,'XData',tt); - set(p_t5,'YData',cdata(:,6)); - chi=chi+1; - end - if sCh(7) == 1 - cdata(:,7) = data(:,chi); - set(p_t6,'XData',tt); - set(p_t6,'YData',cdata(:,7)); - chi=chi+1; - end - if sCh(8) == 1 - cdata(:,8) = data(:,chi); - set(p_t7,'XData',tt); - set(p_t7,'YData',cdata(:,8)); - end -end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to Show data on the GUI +% Input = ai object, sCh channels pressences +% Output = data and time +% Max J. Ortiz C. +% 09-04-15 +% hGUI_Rec = handles from the GUI_Recordings +% ai = analog input "object" +% sCh = Selected channels, binary string to indicate which channels have been selected +% sF = Sample frequency +% sT = Samople time +% pT = peek time +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + + +function cdata = NI_DataShow(handles, ai, sCh, sF, sT, pT) + +% Setting for data peeking +tt = 0:1/sF:pT-1/sF; % Create vector of time +data = zeros(length(tt),8); % Current data + +% Create handles for the plots +% this is faster than creating the plot everytime +if sCh(1) + axes(handles.a_t0); + p_t0 = plot(tt,data(:,1)); + axes(handles.a_f0); + p_f0 = plot(1,1); +end +if sCh(2) + axes(handles.a_t1); + p_t1 = plot(tt,data(:,2)); + axes(handles.a_f1); + p_f1 = plot(1,1); +end +if sCh(3) + axes(handles.a_t2); + p_t2 = plot(tt,data(:,3)); + axes(handles.a_f2); + p_f2 = plot(1,1); +end + +if sCh(4) + axes(handles.a_t3); + p_t3 = plot(tt,data(:,4)); + axes(handles.a_f3); + p_f3 = plot(1,1); +end + +if length(sCh) > 4 % Conditional added to keep compatibility with previous versions + if sCh(5) + axes(handles.a_t4); + p_t4 = plot(tt,data(:,5)); + %axes(handles.a_f4); + %p_f4 = plot(1,1); + end + + if sCh(6) + axes(handles.a_t5); + p_t5 = plot(tt,data(:,6)); + %axes(handles.a_f5); + %p_f5 = plot(1,1); + end + + if sCh(7) + axes(handles.a_t6); + p_t6 = plot(tt,data(:,7)); + %axes(handles.a_f6); + %p_f6 = plot(1,1); + end + + if sCh(8) + axes(handles.a_t7); + p_t7 = plot(tt,data(:,8)); + %axes(handles.a_f7); + %p_f7 = plot(1,1); + end +end + +%% Start DAQ +start(ai); +%ao = init_ao(); test of ao, hardware connections must be done + +% Wait until the first samples are aquired +while ai.SamplesAcquired < sF*pT +end +% Peek Data +while ai.SamplesAcquired < sT*sF + + %putsample(ao,5); test of ao + + % Peek data + set(handles.t_msg,'String',['Peek at: ' num2str(ai.SamplesAcquired)]) % Show message about acquisition + data = peekdata(ai,pT*sF); + data = FilterData(data, handles, sF); %Filter the data + % Fast Fourier Transform + aNs = length(data(:,1)); + NFFT = 2^nextpow2(aNs); % Next power of 2 from number of samples + f = sF/2*linspace(0,1,NFFT/2); + dataf = fft(data(1:aNs,:),NFFT)/aNs; + m = 2*abs(dataf((1:NFFT/2),:)); + + chi = 1; %Channel Index for map data + if sCh(1) + set(p_t0,'YData',data(:,chi)); + set(p_f0,'XData',f); + set(p_f0,'YData',m(:,chi)); + chi=chi+1; + end + if sCh(2) + set(p_t1,'YData',data(:,chi)); + set(p_f1,'XData',f); + set(p_f1,'YData',m(:,chi)); + chi=chi+1; + end + if sCh(3) + set(p_t2,'YData',data(:,chi)); + set(p_f2,'XData',f); + set(p_f2,'YData',m(:,chi)); + chi=chi+1; + end + if sCh(4) + set(p_t3,'YData',data(:,chi)); + set(p_f3,'XData',f); + set(p_f3,'YData',m(:,chi)); + chi=chi+1; + end + if length(sCh) > 4 + if sCh(5) + set(p_t4,'YData',data(:,chi)); + chi=chi+1; + %set(p_f4,'XData',f); + %set(p_f4,'YData',m(:,chi)); + end + if sCh(6) + set(p_t5,'YData',data(:,chi)); + chi=chi+1; + %set(p_f5,'XData',f); + %set(p_f5,'YData',m(:,chi)); + end + if sCh(7) + set(p_t6,'YData',data(:,chi)); + chi=chi+1; + %set(p_f6,'XData',f); + %set(p_f6,'YData',m(:,chi)); + end + if sCh(8) + set(p_t7,'YData',data(:,chi)); + %set(p_f7,'XData',f); + %set(p_f7,'YData',m(:,chi)); + end + end + + drawnow +end + +wait(ai,sT+1); %Wait until the daq is over or until sT+1 is reached +[data,time,abstime] = getdata(ai); +abstime = fix(abstime); +set(handles.t_msg,'String',['DAQ Done ' num2str(abstime(4)) ':' num2str(abstime(5))]) % Show message about acquisition + + +% Save acquired data into cdata +% Settings for total data +tt = time; % Create vector of time +chi=1; +if sCh(1) == 1 + cdata(:,1) = data(:,chi); + set(p_t0,'XData',tt); + set(p_t0,'YData',cdata(:,1)); + chi=chi+1; +end +if sCh(2) == 1 + cdata(:,2) = data(:,chi); + set(p_t1,'XData',tt); + set(p_t1,'YData',cdata(:,2)); + chi=chi+1; +end +if sCh(3) == 1 + cdata(:,3) = data(:,chi); + set(p_t2,'XData',tt); + set(p_t2,'YData',cdata(:,3)); + chi=chi+1; +end +if sCh(4) == 1 + cdata(:,4) = data(:,chi); + set(p_t3,'XData',tt); + set(p_t3,'YData',cdata(:,4)); + chi=chi+1; +end +if length(sCh) > 4 + if sCh(5) == 1 + cdata(:,5) = data(:,chi); + set(p_t4,'XData',tt); + set(p_t4,'YData',cdata(:,5)); + chi=chi+1; + end + if sCh(6) == 1 + cdata(:,6) = data(:,chi); + set(p_t5,'XData',tt); + set(p_t5,'YData',cdata(:,6)); + chi=chi+1; + end + if sCh(7) == 1 + cdata(:,7) = data(:,chi); + set(p_t6,'XData',tt); + set(p_t6,'YData',cdata(:,7)); + chi=chi+1; + end + if sCh(8) == 1 + cdata(:,8) = data(:,chi); + set(p_t7,'XData',tt); + set(p_t7,'YData',cdata(:,8)); + end +end + diff --git a/Comm/AFE/SBI/DAQShow_SBI.m b/Comm/AFE/SBI/DAQShow_SBI.m index 0db911f..6491647 100644 --- a/Comm/AFE/SBI/DAQShow_SBI.m +++ b/Comm/AFE/SBI/DAQShow_SBI.m @@ -1,306 +1,306 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Funtion to show the aquired data in the GUI (based in NI_DataShow) -% -% --------------------------Updates-------------------------- -% [Contributors are welcome to add their email] -% 2012-02-09 / Max Ortiz / Creation, moved from Legacy to SBI -% 2012-03-27 / Max Ortiz / Bug fixed when an arbitrary selection of channels -% 20xx-xx-xx / Author / Comment on update - -function cdata = DAQShow_SBI(handlesX, sCh, sF, sT, pT) - - global handles; - global allData; - global timeStamps; - - % Variable to be sent globally - allData = []; - timeStamps = []; - handles = handlesX; - handles.sCh = sCh; - handles.sF = sF; - - - % Setting for data peeking - tt = 0:1/sF:pT-1/sF; % Create vector of time - data = zeros(length(tt),8); % Current data - - % Create handles for the plots - % this is faster than creating the plot everytime - if sCh(1) - axes(handles.a_t0); - p_t0 = plot(tt,data(:,1)); - handles.p_t0 = p_t0; - axes(handles.a_f0); - p_f0 = plot(1,1); - handles.p_f0 = p_f0; - end - if sCh(2) - axes(handles.a_t1); - p_t1 = plot(tt,data(:,2)); - handles.p_t1 = p_t1; - axes(handles.a_f1); - p_f1 = plot(1,1); - handles.p_f1 = p_f1; - end - if sCh(3) - axes(handles.a_t2); - p_t2 = plot(tt,data(:,3)); - handles.p_t2 = p_t2; - axes(handles.a_f2); - p_f2 = plot(1,1); - handles.p_f2 = p_f2; - end - - if sCh(4) - axes(handles.a_t3); - p_t3 = plot(tt,data(:,4)); - handles.p_t3 = p_t3; - axes(handles.a_f3); - p_f3 = plot(1,1); - handles.p_f3 = p_f3; - - end - - if length(sCh) > 4 % Conditional added to keep compatibility with previous versions - if sCh(5) - axes(handles.a_t4); - p_t4 = plot(tt,data(:,5)); - handles.p_t4 = p_t4; - %axes(handles.a_f4); - %p_f4 = plot(1,1); - end - - if sCh(6) - axes(handles.a_t5); - p_t5 = plot(tt,data(:,6)); - handles.p_t5 = p_t5; - %axes(handles.a_f5); - %p_f5 = plot(1,1); - end - - if sCh(7) - axes(handles.a_t6); - p_t6 = plot(tt,data(:,7)); - handles.p_t6 = p_t6; - %axes(handles.a_f6); - %p_f6 = plot(1,1); - end - - if sCh(8) - axes(handles.a_t7); - p_t7 = plot(tt,data(:,8)); - handles.p_t7 = p_t7; - %axes(handles.a_f7); - %p_f7 = plot(1,1); - end - end - - % Send variables; - %handles.p_f4 = handles.p_f4; - %handles.p_f5 = handles.p_f5; - %handles.p_f6 = handles.p_f6; - %handles.p_f7 = handles.p_f7; - - - - %% Init DAQ - s = InitSBI_NI(sF,sT,sCh); - % Change the interruption time - s.NotifyWhenDataAvailableExceeds = sF*pT; - lh = s.addlistener('DataAvailable', @DataShow_SBI_OneShot); - - %% Run in the backgroud - s.startBackground(); - - % Wait until it has finished done - %s.IsDone % will report 0 - s.wait(); % rather than while - %s.IsDone % will report 1 - - %% Finish session - set(handles.t_msg,'String','Done') % Show message about acquisition - data = allData; - - % Fast Fourier Transform - aNs = length(data(:,1)); - NFFT = 2^nextpow2(aNs); % Next power of 2 from number of samples - f = sF/2*linspace(0,1,NFFT/2); - dataf = fft(data(1:aNs,:),NFFT)/aNs; - m = 2*abs(dataf((1:NFFT/2),:)); - - - % Save acquired data into cdata - % Settings for all data - tt = timeStamps; % Create vector of time - chIdx=1; % Channel Index in the Data matrix - if sCh(1) == 1 - cdata(:,1) = data(:,chIdx); - set(p_t0,'XData',tt); - set(p_t0,'YData',cdata(:,1)); - set(p_f0,'XData',f); - set(p_f0,'YData',m(:,chIdx)); - chIdx=chIdx+1; - end - if sCh(2) == 1 - cdata(:,2) = data(:,chIdx); - set(p_t1,'XData',tt); - set(p_t1,'YData',cdata(:,2)); - set(p_f1,'XData',f); - set(p_f1,'YData',m(:,chIdx)); - chIdx=chIdx+1; - end - if sCh(3) == 1 - cdata(:,3) = data(:,chIdx); - set(p_t2,'XData',tt); - set(p_t2,'YData',cdata(:,3)); - set(p_f2,'XData',f); - set(p_f2,'YData',m(:,chIdx)); - chIdx=chIdx+1; - end - if sCh(4) == 1 - cdata(:,4) = data(:,chIdx); - set(p_t3,'XData',tt); - set(p_t3,'YData',cdata(:,4)); - set(p_f3,'XData',f); - set(p_f3,'YData',m(:,chIdx)); - chIdx=chIdx+1; - end - if length(sCh) > 4 - if sCh(5) == 1 - cdata(:,5) = data(:,chIdx); - set(p_t4,'XData',tt); - set(p_t4,'YData',cdata(:,5)); - chIdx=chIdx+1; - end - if sCh(6) == 1 - cdata(:,6) = data(:,chIdx); - set(p_t5,'XData',tt); - set(p_t5,'YData',cdata(:,6)); - chIdx=chIdx+1; - end - if sCh(7) == 1 - cdata(:,7) = data(:,chIdx); - set(p_t6,'XData',tt); - set(p_t6,'YData',cdata(:,7)); - chIdx=chIdx+1; - end - if sCh(8) == 1 - cdata(:,8) = data(:,chIdx); - set(p_t7,'XData',tt); - set(p_t7,'YData',cdata(:,8)); - end - end - -%Delete listener SBI -delete (lh) - -end - -function DataShow_SBI_OneShot(src,event) - - global handles; - global allData; - global timeStamps; - - % Get info from hendles - sF = handles.sF; - sCh = handles.sCh; - - % Get data - tempData = event.Data; - allData = [allData; tempData]; - timeStamps = [timeStamps; event.TimeStamps]; - - % filter the data - tempData = FilterData(tempData, handles, sF); %Filter the data - % Fast Fourier Transform - aNs = length(tempData(:,1)); - NFFT = 2^nextpow2(aNs); % Next power of 2 from number of samples - f = sF/2*linspace(0,1,NFFT/2); - dataf = fft(tempData(1:aNs,:),NFFT)/aNs; - m = 2*abs(dataf((1:NFFT/2),:)); - - chIdx = 1; %Channel Index for map data - if sCh(1) - p_t0 = handles.p_t0; - p_f0 = handles.p_f0; - set(p_t0,'YData',tempData(:,chIdx)); - set(p_f0,'XData',f); - set(p_f0,'YData',m(:,chIdx)); - chIdx=chIdx+1; - end - if sCh(2) - p_t1 = handles.p_t1; - p_f1 = handles.p_f1; - set(p_t1,'YData',tempData(:,chIdx)); - set(p_f1,'XData',f); - set(p_f1,'YData',m(:,chIdx)); - chIdx=chIdx+1; - end - if sCh(3) - p_t2 = handles.p_t2; - p_f2 = handles.p_f2; - set(p_t2,'YData',tempData(:,chIdx)); - set(p_f2,'XData',f); - set(p_f2,'YData',m(:,chIdx)); - chIdx=chIdx+1; - end - if sCh(4) - p_t3 = handles.p_t3; - p_f3 = handles.p_f3; - set(p_t3,'YData',tempData(:,chIdx)); - set(p_f3,'XData',f); - set(p_f3,'YData',m(:,chIdx)); - chIdx=chIdx+1; - end - if length(sCh) > 4 - if sCh(5) - p_t4 = handles.p_t4; - set(p_t4,'YData',tempData(:,chIdx)); - chIdx=chIdx+1; - %set(p_f4,'XData',f); - %set(p_f4,'YData',m(:,chIdx)); - end - if sCh(6) - p_t5 = handles.p_t5; - set(p_t5,'YData',tempData(:,chIdx)); - chIdx=chIdx+1; - %set(p_f5,'XData',f); - %set(p_f5,'YData',m(:,chIdx)); - end - if sCh(7) - p_t6 = handles.p_t6; - set(p_t6,'YData',tempData(:,chIdx)); - chIdx=chIdx+1; - %set(p_f6,'XData',f); - %set(p_f6,'YData',m(:,chIdx)); - end - if sCh(8) - p_t7 = handles.p_t7; - set(p_t7,'YData',tempData(:,chIdx)); - %set(p_f7,'XData',f); - %set(p_f7,'YData',m(:,chIdx)); - end - end - - drawnow; - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Funtion to show the aquired data in the GUI (based in NI_DataShow) +% +% --------------------------Updates-------------------------- +% [Contributors are welcome to add their email] +% 2012-02-09 / Max Ortiz / Creation, moved from Legacy to SBI +% 2012-03-27 / Max Ortiz / Bug fixed when an arbitrary selection of channels +% 20xx-xx-xx / Author / Comment on update + +function cdata = DAQShow_SBI(handlesX, sCh, sF, sT, pT) + + global handles; + global allData; + global timeStamps; + + % Variable to be sent globally + allData = []; + timeStamps = []; + handles = handlesX; + handles.sCh = sCh; + handles.sF = sF; + + + % Setting for data peeking + tt = 0:1/sF:pT-1/sF; % Create vector of time + data = zeros(length(tt),8); % Current data + + % Create handles for the plots + % this is faster than creating the plot everytime + if sCh(1) + axes(handles.a_t0); + p_t0 = plot(tt,data(:,1)); + handles.p_t0 = p_t0; + axes(handles.a_f0); + p_f0 = plot(1,1); + handles.p_f0 = p_f0; + end + if sCh(2) + axes(handles.a_t1); + p_t1 = plot(tt,data(:,2)); + handles.p_t1 = p_t1; + axes(handles.a_f1); + p_f1 = plot(1,1); + handles.p_f1 = p_f1; + end + if sCh(3) + axes(handles.a_t2); + p_t2 = plot(tt,data(:,3)); + handles.p_t2 = p_t2; + axes(handles.a_f2); + p_f2 = plot(1,1); + handles.p_f2 = p_f2; + end + + if sCh(4) + axes(handles.a_t3); + p_t3 = plot(tt,data(:,4)); + handles.p_t3 = p_t3; + axes(handles.a_f3); + p_f3 = plot(1,1); + handles.p_f3 = p_f3; + + end + + if length(sCh) > 4 % Conditional added to keep compatibility with previous versions + if sCh(5) + axes(handles.a_t4); + p_t4 = plot(tt,data(:,5)); + handles.p_t4 = p_t4; + %axes(handles.a_f4); + %p_f4 = plot(1,1); + end + + if sCh(6) + axes(handles.a_t5); + p_t5 = plot(tt,data(:,6)); + handles.p_t5 = p_t5; + %axes(handles.a_f5); + %p_f5 = plot(1,1); + end + + if sCh(7) + axes(handles.a_t6); + p_t6 = plot(tt,data(:,7)); + handles.p_t6 = p_t6; + %axes(handles.a_f6); + %p_f6 = plot(1,1); + end + + if sCh(8) + axes(handles.a_t7); + p_t7 = plot(tt,data(:,8)); + handles.p_t7 = p_t7; + %axes(handles.a_f7); + %p_f7 = plot(1,1); + end + end + + % Send variables; + %handles.p_f4 = handles.p_f4; + %handles.p_f5 = handles.p_f5; + %handles.p_f6 = handles.p_f6; + %handles.p_f7 = handles.p_f7; + + + + %% Init DAQ + s = InitSBI_NI(sF,sT,sCh); + % Change the interruption time + s.NotifyWhenDataAvailableExceeds = sF*pT; + lh = s.addlistener('DataAvailable', @DataShow_SBI_OneShot); + + %% Run in the backgroud + s.startBackground(); + + % Wait until it has finished done + %s.IsDone % will report 0 + s.wait(); % rather than while + %s.IsDone % will report 1 + + %% Finish session + set(handles.t_msg,'String','Done') % Show message about acquisition + data = allData; + + % Fast Fourier Transform + aNs = length(data(:,1)); + NFFT = 2^nextpow2(aNs); % Next power of 2 from number of samples + f = sF/2*linspace(0,1,NFFT/2); + dataf = fft(data(1:aNs,:),NFFT)/aNs; + m = 2*abs(dataf((1:NFFT/2),:)); + + + % Save acquired data into cdata + % Settings for all data + tt = timeStamps; % Create vector of time + chIdx=1; % Channel Index in the Data matrix + if sCh(1) == 1 + cdata(:,1) = data(:,chIdx); + set(p_t0,'XData',tt); + set(p_t0,'YData',cdata(:,1)); + set(p_f0,'XData',f); + set(p_f0,'YData',m(:,chIdx)); + chIdx=chIdx+1; + end + if sCh(2) == 1 + cdata(:,2) = data(:,chIdx); + set(p_t1,'XData',tt); + set(p_t1,'YData',cdata(:,2)); + set(p_f1,'XData',f); + set(p_f1,'YData',m(:,chIdx)); + chIdx=chIdx+1; + end + if sCh(3) == 1 + cdata(:,3) = data(:,chIdx); + set(p_t2,'XData',tt); + set(p_t2,'YData',cdata(:,3)); + set(p_f2,'XData',f); + set(p_f2,'YData',m(:,chIdx)); + chIdx=chIdx+1; + end + if sCh(4) == 1 + cdata(:,4) = data(:,chIdx); + set(p_t3,'XData',tt); + set(p_t3,'YData',cdata(:,4)); + set(p_f3,'XData',f); + set(p_f3,'YData',m(:,chIdx)); + chIdx=chIdx+1; + end + if length(sCh) > 4 + if sCh(5) == 1 + cdata(:,5) = data(:,chIdx); + set(p_t4,'XData',tt); + set(p_t4,'YData',cdata(:,5)); + chIdx=chIdx+1; + end + if sCh(6) == 1 + cdata(:,6) = data(:,chIdx); + set(p_t5,'XData',tt); + set(p_t5,'YData',cdata(:,6)); + chIdx=chIdx+1; + end + if sCh(7) == 1 + cdata(:,7) = data(:,chIdx); + set(p_t6,'XData',tt); + set(p_t6,'YData',cdata(:,7)); + chIdx=chIdx+1; + end + if sCh(8) == 1 + cdata(:,8) = data(:,chIdx); + set(p_t7,'XData',tt); + set(p_t7,'YData',cdata(:,8)); + end + end + +%Delete listener SBI +delete (lh) + +end + +function DataShow_SBI_OneShot(src,event) + + global handles; + global allData; + global timeStamps; + + % Get info from hendles + sF = handles.sF; + sCh = handles.sCh; + + % Get data + tempData = event.Data; + allData = [allData; tempData]; + timeStamps = [timeStamps; event.TimeStamps]; + + % filter the data + tempData = FilterData(tempData, handles, sF); %Filter the data + % Fast Fourier Transform + aNs = length(tempData(:,1)); + NFFT = 2^nextpow2(aNs); % Next power of 2 from number of samples + f = sF/2*linspace(0,1,NFFT/2); + dataf = fft(tempData(1:aNs,:),NFFT)/aNs; + m = 2*abs(dataf((1:NFFT/2),:)); + + chIdx = 1; %Channel Index for map data + if sCh(1) + p_t0 = handles.p_t0; + p_f0 = handles.p_f0; + set(p_t0,'YData',tempData(:,chIdx)); + set(p_f0,'XData',f); + set(p_f0,'YData',m(:,chIdx)); + chIdx=chIdx+1; + end + if sCh(2) + p_t1 = handles.p_t1; + p_f1 = handles.p_f1; + set(p_t1,'YData',tempData(:,chIdx)); + set(p_f1,'XData',f); + set(p_f1,'YData',m(:,chIdx)); + chIdx=chIdx+1; + end + if sCh(3) + p_t2 = handles.p_t2; + p_f2 = handles.p_f2; + set(p_t2,'YData',tempData(:,chIdx)); + set(p_f2,'XData',f); + set(p_f2,'YData',m(:,chIdx)); + chIdx=chIdx+1; + end + if sCh(4) + p_t3 = handles.p_t3; + p_f3 = handles.p_f3; + set(p_t3,'YData',tempData(:,chIdx)); + set(p_f3,'XData',f); + set(p_f3,'YData',m(:,chIdx)); + chIdx=chIdx+1; + end + if length(sCh) > 4 + if sCh(5) + p_t4 = handles.p_t4; + set(p_t4,'YData',tempData(:,chIdx)); + chIdx=chIdx+1; + %set(p_f4,'XData',f); + %set(p_f4,'YData',m(:,chIdx)); + end + if sCh(6) + p_t5 = handles.p_t5; + set(p_t5,'YData',tempData(:,chIdx)); + chIdx=chIdx+1; + %set(p_f5,'XData',f); + %set(p_f5,'YData',m(:,chIdx)); + end + if sCh(7) + p_t6 = handles.p_t6; + set(p_t6,'YData',tempData(:,chIdx)); + chIdx=chIdx+1; + %set(p_f6,'XData',f); + %set(p_f6,'YData',m(:,chIdx)); + end + if sCh(8) + p_t7 = handles.p_t7; + set(p_t7,'YData',tempData(:,chIdx)); + %set(p_f7,'XData',f); + %set(p_f7,'YData',m(:,chIdx)); + end + end + + drawnow; + end \ No newline at end of file diff --git a/Comm/AFE/SBI/InitSBI_NI.m b/Comm/AFE/SBI/InitSBI_NI.m index efa23b7..aa68605 100644 --- a/Comm/AFE/SBI/InitSBI_NI.m +++ b/Comm/AFE/SBI/InitSBI_NI.m @@ -1,123 +1,123 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% ------------------- Function Description ------------------ -% Initialization of the Session-Based Inferface -% This routine was created from the Init_NIx routines used in previous -% versions of Matlab 2011. The daq toolbox in the 64 releases required a -% completly different initialization (the session-based interface). -% -% --------------------------Updates-------------------------- -% [Contributors are welcome to add their email] -% 2012-01-24 / Max Ortiz / Creation -% 2012-03-27 / Max Ortiz / Bug fixed when an arbitrary selection of channels -% not the most elegant solution but it does the -% job -% 2012-05-29 / Max Ortiz / Removed the routine for sequentially adding -% channels to a loop. -% 20xx-xx-xx / Author / Comment on update - -function [s] = InitSBI_NI(sF, sT, chAI, chAO) - -% Auxiliar variables -nChAI = size(chAI,2); -chAIidx = find(chAI); - -% Close possible daq objects running -if (~isempty(daqfind)) - stop(daqfind) -end - -% Find devices -dev = daq.getDevices; - -% Create a session -s = daq.createSession('ni'); - - -% Add channels in a loop - -for i = 1 : size(chAIidx,2) - chID = ['ai' num2str(chAIidx(i)-1)]; - s.addAnalogInputChannel(dev.ID,chID,'Voltage'); - s.Channels(i).InputType ='SingleEnded'; - s.Channels(i).Range = [-5 5]; -end - -% Add channels -% if nChAI >= 1; -% if chAI(1) -% s.addAnalogInputChannel(dev.ID,'ai0','Voltage'); -% s.Channels(1).InputType ='SingleEnded'; -% s.Channels(1).Range = [-5 5]; -% end -% end -% if nChAI >= 2; -% if chAI(2) -% s.addAnalogInputChannel(dev.ID,'ai1','Voltage'); -% s.Channels(2).InputType ='SingleEnded'; -% s.Channels(2).Range = [-5 5]; -% end -% end -% if nChAI >= 3; -% if chAI(3) -% s.addAnalogInputChannel(dev.ID,'ai2','Voltage'); -% s.Channels(3).InputType ='SingleEnded'; -% s.Channels(3).Range = [-5 5]; -% end -% end -% if nChAI >= 4; -% if chAI(4) -% s.addAnalogInputChannel(dev.ID,'ai3','Voltage'); -% s.Channels(4).InputType ='SingleEnded'; -% s.Channels(4).Range = [-5 5]; -% end -% end -% if nChAI >= 5; -% if chAI(5) -% s.addAnalogInputChannel(dev.ID,'ai4','Voltage'); -% s.Channels(5).InputType ='SingleEnded'; -% s.Channels(5).Range = [-5 5]; -% end -% end -% if nChAI >= 6; -% if chAI(6) -% s.addAnalogInputChannel(dev.ID,'ai5','Voltage'); -% s.Channels(6).InputType ='SingleEnded'; -% s.Channels(6).Range = [-5 5]; -% end -% end -% if nChAI >= 7; -% if chAI(7) -% s.addAnalogInputChannel(dev.ID,'ai6','Voltage'); -% s.Channels(7).InputType ='SingleEnded'; -% s.Channels(7).Range = [-5 5]; -% end -% end -% -% if nChAI >= 8; -% if chAI(8) -% s.addAnalogInputChannel(dev.ID,'ai7','Voltage'); -% s.Channels(8).InputType ='SingleEnded'; -% s.Channels(8).Range = [-5 5]; -% end -% end -% Modify duration and frequency - -s.DurationInSeconds = sT; -s.Rate = sF; - -disp(s); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% ------------------- Function Description ------------------ +% Initialization of the Session-Based Inferface +% This routine was created from the Init_NIx routines used in previous +% versions of Matlab 2011. The daq toolbox in the 64 releases required a +% completly different initialization (the session-based interface). +% +% --------------------------Updates-------------------------- +% [Contributors are welcome to add their email] +% 2012-01-24 / Max Ortiz / Creation +% 2012-03-27 / Max Ortiz / Bug fixed when an arbitrary selection of channels +% not the most elegant solution but it does the +% job +% 2012-05-29 / Max Ortiz / Removed the routine for sequentially adding +% channels to a loop. +% 20xx-xx-xx / Author / Comment on update + +function [s] = InitSBI_NI(sF, sT, chAI, chAO) + +% Auxiliar variables +nChAI = size(chAI,2); +chAIidx = find(chAI); + +% Close possible daq objects running +if (~isempty(daqfind)) + stop(daqfind) +end + +% Find devices +dev = daq.getDevices; + +% Create a session +s = daq.createSession('ni'); + + +% Add channels in a loop + +for i = 1 : size(chAIidx,2) + chID = ['ai' num2str(chAIidx(i)-1)]; + s.addAnalogInputChannel(dev.ID,chID,'Voltage'); + s.Channels(i).InputType ='SingleEnded'; + s.Channels(i).Range = [-5 5]; +end + +% Add channels +% if nChAI >= 1; +% if chAI(1) +% s.addAnalogInputChannel(dev.ID,'ai0','Voltage'); +% s.Channels(1).InputType ='SingleEnded'; +% s.Channels(1).Range = [-5 5]; +% end +% end +% if nChAI >= 2; +% if chAI(2) +% s.addAnalogInputChannel(dev.ID,'ai1','Voltage'); +% s.Channels(2).InputType ='SingleEnded'; +% s.Channels(2).Range = [-5 5]; +% end +% end +% if nChAI >= 3; +% if chAI(3) +% s.addAnalogInputChannel(dev.ID,'ai2','Voltage'); +% s.Channels(3).InputType ='SingleEnded'; +% s.Channels(3).Range = [-5 5]; +% end +% end +% if nChAI >= 4; +% if chAI(4) +% s.addAnalogInputChannel(dev.ID,'ai3','Voltage'); +% s.Channels(4).InputType ='SingleEnded'; +% s.Channels(4).Range = [-5 5]; +% end +% end +% if nChAI >= 5; +% if chAI(5) +% s.addAnalogInputChannel(dev.ID,'ai4','Voltage'); +% s.Channels(5).InputType ='SingleEnded'; +% s.Channels(5).Range = [-5 5]; +% end +% end +% if nChAI >= 6; +% if chAI(6) +% s.addAnalogInputChannel(dev.ID,'ai5','Voltage'); +% s.Channels(6).InputType ='SingleEnded'; +% s.Channels(6).Range = [-5 5]; +% end +% end +% if nChAI >= 7; +% if chAI(7) +% s.addAnalogInputChannel(dev.ID,'ai6','Voltage'); +% s.Channels(7).InputType ='SingleEnded'; +% s.Channels(7).Range = [-5 5]; +% end +% end +% +% if nChAI >= 8; +% if chAI(8) +% s.addAnalogInputChannel(dev.ID,'ai7','Voltage'); +% s.Channels(8).InputType ='SingleEnded'; +% s.Channels(8).Range = [-5 5]; +% end +% end +% Modify duration and frequency + +s.DurationInSeconds = sT; +s.Rate = sF; + +disp(s); diff --git a/Comm/AFE/SBI/TestSBI_NI_USB6009.m b/Comm/AFE/SBI/TestSBI_NI_USB6009.m index 00d93bf..bc595fe 100644 --- a/Comm/AFE/SBI/TestSBI_NI_USB6009.m +++ b/Comm/AFE/SBI/TestSBI_NI_USB6009.m @@ -1,100 +1,100 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Test funtions for data aquisition using Session-base Interfaces -% -% --------------------------Updates-------------------------- -% [Contributors are welcome to add their email] -% 2012-01-31 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment - -function TestSBI_NI_USB6009 - - %TestSimpleAIForeground(); - %TestSimpleAIBackground(); - TestGlobalAIBackground(); - -end - -function TestSimpleAIForeground - - s = daq.createSession('ni') - s.addAnalogInputChannel('dev1','ai0','Voltage') - data = s.startForeground() - plot (data) - -end - -function TestSimpleAIBackground - - s = daq.createSession('ni') - s.addAnalogInputChannel('dev1','ai0','Voltage') - lh = s.addlistener('DataAvailable', @plotData); - s.startBackground(); - s.wait; - delete (lh) - -end - -function TestGlobalAIBackground - %clear global data; - clear all; - %clear functions - - global data - - s = daq.createSession('ni'); - s.addAnalogInputChannel('dev1',0,'voltage'); - s.Rate = 1000; - s.DurationInSeconds = 1; - %Verify that the notification is done automatically - %s.IsNotifyWhenDataAvailableExceedsAuto = 1; - % Change the peek time - s.NotifyWhenDataAvailableExceeds = 100; - lh = s.addlistener('DataAvailable',@plotGloalData); - - s.startBackground(); - % Wait - s.wait; - %close(gcf); - figure(); - plot(data); % plot global data - delete (lh) -end - - -function plotGloalData(src,event) - persistent tempData; - persistent i; - global data - if(isempty(tempData)) - tempData = []; - i = 1; - end - disp(i); - %plot(event.TimeStamps, event.Data); - plot(event.Data); - tempData = [tempData;event.Data]; - data = tempData; - i=i+1; -end - - -function plotData(src,event) - plot(event.TimeStamps, event.Data) - end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Test funtions for data aquisition using Session-base Interfaces +% +% --------------------------Updates-------------------------- +% [Contributors are welcome to add their email] +% 2012-01-31 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment + +function TestSBI_NI_USB6009 + + %TestSimpleAIForeground(); + %TestSimpleAIBackground(); + TestGlobalAIBackground(); + +end + +function TestSimpleAIForeground + + s = daq.createSession('ni') + s.addAnalogInputChannel('dev1','ai0','Voltage') + data = s.startForeground() + plot (data) + +end + +function TestSimpleAIBackground + + s = daq.createSession('ni') + s.addAnalogInputChannel('dev1','ai0','Voltage') + lh = s.addlistener('DataAvailable', @plotData); + s.startBackground(); + s.wait; + delete (lh) + +end + +function TestGlobalAIBackground + %clear global data; + clear all; + %clear functions + + global data + + s = daq.createSession('ni'); + s.addAnalogInputChannel('dev1',0,'voltage'); + s.Rate = 1000; + s.DurationInSeconds = 1; + %Verify that the notification is done automatically + %s.IsNotifyWhenDataAvailableExceedsAuto = 1; + % Change the peek time + s.NotifyWhenDataAvailableExceeds = 100; + lh = s.addlistener('DataAvailable',@plotGloalData); + + s.startBackground(); + % Wait + s.wait; + %close(gcf); + figure(); + plot(data); % plot global data + delete (lh) +end + + +function plotGloalData(src,event) + persistent tempData; + persistent i; + global data + if(isempty(tempData)) + tempData = []; + i = 1; + end + disp(i); + %plot(event.TimeStamps, event.Data); + plot(event.Data); + tempData = [tempData;event.Data]; + data = tempData; + i=i+1; +end + + +function plotData(src,event) + plot(event.TimeStamps, event.Data) + end diff --git a/Comm/AFE/SetDeviceStartAcquisition.m b/Comm/AFE/SetDeviceStartAcquisition.m new file mode 100644 index 0000000..3d550b4 --- /dev/null +++ b/Comm/AFE/SetDeviceStartAcquisition.m @@ -0,0 +1,101 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% ------------------- Function Description ------------------ +% Function to Record Exc Sessions +% +% --------------------------Updates-------------------------- +% 2015-1-12 / Enzo Mastinu / Divided the RecordingSession function into + % several functions: ConnectDevice(), + % SetDeviceStartAcquisition(), + % Acquire_tWs(), StopAcquisition(). This functions + % has been moved to COMM/AFE folder, into this new script. + +% 20xx-xx-xx / Author / Comment + + + +% it sets the chosen device and sends start acquisition command +function SetDeviceStartAcquisition(handles, obj) + + deviceName = handles.deviceName; + nCh = handles.nCh; + sTall = handles.sTall; + sF = handles.sF; + + + %%%%% INTAN RHA2216 %%%%% + if strcmp(deviceName, 'RHA2216') + + % Setup the selected channels + vCh = 0:nCh'-1; % Vector of channels + fwrite(obj,'C','char'); + fwrite(obj,nCh,'uint8'); + for i = 1 : nCh + fwrite(obj,vCh(i),'uint8'); + end + replay = char(fread(obj,1,'char')); + if ~strcmp(replay,'O') + set(handles.t_msg,'String','Error setting the vector of channels'); + fclose(obj); + return + else + set(handles.t_msg,'String','Channel vector set'); + end + + % Set up frequency in the microcontroller + fwrite(obj,'F','char'); + fwrite(obj,sF,'uint16'); + replay = char(fread(obj,1,'char')); + if ~strcmp(replay,'O') + set(handles.t_msg,'String','Error setting the frequency'); + fclose(obj); + return + else + set(handles.t_msg,'String','Frequency set'); + end + + % Set sampling time + fwrite(obj,sTall,'uint8'); + replay = char(fread(obj,1,'char')); + if ~strcmp(replay,'O') + set(handles.t_msg,'String','Error setting sampling time'); + fclose(obj); + return + else + set(handles.t_msg,'String','Sampling time set'); + end + + % Start the aquisition + fwrite(obj,'S','char'); + set(handles.t_msg,'String','Start'); + end + + %%%%% TI ADS1299 %%%%% + if strcmp(deviceName, 'ADS1299') + % Start the acquisition + fwrite(obj,'G','char'); + replay = char(fread(obj,1,'char')); + if strcmp(replay,'G') + set(handles.t_msg,'String','Start'); + else + set(handles.t_msg,'String','Error Start'); + fclose(obj); + return + end + end + +end diff --git a/Comm/AFE/StopAcquisition.m b/Comm/AFE/StopAcquisition.m new file mode 100644 index 0000000..62f46be --- /dev/null +++ b/Comm/AFE/StopAcquisition.m @@ -0,0 +1,46 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% ------------------- Function Description ------------------ +% Function to Record Exc Sessions +% +% --------------------------Updates-------------------------- +% 2015-1-12 / Enzo Mastinu / Divided the RecordingSession function into + % several functions: ConnectDevice(), + % SetDeviceStartAcquisition(), + % Acquire_tWs(), StopAcquisition(). This functions + % has been moved to COMM/AFE folder, into this new script. + +% 20xx-xx-xx / Author / Comment + + + +% it sends the stop acquisition command to the chosen device +function StopAcquisition(deviceName, obj) + + %%%%% INTAN RHA2216 %%%%% + if strcmp(deviceName, 'RHA2216') + fwrite(obj,'Q','char'); % Stop the aquisition ´ + fclose(obj); % Close connection + end + + %%%%% ADS1299 %%%%% + if strcmp(deviceName, 'ADS1299') + fwrite(obj,'G','char'); % Stop the aquisition ´ + fclose(obj); % Close connection + end + +end \ No newline at end of file diff --git a/Comm/ALC/VCP_Piccolo/Connect_ALC.m b/Comm/ALC/VCP_Piccolo/Connect_ALC.m index 7b2478e..211fcd4 100644 --- a/Comm/ALC/VCP_Piccolo/Connect_ALC.m +++ b/Comm/ALC/VCP_Piccolo/Connect_ALC.m @@ -1,39 +1,39 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% ------------------- Function Description ------------------ -% Function to create the communicatio object - -% --------------------------Updates-------------------------- -% 2011-11-09 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment - - - -function obj = Connect_ALC(conn) - %conn, Connection String - %Find serial port objects with specified property values - obj.io=instrfind('Status','open'); - if isempty(obj.io) - obj.io=serial(conn, 'BaudRate', 14400); - pause(1) - % Open io for read and write access - set(obj.io,'InputBuffer',1024*64) - fopen(obj.io) - pause(1) - end - -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% ------------------- Function Description ------------------ +% Function to create the communicatio object + +% --------------------------Updates-------------------------- +% 2011-11-09 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment + + + +function obj = Connect_ALC(conn) + %conn, Connection String + %Find serial port objects with specified property values + obj.io=instrfind('Status','open'); + if isempty(obj.io) + obj.io=serial(conn, 'BaudRate', 14400); + pause(1) + % Open io for read and write access + set(obj.io,'InputBuffer',1024*64) + fopen(obj.io) + pause(1) + end + +end diff --git a/Comm/ALC/VCP_Piccolo/TestConnectionALC.m b/Comm/ALC/VCP_Piccolo/TestConnectionALC.m index 55cb402..d1f0d0f 100644 --- a/Comm/ALC/VCP_Piccolo/TestConnectionALC.m +++ b/Comm/ALC/VCP_Piccolo/TestConnectionALC.m @@ -1,38 +1,38 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Function to test the communicatio object -% --------------------------Updates-------------------------- -% 2011-11-09 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment - -%% Test Connection -function result = TestConnectionALC(obj) - %If buffer not empty, - if obj.io.BytesAvailable>0 - fread(obj.io,obj.io.BytesAvailable); - end - %Send connection test - fwrite(obj.io,'C'); - pause(0.5); - if fread(obj.io,1)==1 - result=1; - else - result=0; - end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to test the communicatio object +% --------------------------Updates-------------------------- +% 2011-11-09 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment + +%% Test Connection +function result = TestConnectionALC(obj) + %If buffer not empty, + if obj.io.BytesAvailable>0 + fread(obj.io,obj.io.BytesAvailable); + end + %Send connection test + fwrite(obj.io,'C'); + pause(0.5); + if fread(obj.io,1)==1 + result=1; + else + result=0; + end end \ No newline at end of file diff --git a/Comm/ALC/VCP_Piccolo/TestPWMusingSCIbyCycling.m b/Comm/ALC/VCP_Piccolo/TestPWMusingSCIbyCycling.m index 0ad6fa7..7c9fa9d 100644 --- a/Comm/ALC/VCP_Piccolo/TestPWMusingSCIbyCycling.m +++ b/Comm/ALC/VCP_Piccolo/TestPWMusingSCIbyCycling.m @@ -1,128 +1,128 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Function to test the i-limb motors. This function will increase and -% decrease the speed as well as to change the motor direction. -% -% The corresponding function in the ACL has the same name. -% -% This fuction is a VERY slow implementation of SCI communication. It waits -% until the ALC acknowledges the comunication, otherwise it exits with a -% failure (result = 0). -% -% The driver are controlled to work in fast decay (see data-sheet DVR8833) -% in order to achieve this, one PWM sets the speed while the other is held -% low -% -% pwmDC = PWM duty cycle is inverse, so 10 = 90% of duty cycle -% -% comObj is the communication objectt required for data transmission -% -% --------------------------Updates-------------------------- -% 2011-11-07 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment - - -function result = TestPWMusingSCIbyCycling(comObj) - - % Send the Test routine in the ALC - fwrite(comObj.io,'T') - pause(0.1) - % Send the Test ID - fwrite(comObj.io,'B'); - pause(0.1) - reps = 2; - - % Repeat the cycle "reps" times - for i = 1 : reps; - - % Cycle the PWM until 100 percent to go FORWARD - for pwmDC = 0 : 20 : 100 - - % Update the all PWM - for j = 1 : 2 : 10 - - % First PWM to set the speed - fwrite(comObj.io,pwmDC,'uint8','async') - %pause(0.1) - % Read answer - if fread(comObj.io,1) ~= j - result=0; - return; - end - - % Second PWM held low - fwrite(comObj.io,100,'uint8','async') - %pause(0.1) - % Read answer - if fread(comObj.io,1) ~= j+1 - result=0; - return; - end - - end - - % Continue the transmission - fwrite(comObj.io,'C'); - end - - % Cycle the PWM until 100 percent to go Backwards - for pwmDC = 0 : 20 : 100 - - % Update the all PWM - for j = 1 : 2 : 10 - - % First PWM to set the speed - fwrite(comObj.io,100,'uint8','async') - %pause(0.1) - % Read answer - if fread(comObj.io,1) ~= j - result=0; - return; - end - - % Second PWM held low - fwrite(comObj.io,pwmDC,'uint8','async') - %pause(0.1) - % Read answer - if fread(comObj.io,1) ~= j+1 - result=0; - return; - end - - end - - % Break or continue the repetitions - if i >= reps && pwmDC >= 100 % Break the cycle - fwrite(comObj.io,'B') - %pause(0.1) - else % Continue the cycle - fwrite(comObj.io,'C') - %pause(0.1) - end - end - - end - - % Read confirmation - if fread(comObj.io,1)==1 - result=1; - else - result=0; - end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to test the i-limb motors. This function will increase and +% decrease the speed as well as to change the motor direction. +% +% The corresponding function in the ACL has the same name. +% +% This fuction is a VERY slow implementation of SCI communication. It waits +% until the ALC acknowledges the comunication, otherwise it exits with a +% failure (result = 0). +% +% The driver are controlled to work in fast decay (see data-sheet DVR8833) +% in order to achieve this, one PWM sets the speed while the other is held +% low +% +% pwmDC = PWM duty cycle is inverse, so 10 = 90% of duty cycle +% +% comObj is the communication objectt required for data transmission +% +% --------------------------Updates-------------------------- +% 2011-11-07 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment + + +function result = TestPWMusingSCIbyCycling(comObj) + + % Send the Test routine in the ALC + fwrite(comObj.io,'T') + pause(0.1) + % Send the Test ID + fwrite(comObj.io,'B'); + pause(0.1) + reps = 2; + + % Repeat the cycle "reps" times + for i = 1 : reps; + + % Cycle the PWM until 100 percent to go FORWARD + for pwmDC = 0 : 20 : 100 + + % Update the all PWM + for j = 1 : 2 : 10 + + % First PWM to set the speed + fwrite(comObj.io,pwmDC,'uint8','async') + %pause(0.1) + % Read answer + if fread(comObj.io,1) ~= j + result=0; + return; + end + + % Second PWM held low + fwrite(comObj.io,100,'uint8','async') + %pause(0.1) + % Read answer + if fread(comObj.io,1) ~= j+1 + result=0; + return; + end + + end + + % Continue the transmission + fwrite(comObj.io,'C'); + end + + % Cycle the PWM until 100 percent to go Backwards + for pwmDC = 0 : 20 : 100 + + % Update the all PWM + for j = 1 : 2 : 10 + + % First PWM to set the speed + fwrite(comObj.io,100,'uint8','async') + %pause(0.1) + % Read answer + if fread(comObj.io,1) ~= j + result=0; + return; + end + + % Second PWM held low + fwrite(comObj.io,pwmDC,'uint8','async') + %pause(0.1) + % Read answer + if fread(comObj.io,1) ~= j+1 + result=0; + return; + end + + end + + % Break or continue the repetitions + if i >= reps && pwmDC >= 100 % Break the cycle + fwrite(comObj.io,'B') + %pause(0.1) + else % Continue the cycle + fwrite(comObj.io,'C') + %pause(0.1) + end + end + + end + + % Read confirmation + if fread(comObj.io,1)==1 + result=1; + else + result=0; + end end \ No newline at end of file diff --git a/Comm/ALC/VCP_Piccolo/Update2PWMusingSCI.m b/Comm/ALC/VCP_Piccolo/Update2PWMusingSCI.m index b4c3ded..22a0975 100644 --- a/Comm/ALC/VCP_Piccolo/Update2PWMusingSCI.m +++ b/Comm/ALC/VCP_Piccolo/Update2PWMusingSCI.m @@ -1,54 +1,54 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Function to test the i-limb motors per DoF. -% -% The corresponding function in the ACL has the same name. -% -% This fuction is a slow implementation of SCI communication. It waits -% until the ALC acknowledges the comunication, otherwise it exits with a -% failure (result = 0). -% -% --------------------------Updates-------------------------- -% 2011-11-10 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment - - -function result = Update2PWMusingSCI(obj, pwmID, pwm1, pwm2) - - % Send the Test routine in the ALC - fwrite(obj.io,'D'); - %pause(0.01); % Delay seems to not be neccesary - % Send PWM ID - fwrite(obj.io,pwmID); - %pause(0.01) % Delay seems to not be neccesary - % Send duty cycle - fwrite(obj.io,100-pwm1,'uint8','sync') - %pause(0.01) % Delay seems to not be neccesary - fwrite(obj.io,100-pwm2,'uint8','sync') - %pause(0.01) % Delay seems to not be neccesary - - % If no problems where encountered, the ALC must return 1 - if fread(obj.io,1)==1 - result=1; - else - result=0; - return; - end - -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to test the i-limb motors per DoF. +% +% The corresponding function in the ACL has the same name. +% +% This fuction is a slow implementation of SCI communication. It waits +% until the ALC acknowledges the comunication, otherwise it exits with a +% failure (result = 0). +% +% --------------------------Updates-------------------------- +% 2011-11-10 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment + + +function result = Update2PWMusingSCI(obj, pwmID, pwm1, pwm2) + + % Send the Test routine in the ALC + fwrite(obj.io,'D'); + %pause(0.01); % Delay seems to not be neccesary + % Send PWM ID + fwrite(obj.io,pwmID); + %pause(0.01) % Delay seems to not be neccesary + % Send duty cycle + fwrite(obj.io,100-pwm1,'uint8','sync') + %pause(0.01) % Delay seems to not be neccesary + fwrite(obj.io,100-pwm2,'uint8','sync') + %pause(0.01) % Delay seems to not be neccesary + + % If no problems where encountered, the ALC must return 1 + if fread(obj.io,1)==1 + result=1; + else + result=0; + return; + end + +end diff --git a/Comm/ALC/VCP_Piccolo/UpdateAllPWMusingSCI.m b/Comm/ALC/VCP_Piccolo/UpdateAllPWMusingSCI.m index f74ce27..04b5cc4 100644 --- a/Comm/ALC/VCP_Piccolo/UpdateAllPWMusingSCI.m +++ b/Comm/ALC/VCP_Piccolo/UpdateAllPWMusingSCI.m @@ -1,61 +1,61 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Function to test the i-limb motors. This function will update the values -% of the PWM using a PWM ID and then sending the percentage of duty cycle. -% -% The corresponding function in the ACL has the same name. -% -% This fuction is a slow implementation of SCI communication. It waits -% until the ALC acknowledges the comunication, otherwise it exits with a -% failure (result = 0). -% -% --------------------------Updates-------------------------- -% 2011-11-08 / Max Ortiz / Creation -% 2012-05-03 / Pratham D / Added K and L -% 2012-07-08 / Max Ortiz / Update the communication protocol - -function result = UpdateAllPWMusingSCI(obj, pwms) - - % Setup IDs - pwmID = ['A' ; 'B' ; 'C' ; 'D' ; 'E'; 'F' ; 'G' ; 'H' ; 'I' ; 'J'; 'K' ; 'L']; - - for i = 1 : size(pwms,2) - % Send the Test routine in the ALC -% fwrite(obj.io,'T'); -% %pause(0.01); % Delay seems to not be neccesary -% % Send the Test ID -% fwrite(obj.io,'X'); - % Send Single PWM update - fwrite(obj.io,'S'); - %Send ID - fwrite(obj.io,pwmID(i)); - %pause(0.01) % Delay seems to not be neccesary - % Send duty cycle - fwrite(obj.io,pwms(i),'uint8','async') - %pause(0.01) % Delay seems to not be neccesary - - % If no problems where encountered, the ALC must return 1 - if fread(obj.io,1)==1 - result=1; - else - result=0; - return; - end - end -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to test the i-limb motors. This function will update the values +% of the PWM using a PWM ID and then sending the percentage of duty cycle. +% +% The corresponding function in the ACL has the same name. +% +% This fuction is a slow implementation of SCI communication. It waits +% until the ALC acknowledges the comunication, otherwise it exits with a +% failure (result = 0). +% +% --------------------------Updates-------------------------- +% 2011-11-08 / Max Ortiz / Creation +% 2012-05-03 / Pratham D / Added K and L +% 2012-07-08 / Max Ortiz / Update the communication protocol + +function result = UpdateAllPWMusingSCI(obj, pwms) + + % Setup IDs + pwmID = ['A' ; 'B' ; 'C' ; 'D' ; 'E'; 'F' ; 'G' ; 'H' ; 'I' ; 'J'; 'K' ; 'L']; + + for i = 1 : size(pwms,2) + % Send the Test routine in the ALC +% fwrite(obj.io,'T'); +% %pause(0.01); % Delay seems to not be neccesary +% % Send the Test ID +% fwrite(obj.io,'X'); + % Send Single PWM update + fwrite(obj.io,'S'); + %Send ID + fwrite(obj.io,pwmID(i)); + %pause(0.01) % Delay seems to not be neccesary + % Send duty cycle + fwrite(obj.io,pwms(i),'uint8','async') + %pause(0.01) % Delay seems to not be neccesary + + % If no problems where encountered, the ALC must return 1 + if fread(obj.io,1)==1 + result=1; + else + result=0; + return; + end + end +end diff --git a/Comm/ALC/VCP_Piccolo/UpdatePWMusingSCI_PanTilt.m b/Comm/ALC/VCP_Piccolo/UpdatePWMusingSCI_PanTilt.m index d3ad12e..e1fb5af 100644 --- a/Comm/ALC/VCP_Piccolo/UpdatePWMusingSCI_PanTilt.m +++ b/Comm/ALC/VCP_Piccolo/UpdatePWMusingSCI_PanTilt.m @@ -1,79 +1,79 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Function to send the PWM for control of the servo motors for the Pan & -% Tilt unit. -% -% --------------------------Updates-------------------------- -% 2012-05-04 / Max Ortiz / Creation -% 2012-06-29 / Max Ortiz / Modified the protocolo for communication with -% the microcontroller, now it will only send a "S" -% before the servo ID, and duty cycle. -% 20xx-xx-xx / Author / - - -function [result pwmPer] = UpdatePWMusingSCI_PanTilt(obj, pwmID, pwmPer) - - %% Safety routine (specifically for the servocity pantilt) - %Tilt - if strcmp(pwmID,'L') - maxP = 85; - minP = 7; - if minP > pwmPer - pwmPer = minP; - end - if maxP < pwmPer - pwmPer = maxP; - end - end - %Pan - if strcmp(pwmID,'K') - maxP = 100; - minP = 1; - if minP > pwmPer - pwmPer = minP; - end - if maxP < pwmPer - pwmPer = maxP; - end - end - - %% Send values to controller - % Send the Test routine in the ALC - % fwrite(obj.io,'T'); - % pause(0.01); % Delay seems to not be neccesary - % Send the Test ID - % fwrite(obj.io,'X'); - - % Send servo indentifier - fwrite(obj.io,'S'); - %Send ID - fwrite(obj.io,pwmID); - %pause(0.01) % Delay seems to not be neccesary - % Send duty cycle - fwrite(obj.io,pwmPer,'uint8','async') - %pause(0.01) % Delay seems to not be neccesary - - if fread(obj.io,1)==1 - result=1; - else - result=0; - return; - end - -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to send the PWM for control of the servo motors for the Pan & +% Tilt unit. +% +% --------------------------Updates-------------------------- +% 2012-05-04 / Max Ortiz / Creation +% 2012-06-29 / Max Ortiz / Modified the protocolo for communication with +% the microcontroller, now it will only send a "S" +% before the servo ID, and duty cycle. +% 20xx-xx-xx / Author / + + +function [result pwmPer] = UpdatePWMusingSCI_PanTilt(obj, pwmID, pwmPer) + + %% Safety routine (specifically for the servocity pantilt) + %Tilt + if strcmp(pwmID,'L') + maxP = 85; + minP = 7; + if minP > pwmPer + pwmPer = minP; + end + if maxP < pwmPer + pwmPer = maxP; + end + end + %Pan + if strcmp(pwmID,'K') + maxP = 100; + minP = 1; + if minP > pwmPer + pwmPer = minP; + end + if maxP < pwmPer + pwmPer = maxP; + end + end + + %% Send values to controller + % Send the Test routine in the ALC + % fwrite(obj.io,'T'); + % pause(0.01); % Delay seems to not be neccesary + % Send the Test ID + % fwrite(obj.io,'X'); + + % Send servo indentifier + fwrite(obj.io,'S'); + %Send ID + fwrite(obj.io,pwmID); + %pause(0.01) % Delay seems to not be neccesary + % Send duty cycle + fwrite(obj.io,pwmPer,'uint8','async') + %pause(0.01) % Delay seems to not be neccesary + + if fread(obj.io,1)==1 + result=1; + else + result=0; + return; + end + +end diff --git a/Control/ConnectVRE.m b/Comm/ConnectVRE.m similarity index 95% rename from Control/ConnectVRE.m rename to Comm/ConnectVRE.m index f62db15..2a02cea 100644 --- a/Control/ConnectVRE.m +++ b/Comm/ConnectVRE.m @@ -1,49 +1,50 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Used to Connect a VRE session to a struct. -% Can be used for Realtime, Training, Motion Test etc. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2013-01-29 / Nichlas Sander / Creation - -function handles = ConnectVRE(handles, program) - if ~isfield(handles,'vre_Com') - open(program); - handles.vre_Com = tcpip('127.0.0.1',23068,'NetworkRole','server'); - fopen(handles.vre_Com); - end - handles = DisableIfExists(handles,'pb_socketConnect'); - handles = DisableIfExists(handles,'pb_socketConnect2'); - handles = EnableIfExists(handles,'pb_socketDisconnect'); - handles = EnableIfExists(handles,'pb_Camera'); - handles = EnableIfExists(handles,'pb_ActivateArm'); -end - -function handles = EnableIfExists(handles,field) - if isfield(handles,field) - set(handles.(field),'Enable','on'); - end -end - -function handles = DisableIfExists(handles,field) - if isfield(handles,field) - set(handles.(field),'Enable','off'); - end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Used to Connect a VRE session to a struct. +% Can be used for Realtime, Training, Motion Test etc. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2013-01-29 / Nichlas Sander / Creation + +function handles = ConnectVRE(handles, program) + if ~isfield(handles,'vre_Com') + open(program); + handles.vre_Com = tcpip('127.0.0.1',23068,'NetworkRole','server'); + fopen(handles.vre_Com); + end + handles = DisableIfExists(handles,'pb_socketConnect'); + handles = DisableIfExists(handles,'pb_socketConnect2'); + handles = DisableIfExists(handles,'pb_ARarm'); + handles = EnableIfExists(handles,'pb_socketDisconnect'); + handles = EnableIfExists(handles,'pb_Camera'); + handles = EnableIfExists(handles,'pb_ActivateArm'); +end + +function handles = EnableIfExists(handles,field) + if isfield(handles,field) + set(handles.(field),'Enable','on'); + end +end + +function handles = DisableIfExists(handles,field) + if isfield(handles,field) + set(handles.(field),'Enable','off'); + end end \ No newline at end of file diff --git a/Control/DisconnectVRE.m b/Comm/DisconnectVRE.m similarity index 95% rename from Control/DisconnectVRE.m rename to Comm/DisconnectVRE.m index 829e0a2..96eb48f 100644 --- a/Control/DisconnectVRE.m +++ b/Comm/DisconnectVRE.m @@ -1,48 +1,49 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Used to Disconnect any existing VRE on a struct. -% Can be used for Realtime, Training, Motion Test etc. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2013-01-29 / Nichlas Sander / Creation - -function handles = DisconnectVRE(handles) - if isfield(handles,'vre_Com') - fclose(handles.vre_Com); - handles = rmfield(handles,'vre_Com'); - end - handles = EnableIfExists(handles,'pb_socketConnect'); - handles = EnableIfExists(handles,'pb_socketConnect2'); - handles = DisableIfExists(handles,'pb_socketDisconnect'); - handles = DisableIfExists(handles,'pb_Camera'); - handles = DisableIfExists(handles,'pb_ActivateArm'); -end - -function handles = DisableIfExists(handles,field) - if isfield(handles,field) - set(handles.(field),'Enable','off'); - end -end - -function handles = EnableIfExists(handles,field) - if isfield(handles,field) - set(handles.(field),'Enable','on'); - end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Used to Disconnect any existing VRE on a struct. +% Can be used for Realtime, Training, Motion Test etc. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2013-01-29 / Nichlas Sander / Creation + +function handles = DisconnectVRE(handles) + if isfield(handles,'vre_Com') + fclose(handles.vre_Com); + handles = rmfield(handles,'vre_Com'); + end + handles = EnableIfExists(handles,'pb_socketConnect'); + handles = EnableIfExists(handles,'pb_socketConnect2'); + handles = EnableIfExists(handles,'pb_ARarm'); + handles = DisableIfExists(handles,'pb_socketDisconnect'); + handles = DisableIfExists(handles,'pb_Camera'); + handles = DisableIfExists(handles,'pb_ActivateArm'); +end + +function handles = DisableIfExists(handles,field) + if isfield(handles,field) + set(handles.(field),'Enable','off'); + end +end + +function handles = EnableIfExists(handles,field) + if isfield(handles,field) + set(handles.(field),'Enable','on'); + end end \ No newline at end of file diff --git a/Comm/GetMovementCombination.m b/Comm/GetMovementCombination.m index aec8c3a..321d16f 100644 --- a/Comm/GetMovementCombination.m +++ b/Comm/GetMovementCombination.m @@ -1,98 +1,98 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Input: None. Hard-coded, should be dynamic. -% -% Output: combinations - The possible combinations of the inputting -% movements, with respect to the limitations in the limitMovements.def -% file. -% --------------------------Updates-------------------------- -% [Contributors are welcome to add their email] -% 2012-07-30 / Nichlas Sander / Creation of GetMovementCombination - -function combinations = GetMovementCombination(num,numMovements) - -if num == 1 - combinations = randperm(numMovements)'; - return; -elseif num == 2 - if numMovements < 3 - return; - end - combos = [1,3;1,4;2,3;2,4;]; - if numMovements > 4 - combos = [combos; - 1,5; - 1,6; - 2,5; - 2,6; - 3,5; - 3,6; - 4,5; - 4,6; ]; - end -elseif num == 3 - if numMovements < 6 - return; - end - combos = [ 1,3,5; - 1,3,6; - 1,4,5; - 1,4,6; - 2,3,5; - 2,3,6; - 2,4,5; - 2,4,6; ]; -else - - return; -end - - -combinations = combos(randperm(length(combos)),:); - -% fid = fopen('limitMovements.def'); -% tline = fgetl(fid); -% while ischar(tline) -% %Puts the data of tline into t -% t = textscan(tline,'%s','delimiter',';'); -% t = t{1}; -% %These are for movements that are not to be combined with any other -% %movements, such as rest. -% if strcmp(t(2),'*') -% tline = fgetl(fid); -% continue; -% end -% temp = t(2); -% temp = textscan(temp{1},'%s','delimiter',','); -% limitMovements(t(1),2:length(temp{1})+1) = temp{1}; -% tline = fgetl(fid); -% end -% %Loop through the movements, and generate a random array. -% for j = 1:size(movOutIdx,1) -% movement = movements(movOutIdx{j}); -% notAllowed = limitMovements(movement.idVRE,:); -% movs = movOutIdx; -% movs(j) = []; -% movs(find(movs == notAllowed)) = []; -% end -% -% combinations = zeros(length(movements),1); -% -% fclose(fid); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Input: None. Hard-coded, should be dynamic. +% +% Output: combinations - The possible combinations of the inputting +% movements, with respect to the limitations in the limitMovements.def +% file. +% --------------------------Updates-------------------------- +% [Contributors are welcome to add their email] +% 2012-07-30 / Nichlas Sander / Creation of GetMovementCombination + +function combinations = GetMovementCombination(num,numMovements) + +if num == 1 + combinations = randperm(numMovements)'; + return; +elseif num == 2 + if numMovements < 3 + return; + end + combos = [1,3;1,4;2,3;2,4;]; + if numMovements > 4 + combos = [combos; + 1,5; + 1,6; + 2,5; + 2,6; + 3,5; + 3,6; + 4,5; + 4,6; ]; + end +elseif num == 3 + if numMovements < 6 + return; + end + combos = [ 1,3,5; + 1,3,6; + 1,4,5; + 1,4,6; + 2,3,5; + 2,3,6; + 2,4,5; + 2,4,6; ]; +else + + return; +end + + +combinations = combos(randperm(length(combos)),:); + +% fid = fopen('limitMovements.def'); +% tline = fgetl(fid); +% while ischar(tline) +% %Puts the data of tline into t +% t = textscan(tline,'%s','delimiter',';'); +% t = t{1}; +% %These are for movements that are not to be combined with any other +% %movements, such as rest. +% if strcmp(t(2),'*') +% tline = fgetl(fid); +% continue; +% end +% temp = t(2); +% temp = textscan(temp{1},'%s','delimiter',','); +% limitMovements(t(1),2:length(temp{1})+1) = temp{1}; +% tline = fgetl(fid); +% end +% %Loop through the movements, and generate a random array. +% for j = 1:size(movOutIdx,1) +% movement = movements(movOutIdx{j}); +% notAllowed = limitMovements(movement.idVRE,:); +% movs = movOutIdx; +% movs(j) = []; +% movs(find(movs == notAllowed)) = []; +% end +% +% combinations = zeros(length(movements),1); +% +% fclose(fid); end \ No newline at end of file diff --git a/Comm/Keys/GUI_SendKeys.fig b/Comm/Keys/GUI_SendKeys.fig index c02c382913c97a3380477c218ae274ef886032e2..05afa552b7a69decf4f967531174229346802071 100644 GIT binary patch literal 7063 zcma)hXEYp8x3(4~x)2efM2k-J(W6H16TOD$Bt|!g-ibu-L`{qq-RLdKDA7WUJ{WzJ z!QgY>cYVL^pL_nCz1J@5oVE6I_OsW~QZUq1c*88pFUYK=V94j-~JhV+H2!n z%#CX=P0Rzn-;r?%z4G@Myurpogy306p#EV-!ZUr|N|wt6qGz0qjpNJXat*s|YJp(6 z0j9Z&RFSFAmj7ZO6;LthMd@MfKlRz8_U{lqPE9O&a=rj7$dDgOUSk9&sPXRe_PLe5##JR#TO*xdMazbxDTR=RSfi;SJcu4JkdPLybpL3sJqA%TSyg zYo1uI+U^UKHTh0H!J8(tls7pn&r6#d=4)B)>HSICUkS18kA)u~W+`^^fp!>4s;L96 zG~&R4RH>zgfCUYJ%-Pw1;TS~yE^!n7_O5eNPVFvjQ=IKy`P7QNi|dMSM4bKJ;&kcD z{T0(jGpU?-qJ;Z%ud0@#bjCIK!F(Z_hk=^^F=u2>uijdfr*R9MPo`IHoBZ)Tf$B?- zAd)7SqtRp@EW#XMK~pJFkptX>k1_rvL_7VkyQ z=Bi$_Dw#K51DpwF=O_4o7^eVfdmB2A^}#eZXwg4g^Aya9D%_nx8J3JOIBbb4o*65G z$1%~Kg}(%}{ykTtW$<+3(C6{&H)B~cR(DLqAKOI%8+ z?&JU4;I*d*Xp{bR*}gxZA)jmka1wTstKv(^?Wdj2FlRe9K=`kUrO85Zaec@DEi09& zk+){;_zhiFzv(e+zl1DRtAC<-!nO8AWA-)BMW5H(ejvC$Qji#W>9lhPe^`2@<{Z=< zmN&s7&+=@39Qz`h%=UDQq7m0D*JoX;WSb%|F(mn1sg|ZCX>T3<qYQ#dhFdD=UkKE|+3V-`8Zra1V~Z1k5IWM*usF|R{)^s`;UyiyflVmaESztZt1&wO$3 zY3BKXs5Vnx67qM+C$BvJs{*bWcYzQx`W8ZyAF$8w3Sr~x91fFQypdz%X+jfJ<*#^) z0gp(;V21x*wU}_1nOwFfpd)yLvX)tbtv#!gI|DoOJwj)uq%qDqP3J+6TrFV2((~e6 zM4bUa5UVd&R>4f(GAJPuP^%K9{f7x-{6j>*Q3m|{6EI(+)trB#=pdlkPaak0}C)zLefN|nS8Ttf4ex~WqdyS`QT~zTE%t`5h<;Voo^C|me7JY=tGGi zXu2KW@(tPZp}4u&IsF3}`v#)MiRkYWh@%!uRIWsRZZ!vR_6&eOf z4e{mxc!}NS@2y#?yXtOm@$QS)*IO;$2d4Fvw`lAbYkVjU7jowJ4}>wgU18ieZy4jP zLMFlwPmIq4(xtDz^HA*)8&~)2Hqic5TYfd@fBx4>kMUfzy_L~t{M_9w9I`Wyyli=W zQb2UuYwNwO?NmPR=fZi}uRVu?-8&WB&|+cT-|ri1dCUkt^ZDymTU6w=#9u+%X#8AX z&PP5d##-||XtE$}snP?`4x||jv4%5F}Y20hT z`dsmon|`nyMiw7ym4hCi@IC7^y{{BsGKq`?_x=*usnq!O8tU2-q5R9d@(inGV(#nW zJDUw|dX3cEkEQg}gkGeR*t8A8tnK6Gt{ED2W)8zHsh0M36xQ*7@o?pb_#e-cEcEo2 z`}RwnSbQiN30W!Mhg75Ek$0=ci@c0XXDiI{hlS0AGqR<%XXqWL=0wkXy5~0oZW%3M zQg0+Gyy+P~Qy-Z51tOTL^K7h9Cad4n(}J_OZcL3X20$@HjLB-Nsz);z0i{7oA11bT z`rm+X7EyWvw;GSPcZZCM%f7ne7oG{1O1);li@M_eG-S@&1|;`POS;-Chh1)tH4jF1 zwyUwTaH}XmeWQW8e2!kyF;8Y?C=ljh0BQGW{f_Q#A}nL{V#}4Hnoy5}8!O~;TJ^}J zErnR+z>ers@3InET@6xm%r6Q-ug}y%Z%EfE$VCb_&K9IjI+cT@er)Le{nKzF*yJ%& z_i8V;HWR;Imakn6(q3a{mZB(YOG8k6tg}^o(vVa{z!xViH<*g#AA$K3aSn;}3HEkO z4EfL#`P7G8UC&>|4}=$d!mGp$WMmP+9I|G-h^SX1d|^P(nkws@r*j8aKgl>0N1f*Y z$p$)pg2jS}hpJspBpZ755F*Q?h*@$G2>yc$xZ$`X0EpUi zy&k9Wl!e^k{2rb-s$y_QjYTpGO_jy_i(a-=E=95X ze3dAK4)n*eCU-j>Qyk+x0p^FXUQmf!8mGG2Pm?t{OIq#UyWGJq?n6Fdg}VH3`2ka5 z^+5d)sJk1Nl>L}Af4&y_3tk-{(O!0s=~*gB%pqz0LCem#vd#A6?a@_$g{hp>`&*Dx zuw<{>dJX=eKJgl}Ff8bEwB1AfA_>z|$~jJ`4WM1!`HjPeo*Yd)j>k87jnI^OaFV}a zlHlb`*qM@?dvmhSE#E18O)KhGxyJ)T2+;()mSxV&Jt<*GjMk*I+>>a{p(TCAM~ zWxXGnc55@L>o3s!B7J@@%7O|~V`zu&R>4}31fRg8)VI|;xJ&7>rJh@hcS%zUE%!Ky zx4%y}sbxj7AJFHP9?CoZ;pyzhZ zqP|iLaD2y!B1%<>?Lht$J~n7c=7LTVZQqd9BPVTi{&F)f!j7dksIf@LN^xoLsHX@G zZS>3N-j(oTIP%)b?vGcG=<%m0NQ11#P8@V)@TbF}wa{cf z?gwImYT0uM=LpDfVSa06~RB>oszd)p^JS>58p1(Jz z#>I?d^Q+EcBJR|M&R^wA7(<%Y+(d32uzum)-8qns|Utl9v7rfBj?SXFnOzab3sM-_nc-)bf}VX-pMk8N#?H(9hg zA2l~RgDg6WyhU}}OlNa=Al3BhK!kxugvzaz#S3U>Nl06BA6Ok&EnR+UPS(~|CEUd+`GFg38v8T`*o)jE~<44 z%8NMHi>v4sV`3CH!X#GO+^fCaW}qy~K40ml;L+6I(rt19nZ;^&sTdc=h9`> zzegH2D0VH5B7K7kbfCA;BgE1#jW;*}Sj;M_QAy4Qlrg2;SoRuEirkFeHYMd(UAGn4 zC`8Ea9ZXIs519a{wsF)rJsoyTW_tQhScAF zTX>BQmx>y>IFsr*X=glsmp=OLolY_%cH&$%RgPO*D&>cv$e{H-pMim(9~Hli72TfC zT_!kUo-r=@G16at>C5x|8Lw9s*{h#=McWdTvG=ZW*mh*3w$C?u0|^?KdhfM_8DkP8 z?;2?R)HyeXPM3u8Cu7(oWYcq404t&wD`FQMdY$SAH-Z6Rr+jez4vN#E>th0N!_SVf zBPdtlVmFtkYs|{{S6!J{qr3~NVDdbe$?zF1c^BN$e{CaqhE=LS%8|kRH^I;x#0$A0 zi%dI<(@%%hG=#+_jJ8!#p*mQc-wi3v_FT}sDw>!c5b%p%llf`sljgSBae zC=qz;ZqLk|~kXAN(l`!#p5oTb$bf=06#9!NsfG|NMzSN+zbj03r(Ni%N_IYDpsnL3N z@XI!ZsB%osABbBu5tL8oHym!Rqr1Kj_F4dpkPrpY5C|~ zoX4sB%$fY$P8<($Dy`FQp7D9#-msYWxzk_d#8$eJ6N*E7j=NLNY$@Y~svvM|liC+_U2ftc>IojP zzW`m{UTRxwh|q zuY-qMF_bii79hu{W|mdZ&GC~b$Iryy0uIHL^q`Oz!Ddp|!k4?OX-mJCdsf54d*GkO zk5@QP2F`ewl}F4*z5}02S{~6){=GZ!1&JXiIdlqZXqv6FHpcPLv$)#V1KvL&^v@J&NmY{Dw#iqeK{wnaoL?7|Cj720$R8Pl@km{?RWOs+tUiM?U&6ip@ z-K3XwB7MTJKuxNe8sox8MxS|?`Y2JMOfU-^?XS+MYW*Y07Wo#vL)x1eGWW&Roqn|- zW0onKp56sXm2MRd3&Nt*)iwnY+>--VM4o|vPcnp_%1ToQ4s(0g;@acDFHWhSov|k>jmLJPv)(V&U_su9D^@V8;tfuv|y3-o+x!30Bq%#fb=3UI0e;LluJu zTo-`e95C|FpZ)^J0+9RENzw#s`M38pHdd4uG_=(#m`DFXA@JV==cD#%V-hPVwU0?ySCeE&3Vin|e5ijt-ffJ~trL zk9YZk3OyA(VQ@vPv#gZAa9CpPs~DhTr)f5O02sP_6e1o-(d_m|Q#$v!^C{ZlJ4jRg zWYdqo1EWWg|1U{pqM7wrs|@XEzPbU;a=hI+Yxs2C=kj21HF_ykB{bYByPoIogbhD2ZAYRom@FSfqpd2f>Evxr(LXtP+wA4 zh{T1*dF3;kyF9W|F<1YH$>Y{!^Bo0sX$m_{zJz_k-#19}yYee!*rq^IHH==0aW`Nx z3sU!SN4#n7LTrAHzI~L&!h@7n?tt8qR74*mn~;aCQa!<>ez%Us0j(&e6lcdqbDvdB zl+RyDF;}8&m!n3K7YR{QQL-|jO|vWE|NMeY?($PH{dE*JDdNe}4ku+>iq=_c3NMdi zK+@G@+Tm?FZa_ySVoO1&Th5lwyHDG;^r^!M$mq zl>{Q`M^PeL28cv}$Hys}wrQHyTyfu?E$@NB=;(c*Ff?T2pl+H*e)sWr9>YAC2JnIR zZR#VA!t`co0K++|j17MG%{47#wg2&X$Oo-?XWaHO1LZ(wt_{rfG^x`Q#=km)mU=(Z zZF{r(*a(4|rWs`|;{pF?RyqLw(|+h)`Fwnz@suFlOw+_9DUC#OvlJfY6_r#h6pt-M zxn>R;EfyzSeH__4Q7iNt-vYJ_DvsU+J+|tW^p1WJmB{yt}XM+?4n%G6NW z$8dW;xhLyWDQ2g*`MZ{PYFsb}MdF|%8tB(S#}7etbt@wj;h9SZ+&u{MMV#)4-CrK_Hp}0%7UjxVV{N`x#rx@E3OJ3=?zpo^^gLBrD z-FU}z#rUha9hQ7>wMDk<)`iZ+kJ&JC+YVL62A*CqqTGggVn>%7oPq3D&wuIeKv-zLBRR(2Sv@hKl4skxQl`^FiEu;rP~vy2TZNsq(goen*ew7Xn6jqD@` z&-Tgg{Kf9Z7ypX`55%~KTg%BX?~fkz6}6uQ>$<4ONqr65+SH* zL5A$~D#oLe08W}n?iuUPxlnz%C0BNZ8`VaRcZ+ru3F*G^X&BH8xseI(kxOiP8%~~m zgpeXpzCN!)Ib-zK_b~Sw`iOfZAf$BnkzXi4;C}7QXeSN8WQ8~?LjDE*D6&EAG|?Wx z4|&SQN+D|3ahn~f080e4IOU8+@!<+3a!8()agX3kICoZoTu-LP_2aK+G;gg^|6JLe z(?pg+o~h@Bgx0uN>B;wP$2N3fjUwq1{^$2(>Ve$(?I^}c?V zo%xJ`f`H6>P9!EwEwpg31vg`zYB?cRqwagimY=?0f7VJ4M)H>IvZZY*MS6um85&VLZwkc2&w4cqC&`7jv-$${rP2 zgkbVf3)$+Wp{-3Ds3}0$*S{eXpi@2m@Ahw=rjd6RC8L$xM)&JjyMC{AygKDvw(XlN zjtL6K*iZdpUimHZ=4||w8L>S5M(fYL@>&#@+1x3cW2O2nesv|pxP=Ejp}u|g>e@^9 z>`CE{S>X<+!jw)}aRI)v9Bv%=DfKPGt81}W*Z#U^#DzPw3R41M#a8&rT{v-I8tU8U zudYG5XY_?T;tEq9Va4zV!$S})^)2IW2rmZGS$TW(B8VgUmUXAO5?%~0I1dIRf+JyL zMS#QGQGd3s%?!*Jt5Xf|h`<&5|5olH+aWTq?{$U@_Kr`$QujD0J-7>`2>vGl?s9T{ Pza99~ssQrN=jQ(bf*?Ch literal 4782 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 diff --git a/Comm/Keys/GUI_SendKeys.m b/Comm/Keys/GUI_SendKeys.m index 363878a..a2ce5bf 100644 --- a/Comm/Keys/GUI_SendKeys.m +++ b/Comm/Keys/GUI_SendKeys.m @@ -22,7 +22,7 @@ % Edit the above text to modify the response to help GUI_SendKeys -% Last Modified by GUIDE v2.5 22-Nov-2012 09:30:26 +% Last Modified by GUIDE v2.5 07-Aug-2013 14:54:20 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; @@ -65,14 +65,13 @@ function GUI_SendKeys_OpeningFcn(hObject, eventdata, handles, varargin) movements = mainHandles.patRec.mov; handles.mov = movements; - - numberOfMovements = mainHandles.patRec.nOuts; - - for i = 1:numberOfMovements + for i = 1:length(movements) obj = sprintf('txt_%i',i); - set(handles.(obj),'String',movements{i}); + if(isfield(handles,obj)) + set(handles.(obj),'String',movements{i}); + end end - for i = numberOfMovements:7 + for i = length(movements):7 obj = sprintf('txt_%i',i); set(handles.(obj),'Visible','off'); obj = sprintf('popupmenu%i',i); @@ -276,14 +275,185 @@ function pb_close_Callback(hObject, eventdata, handles) inObj = handles.main; savedKeys = zeros(length(handles.mov),1); +savedControls = zeros(length(handles.mov),1); for i = 1:length(handles.mov)-1 obj = sprintf('popupmenu%i',i); - savedKeys(i) = get(handles.(obj),'Value') - 1; + if(isfield(handles,obj)) + savedKeys(i) = get(handles.(obj),'Value') - 1; + end +end +for i = 1:length(handles.mov)-1 + obj = sprintf('popupmenu%i',i+7); + if(isfield(handles,obj)) + savedControls(i) = get(handles.(obj),'Value') - 1; + end end mainHandles = guidata(inObj); mainHandles.savedKeys = savedKeys; +mainHandles.savedControls = savedControls; guidata(inObj,mainHandles); close(clf); + + +% --- Executes on selection change in popupmenu8. +function popupmenu8_Callback(hObject, eventdata, handles) +% hObject handle to popupmenu8 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu8 contents as cell array +% contents{get(hObject,'Value')} returns selected item from popupmenu8 + + +% --- Executes during object creation, after setting all properties. +function popupmenu8_CreateFcn(hObject, eventdata, handles) +% hObject handle to popupmenu8 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in popupmenu9. +function popupmenu9_Callback(hObject, eventdata, handles) +% hObject handle to popupmenu9 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu9 contents as cell array +% contents{get(hObject,'Value')} returns selected item from popupmenu9 + + +% --- Executes during object creation, after setting all properties. +function popupmenu9_CreateFcn(hObject, eventdata, handles) +% hObject handle to popupmenu9 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in popupmenu10. +function popupmenu10_Callback(hObject, eventdata, handles) +% hObject handle to popupmenu10 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu10 contents as cell array +% contents{get(hObject,'Value')} returns selected item from popupmenu10 + + +% --- Executes during object creation, after setting all properties. +function popupmenu10_CreateFcn(hObject, eventdata, handles) +% hObject handle to popupmenu10 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in popupmenu11. +function popupmenu11_Callback(hObject, eventdata, handles) +% hObject handle to popupmenu11 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu11 contents as cell array +% contents{get(hObject,'Value')} returns selected item from popupmenu11 + + +% --- Executes during object creation, after setting all properties. +function popupmenu11_CreateFcn(hObject, eventdata, handles) +% hObject handle to popupmenu11 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in popupmenu12. +function popupmenu12_Callback(hObject, eventdata, handles) +% hObject handle to popupmenu12 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu12 contents as cell array +% contents{get(hObject,'Value')} returns selected item from popupmenu12 + + +% --- Executes during object creation, after setting all properties. +function popupmenu12_CreateFcn(hObject, eventdata, handles) +% hObject handle to popupmenu12 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in popupmenu13. +function popupmenu13_Callback(hObject, eventdata, handles) +% hObject handle to popupmenu13 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu13 contents as cell array +% contents{get(hObject,'Value')} returns selected item from popupmenu13 + + +% --- Executes during object creation, after setting all properties. +function popupmenu13_CreateFcn(hObject, eventdata, handles) +% hObject handle to popupmenu13 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in popupmenu14. +function popupmenu14_Callback(hObject, eventdata, handles) +% hObject handle to popupmenu14 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu14 contents as cell array +% contents{get(hObject,'Value')} returns selected item from popupmenu14 + + +% --- Executes during object creation, after setting all properties. +function popupmenu14_CreateFcn(hObject, eventdata, handles) +% hObject handle to popupmenu14 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end diff --git a/Control/ResetVRE.m b/Comm/ResetVRE.m similarity index 97% rename from Control/ResetVRE.m rename to Comm/ResetVRE.m index ce29d7f..b206233 100644 --- a/Control/ResetVRE.m +++ b/Comm/ResetVRE.m @@ -1,34 +1,34 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This is used to reset the position of a specified hand within a -% VRE-session. -% The 'returningValue' specifies whether the VR-system is expected to -% return a value. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2013-01-29 / Nichlas Sander / Creation - -function ResetVRE(obj,handToReset, returningValue) - fwrite(obj,sprintf('%c%c%c%c%c','r',char(handToReset),char(0),char(0),char(0))); - if returningValue - fread(obj,1); - end -end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This is used to reset the position of a specified hand within a +% VRE-session. +% The 'returningValue' specifies whether the VR-system is expected to +% return a value. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2013-01-29 / Nichlas Sander / Creation + +function ResetVRE(obj,handToReset, returningValue) + fwrite(obj,sprintf('%c%c%c%c%c','r',char(handToReset),char(0),char(0),char(0))); + if returningValue + fread(obj,1); + end +end + diff --git a/Comm/SendKeys.m b/Comm/SendKeys.m index d3b1e63..2a54c1c 100644 --- a/Comm/SendKeys.m +++ b/Comm/SendKeys.m @@ -1,66 +1,66 @@ -function SendKeys(keys,savedKeys) - import java.awt.*; - import java.awt.event.*; - rob=Robot; - - %AvailableKeys = {KeyEvent.VK_A,KeyEvent.VK_B,KeyEvent.VK_C,KeyEvent.VK_D,KeyEvent.VK_E,KeyEvent.VK_F,KeyEvent.VK_G,KeyEvent.VK_H,KeyEvent.VK_I,KeyEvent.VK_J,KeyEvent.VK_K,KeyEvent.VK_L,KeyEvent.VK_M,KeyEvent.VK_N,KeyEvent.VK_O,KeyEvent.VK_P,KeyEvent.VK_Q,KeyEvent.VK_R,KeyEvent.VK_S,KeyEvent.VK_T,KeyEvent.VK_U,KeyEvent.VK_V,KeyEvent.VK_W,KeyEvent.VK_X,KeyEvent.VK_Y,KeyEvent.VK_Z}; - - if(numel(savedKeys) > 0) - for i = 1:length(savedKeys) - KeyFunction(savedKeys(i),keys(i),rob); - end - else - if keys(1) - rob.keyPress(KeyEvent.VK_W); - else - rob.keyRelease(KeyEvent.VK_W); - end - if keys(2) - rob.keyPress(KeyEvent.VK_S); - else - rob.keyRelease(KeyEvent.VK_S); - end - if keys(3) - rob.keyPress(KeyEvent.VK_A); - else - rob.keyRelease(KeyEvent.VK_A); - end - if keys(4) - rob.keyPress(KeyEvent.VK_D); - else - rob.keyRelease(KeyEvent.VK_D); - end - end -end - -function KeyFunction(key,event,rob) - import java.awt.*; - import java.awt.event.*; - - switch(key) - case 1 - if event - rob.keyPress(KeyEvent.VK_W); - else - rob.keyRelease(KeyEvent.VK_W); - end - case 2 - if event - rob.keyPress(KeyEvent.VK_A); - else - rob.keyRelease(KeyEvent.VK_A); - end - case 3 - if event - rob.keyPress(KeyEvent.VK_S); - else - rob.keyRelease(KeyEvent.VK_S); - end - case 4 - if event - rob.keyPress(KeyEvent.VK_D); - else - rob.keyRelease(KeyEvent.VK_D); - end - end +function SendKeys(keys,savedKeys) + import java.awt.*; + import java.awt.event.*; + rob=Robot; + + %AvailableKeys = {KeyEvent.VK_A,KeyEvent.VK_B,KeyEvent.VK_C,KeyEvent.VK_D,KeyEvent.VK_E,KeyEvent.VK_F,KeyEvent.VK_G,KeyEvent.VK_H,KeyEvent.VK_I,KeyEvent.VK_J,KeyEvent.VK_K,KeyEvent.VK_L,KeyEvent.VK_M,KeyEvent.VK_N,KeyEvent.VK_O,KeyEvent.VK_P,KeyEvent.VK_Q,KeyEvent.VK_R,KeyEvent.VK_S,KeyEvent.VK_T,KeyEvent.VK_U,KeyEvent.VK_V,KeyEvent.VK_W,KeyEvent.VK_X,KeyEvent.VK_Y,KeyEvent.VK_Z}; + + if(numel(savedKeys) > 0) + for i = 1:length(savedKeys) + KeyFunction(savedKeys(i),keys(i),rob); + end + else + if keys(1) + rob.keyPress(KeyEvent.VK_W); + else + rob.keyRelease(KeyEvent.VK_W); + end + if keys(2) + rob.keyPress(KeyEvent.VK_S); + else + rob.keyRelease(KeyEvent.VK_S); + end + if keys(3) + rob.keyPress(KeyEvent.VK_A); + else + rob.keyRelease(KeyEvent.VK_A); + end + if keys(4) + rob.keyPress(KeyEvent.VK_D); + else + rob.keyRelease(KeyEvent.VK_D); + end + end +end + +function KeyFunction(key,event,rob) + import java.awt.*; + import java.awt.event.*; + + switch(key) + case 1 + if event + rob.keyPress(KeyEvent.VK_W); + else + rob.keyRelease(KeyEvent.VK_W); + end + case 2 + if event + rob.keyPress(KeyEvent.VK_A); + else + rob.keyRelease(KeyEvent.VK_A); + end + case 3 + if event + rob.keyPress(KeyEvent.VK_S); + else + rob.keyRelease(KeyEvent.VK_S); + end + case 4 + if event + rob.keyPress(KeyEvent.VK_D); + else + rob.keyRelease(KeyEvent.VK_D); + end + end end \ No newline at end of file diff --git a/Comm/VRE/GUI_Test_VRE.fig b/Comm/VRE/GUI_Test_VRE.fig index ade2e37e9d459513ee24fe12a15acb45ade0dc9c..a271ace753a6b83f3e22b0ddc86b228102667d4c 100644 GIT binary patch delta 5364 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 S@w^H5f1mTFBEA6HCRWO+0^gqi delta 4519 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 F0T~TrMP&d0 diff --git a/Comm/VRE/GUI_Test_VRE.m b/Comm/VRE/GUI_Test_VRE.m index f82d8e2..8245356 100644 --- a/Comm/VRE/GUI_Test_VRE.m +++ b/Comm/VRE/GUI_Test_VRE.m @@ -22,7 +22,7 @@ % Edit the above text to modify the response to help GUI_Test_VRE -% Last Modified by GUIDE v2.5 07-Dec-2012 13:46:35 +% Last Modified by GUIDE v2.5 22-Oct-2013 11:25:20 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; @@ -73,24 +73,25 @@ function GUI_Test_VRE_OpeningFcn(hObject, eventdata, handles, varargin) varargout{1} = handles.output; -% --- Executes on button press in pb_connect. -function pb_connect_Callback(hObject, eventdata, handles) -% hObject handle to pb_connect (see GCBO) +% --- Executes on button press in pb_startVRE. +function pb_startVRE_Callback(hObject, eventdata, handles) +% hObject handle to pb_startVRE (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) -if(strcmp(get(handles.pb_connect,'String'),'Connect')) - port = str2double(get(handles.et_connection,'String')); - set(handles.t_msg,'String','Waiting for client connection.'); - obj = tcpip('127.0.0.1',port,'NetworkRole','server'); - fopen(obj); - set(handles.t_msg,'String','Server established on port'); - set(handles.pb_connect,'String','Disconnect'); - handles.com = obj; -else - fclose(handles.com); - set(handles.t_msg,'String','Disconnected'); - set(handles.pb_connect,'String','Connect'); -end +% % if(strcmp(get(handles.pb_startVRE,'String'),'Connect')) +% % port = str2double(get(handles.et_connection,'String')); +% % set(handles.t_msg,'String','Waiting for client connection.'); +% % obj = tcpip('127.0.0.1',port,'NetworkRole','server'); +% % fopen(obj); +% % set(handles.t_msg,'String','Server established on port'); +% % set(handles.pb_startVRE,'String','Disconnect'); +% % handles.com = obj; +% % else +% % fclose(handles.com); +% % set(handles.t_msg,'String','Disconnected'); +% % set(handles.pb_startVRE,'String','Connect'); +% % end +handles = ConnectVRE(handles,'Virtual Reality.exe'); guidata(hObject,handles); % --- Executes during object creation, after setting all properties. @@ -105,19 +106,20 @@ function et_connection_CreateFcn(hObject, eventdata, handles) set(hObject,'BackgroundColor','white'); end -% --- Executes on button press in pb_start. -function pb_start_Callback(hObject, eventdata, handles) -% hObject handle to pb_start (see GCBO) +% --- Executes on button press in pb_startARE. +function pb_startARE_Callback(hObject, eventdata, handles) +% hObject handle to pb_startARE (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) -open('Virtual Reality.exe'); +handles = ConnectVRE(handles,'Augmented Reality.exe'); +guidata(hObject,handles); % --- Executes on button press in pb_sendValues. function pb_sendValues_Callback(hObject, eventdata, handles) % hObject handle to pb_sendValues (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) -obj = handles.com; +obj = handles.vre_Com; one = get(handles.tb_value1,'String'); two = get(handles.tb_value2,'String'); three = get(handles.tb_value3,'String'); @@ -250,3 +252,60 @@ function tb_value5_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end + + +% --- Executes on button press in pb_send100. +function pb_send100_Callback(hObject, eventdata, handles) +% hObject handle to pb_send100 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +obj = handles.vre_Com; +one = get(handles.tb_value1,'String'); +two = get(handles.tb_value2,'String'); +three = get(handles.tb_value3,'String'); +four = get(handles.tb_value4,'String'); +five = get(handles.tb_value5,'String'); +pause on; +% VREActivation(obj,four,[],two, three, 0); +tic; +for i = 1:100 + fwrite(obj,sprintf('%c%c%c%c%c',checkValues(one),checkValues(two),checkValues(three),checkValues(four), checkValues(five))); + if handles.returnValues + fread(obj,1); + end +end +set(handles.txt_Time,'String',toc); + + +% --- Executes on button press in pb_return. +function pb_return_Callback(hObject, eventdata, handles) +% hObject handle to pb_return (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +% % if handles.returnValues +% % fwrite(handles.com,sprintf('%c%c%c%c%c','c',char(7),char(0),char(0),char(0))); +% % handles.returnValues = 0; +% % else +% % fwrite(handles.com,sprintf('%c%c%c%c%c','c',char(7),char(1),char(0),char(0))); +% % handles.returnValues = 1; +% % end +handles = DisconnectVRE(handles); +guidata(hObject, handles); + + +% --- If Enable == 'on', executes on mouse press in 5 pixel border. +% --- Otherwise, executes on mouse press in 5 pixel border or over pb_startARE. +function pb_startARE_ButtonDownFcn(hObject, eventdata, handles) +% hObject handle to pb_startARE (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +handles = ConnectVRE(handles,'Augmented Reality.exe'); +guidata(hObject,handles); + +% --- Executes on button press in pb_startAREARM. +function pb_startAREARM_Callback(hObject, eventdata, handles) +% hObject handle to pb_startAREARM (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +handles = ConnectVRE(handles,'Augmented Reality ARM.exe'); +guidata(hObject,handles); diff --git a/Comm/VRE/Ogre.log b/Comm/VRE/Ogre.log index c554b32..aa0c11b 100644 --- a/Comm/VRE/Ogre.log +++ b/Comm/VRE/Ogre.log @@ -1,31 +1,31 @@ -16:33:58: Creating resource group General -16:33:58: Creating resource group Internal -16:33:58: Creating resource group Autodetect -16:33:58: SceneManagerFactory for type 'DefaultSceneManager' registered. -16:33:58: Registering ResourceManager for type Material -16:33:58: Registering ResourceManager for type Mesh -16:33:58: Registering ResourceManager for type Skeleton -16:33:58: MovableObjectFactory for type 'ParticleSystem' registered. -16:33:58: OverlayElementFactory for type Panel registered. -16:33:58: OverlayElementFactory for type BorderPanel registered. -16:33:58: OverlayElementFactory for type TextArea registered. -16:33:58: Registering ResourceManager for type Font -16:33:58: ArchiveFactory for archive type FileSystem registered. -16:33:58: ArchiveFactory for archive type Zip registered. -16:33:58: DDS codec registering -16:33:58: FreeImage version: 3.13.1 -16:33:58: This program uses FreeImage, a free, open source image library supporting all common bitmap formats. See http://freeimage.sourceforge.net for details -16:33:58: Supported formats: bmp,ico,jpg,jif,jpeg,jpe,jng,koa,iff,lbm,mng,pbm,pbm,pcd,pcx,pgm,pgm,png,ppm,ppm,ras,tga,targa,tif,tiff,wap,wbmp,wbm,psd,cut,xbm,xpm,gif,hdr,g3,sgi,exr,j2k,j2c,jp2,pfm,pct,pict,pic,bay,bmq,cr2,crw,cs1,dc2,dcr,dng,erf,fff,hdr,k25,kdc,mdc,mos,mrw,nef,orf,pef,pxn,raf,raw,rdc,sr2,srf,arw,3fr,cine,ia,kc2,mef,nrw,qtk,rw2,sti,drf,dsc,ptx,cap,iiq,rwz -16:33:58: Registering ResourceManager for type HighLevelGpuProgram -16:33:58: Registering ResourceManager for type Compositor -16:33:58: MovableObjectFactory for type 'Entity' registered. -16:33:58: MovableObjectFactory for type 'Light' registered. -16:33:58: MovableObjectFactory for type 'BillboardSet' registered. -16:33:58: MovableObjectFactory for type 'ManualObject' registered. -16:33:58: MovableObjectFactory for type 'BillboardChain' registered. -16:33:58: MovableObjectFactory for type 'RibbonTrail' registered. -16:33:58: OGRE EXCEPTION(6:FileNotFoundException): 'plugins_d.cfg' file not found! in ConfigFile::load at ..\..\..\..\OgreMain\src\OgreConfigFile.cpp (line 83) -16:33:58: plugins_d.cfg not found, automatic plugin loading disabled. -16:33:58: *-*-* OGRE Initialising -16:33:58: *-*-* Version 1.7.2 (Cthugha) -16:33:58: OGRE EXCEPTION(6:FileNotFoundException): 'ogre.cfg' file not found! in ConfigFile::load at ..\..\..\..\OgreMain\src\OgreConfigFile.cpp (line 83) +16:33:58: Creating resource group General +16:33:58: Creating resource group Internal +16:33:58: Creating resource group Autodetect +16:33:58: SceneManagerFactory for type 'DefaultSceneManager' registered. +16:33:58: Registering ResourceManager for type Material +16:33:58: Registering ResourceManager for type Mesh +16:33:58: Registering ResourceManager for type Skeleton +16:33:58: MovableObjectFactory for type 'ParticleSystem' registered. +16:33:58: OverlayElementFactory for type Panel registered. +16:33:58: OverlayElementFactory for type BorderPanel registered. +16:33:58: OverlayElementFactory for type TextArea registered. +16:33:58: Registering ResourceManager for type Font +16:33:58: ArchiveFactory for archive type FileSystem registered. +16:33:58: ArchiveFactory for archive type Zip registered. +16:33:58: DDS codec registering +16:33:58: FreeImage version: 3.13.1 +16:33:58: This program uses FreeImage, a free, open source image library supporting all common bitmap formats. See http://freeimage.sourceforge.net for details +16:33:58: Supported formats: bmp,ico,jpg,jif,jpeg,jpe,jng,koa,iff,lbm,mng,pbm,pbm,pcd,pcx,pgm,pgm,png,ppm,ppm,ras,tga,targa,tif,tiff,wap,wbmp,wbm,psd,cut,xbm,xpm,gif,hdr,g3,sgi,exr,j2k,j2c,jp2,pfm,pct,pict,pic,bay,bmq,cr2,crw,cs1,dc2,dcr,dng,erf,fff,hdr,k25,kdc,mdc,mos,mrw,nef,orf,pef,pxn,raf,raw,rdc,sr2,srf,arw,3fr,cine,ia,kc2,mef,nrw,qtk,rw2,sti,drf,dsc,ptx,cap,iiq,rwz +16:33:58: Registering ResourceManager for type HighLevelGpuProgram +16:33:58: Registering ResourceManager for type Compositor +16:33:58: MovableObjectFactory for type 'Entity' registered. +16:33:58: MovableObjectFactory for type 'Light' registered. +16:33:58: MovableObjectFactory for type 'BillboardSet' registered. +16:33:58: MovableObjectFactory for type 'ManualObject' registered. +16:33:58: MovableObjectFactory for type 'BillboardChain' registered. +16:33:58: MovableObjectFactory for type 'RibbonTrail' registered. +16:33:58: OGRE EXCEPTION(6:FileNotFoundException): 'plugins_d.cfg' file not found! in ConfigFile::load at ..\..\..\..\OgreMain\src\OgreConfigFile.cpp (line 83) +16:33:58: plugins_d.cfg not found, automatic plugin loading disabled. +16:33:58: *-*-* OGRE Initialising +16:33:58: *-*-* Version 1.7.2 (Cthugha) +16:33:58: OGRE EXCEPTION(6:FileNotFoundException): 'ogre.cfg' file not found! in ConfigFile::load at ..\..\..\..\OgreMain\src\OgreConfigFile.cpp (line 83) diff --git a/Comm/limitMovements.def b/Comm/limitMovements.def index 52ad925..2228807 100644 --- a/Comm/limitMovements.def +++ b/Comm/limitMovements.def @@ -1,7 +1,7 @@ -0;* -1;2,7,8,9,10,11,12,13,14,15,16,17,18,19 -2;1,7,8,9,10,11,12,13,14,15,16,17,18,19 -3;4 -4;3 -5;6 +0;* +1;2,7,8,9,10,11,12,13,14,15,16,17,18,19 +2;1,7,8,9,10,11,12,13,14,15,16,17,18,19 +3;4 +4;3 +5;6 6;5 \ No newline at end of file diff --git a/Comm/motors.def b/Comm/motors.def deleted file mode 100644 index 3a62b14..0000000 --- a/Comm/motors.def +++ /dev/null @@ -1,8 +0,0 @@ -0,0,0 -A,0,60 -B,0,60 -C,0,60 -D,0,100 -E,0,100 -K,1,72 -L,1,54 \ No newline at end of file diff --git a/Comm/movements.def b/Comm/movements.def deleted file mode 100644 index cb12416..0000000 --- a/Comm/movements.def +++ /dev/null @@ -1,25 +0,0 @@ -0,Rest,0,0,1 -1,Open Hand,9,1,2 -2,Close Hand,9,0,2 -3,Flex Hand,7,0,1 -4,Ext Hand,7,1,1 -5,Pronation,8,0,3 -6,Supination,8,1,3 -24,Side Grip,13,1,1 -23,Fine Grip,12,1,1 -22,Agree,11,1,1 -17,Point,10,1,1 -7,Thumb Ext,5,1,1 -8,Thumb Flex,5,0,1 -19,Thumb Yaw Ext,6,1,1 -18,Thumb Yaw Flex,6,0,1 -20,Flex Elbow,15,1,4 -21,Ext Elbow,15,0,4 -9,Index Ext,4,1,1 -10,Index Flex,4,0,1 -11,Middle Ext,3,1,1 -12,Middle Flex,3,0,1 -13,Ring Ext,2,1,1 -14,Ring Flex,2,0,1 -15,Little Ext,1,1,1 -16,Little Flex,1,0,1 diff --git a/Control/ActivateMotors.m b/Control/ActivateMotors.m index d70fc1c..a975190 100644 --- a/Control/ActivateMotors.m +++ b/Control/ActivateMotors.m @@ -1,74 +1,74 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Function to update the motors. A motor is paired to 2 movements which make -% the different directions for the motor. If both movements are selected at -% the same time, they are both discarted. -% -% It runs over the lenght of pwmIDs/2 and assigns the corresponding pwm -% value saved in pwmAs and pwmBs. The selected motors to activate are taken -% from outMov. -% -% --------------------------Updates-------------------------- -% 2011-11-17 / Max Ortiz / Creation -% 2012-03-19 / Max Ortiz / Review if the recieved outMov is different from -% the last one, otherwise don't update the motors PWM - - -function ActivateMotors(com, pwmIDs, pwmAs, pwmBs, outMov) - -persistent outMovLast; - -if isempty(outMovLast) - outMovLast = zeros(1,size(pwmIDs,1)); -end - -toutMov = zeros(1,10); - if outMov ~= 0 - toutMov(outMov) = 1; - end -outMov = toutMov; - -if ~strcmp(outMov,outMovLast) - % Go through all PWMS - for i = 1 : 2 : size(pwmIDs) - % Only send the PWMs that have change - if outMov(i) ~= outMovLast(i) || outMov(i+1) ~= outMovLast(i+1) - % Only considered one movement per motor (or nothing) - if xor(outMov(i), outMov(i+1)) - if outMov(i) - pwmA = pwmAs(i); - pwmB = pwmBs(i); - else - pwmA = pwmAs(i+1); - pwmB = pwmBs(i+1); - end - else - pwmA = 0; - pwmB = 0; - end - - % Send motor values - if ~Update2PWMusingSCI(com, pwmIDs(i), pwmA, pwmB) - set(handles.t_msg,'String',['Failed in motor ' pwmID(round(i/2))]); - end - end - end -end - -outMovLast = outMov; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to update the motors. A motor is paired to 2 movements which make +% the different directions for the motor. If both movements are selected at +% the same time, they are both discarted. +% +% It runs over the lenght of pwmIDs/2 and assigns the corresponding pwm +% value saved in pwmAs and pwmBs. The selected motors to activate are taken +% from outMov. +% +% --------------------------Updates-------------------------- +% 2011-11-17 / Max Ortiz / Creation +% 2012-03-19 / Max Ortiz / Review if the recieved outMov is different from +% the last one, otherwise don't update the motors PWM + + +function ActivateMotors(com, pwmIDs, pwmAs, pwmBs, outMov) + +persistent outMovLast; + +if isempty(outMovLast) + outMovLast = zeros(1,size(pwmIDs,1)); +end + +toutMov = zeros(1,10); + if outMov ~= 0 + toutMov(outMov) = 1; + end +outMov = toutMov; + +if ~strcmp(outMov,outMovLast) + % Go through all PWMS + for i = 1 : 2 : size(pwmIDs) + % Only send the PWMs that have change + if outMov(i) ~= outMovLast(i) || outMov(i+1) ~= outMovLast(i+1) + % Only considered one movement per motor (or nothing) + if xor(outMov(i), outMov(i+1)) + if outMov(i) + pwmA = pwmAs(i); + pwmB = pwmBs(i); + else + pwmA = pwmAs(i+1); + pwmB = pwmBs(i+1); + end + else + pwmA = 0; + pwmB = 0; + end + + % Send motor values + if ~Update2PWMusingSCI(com, pwmIDs(i), pwmA, pwmB) + set(handles.t_msg,'String',['Failed in motor ' pwmID(round(i/2))]); + end + end + end +end + +outMovLast = outMov; diff --git a/Control/ApplyControl.m b/Control/ApplyControl.m index 7ee50f9..35f906a 100644 --- a/Control/ApplyControl.m +++ b/Control/ApplyControl.m @@ -1,78 +1,78 @@ -%% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% -% [patRec, outMov] = ApplyControl(patRec, outMov, outVec) -% -% Applies the control algorithm stored inside patRec.control, if no -% controlAlg structure is created, the outMov is passed through to -% the output. -% -% INPUTS: -% -% outMov - is the outMov predicted by the classifier -% -% outVec - is the probabilities of each movement predicted by the -% classifier -% -% OUTPUTS: -% -% patRec - is the updated patRec structure, the control algorithms can -% change e.g. their own properties and the output buffer. Inorder for -% the control algorithms to work as intended, these changes has to be -% stored between trials. -% -% outMov - is the movement outputted from the control algorithm, may -% not be the same as the outMov predicted by the classifier. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-07-18 / Max Ortiz / Creation (moved out from RealtimePatRec) -% -% 2012-10-05 / Joel Falk-Dahlin / Changing from string checking to storing -% a function handle within the controlAlg -% structrue. Allows a single function call -% to execute all controlAlg and no extra -% hard coding is needed to implement new. -% -% Added outVec and handles as extra inputs -% to allow for even more complex control -% algorithms. -% -% 2012-10-19 / Joel Falk-Dahlin / Added outMov = 0 control. -% 2012-11-23 / Joel Falk-Dahlin / Removed handles since speeds now are in -% patRec -% -% 20xx-xx-xx / Author / Comment on update - -function [patRec, outMov] = ApplyControl(patRec, outMov, outVec) - - if isfield(patRec.control,'controlAlg') - [patRec, outMov] = patRec.control.controlAlg.fnc(patRec, outMov, outVec); - end - -%% OLD CODE -% if strcmp(patRec.controlAlg,'Majority vote') -% [patRec outMov] = MajorityVote(patRec,outMov); -% elseif strcmp(patRec.controlAlg,'Buffer output') -% [patRec outMov] = BufferOutput(patRec,outMov); -% elseif strcmp(patRec.controlAlg,'Ramp') -% -% end -end +%% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% [patRec, outMov] = ApplyControl(patRec, outMov, outVec) +% +% Applies the control algorithm stored inside patRec.control, if no +% controlAlg structure is created, the outMov is passed through to +% the output. +% +% INPUTS: +% +% outMov - is the outMov predicted by the classifier +% +% outVec - is the probabilities of each movement predicted by the +% classifier +% +% OUTPUTS: +% +% patRec - is the updated patRec structure, the control algorithms can +% change e.g. their own properties and the output buffer. Inorder for +% the control algorithms to work as intended, these changes has to be +% stored between trials. +% +% outMov - is the movement outputted from the control algorithm, may +% not be the same as the outMov predicted by the classifier. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-07-18 / Max Ortiz / Creation (moved out from RealtimePatRec) +% +% 2012-10-05 / Joel Falk-Dahlin / Changing from string checking to storing +% a function handle within the controlAlg +% structrue. Allows a single function call +% to execute all controlAlg and no extra +% hard coding is needed to implement new. +% +% Added outVec and handles as extra inputs +% to allow for even more complex control +% algorithms. +% +% 2012-10-19 / Joel Falk-Dahlin / Added outMov = 0 control. +% 2012-11-23 / Joel Falk-Dahlin / Removed handles since speeds now are in +% patRec +% +% 20xx-xx-xx / Author / Comment on update + +function [patRec, outMov] = ApplyControl(patRec, outMov, outVec) + + if isfield(patRec.control,'controlAlg') + [patRec, outMov] = patRec.control.controlAlg.fnc(patRec, outMov, outVec); + end + +%% OLD CODE +% if strcmp(patRec.controlAlg,'Majority vote') +% [patRec outMov] = MajorityVote(patRec,outMov); +% elseif strcmp(patRec.controlAlg,'Buffer output') +% [patRec outMov] = BufferOutput(patRec,outMov); +% elseif strcmp(patRec.controlAlg,'Ramp') +% +% end +end \ No newline at end of file diff --git a/Control/ApplyProportionalControl.m b/Control/ApplyProportionalControl.m index 5b25ec3..d894086 100644 --- a/Control/ApplyProportionalControl.m +++ b/Control/ApplyProportionalControl.m @@ -1,79 +1,79 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% patRec = ApplyProportionalControl(tSet,patRec) -% -% Calculates degrees to move based upon the variables stored inside -% patRec.control.propControl. If propControl does not exist, the -% movements are performed at maximum degrees per movement, which is -% stored within patRec.control.maxDegPerMov -% -% INPUTS: -% -% tSet - feature set calculated from one time window, can be -% calculated by SignalProcessing_RealtimePatRec(). -% -% patRec - the pattern recognition structure -% -% OUTPUTS: -% -% patRec - updated pattern recognition structure containing -% patRec.control.currentDegPerMov, which are the degrees to move -% the device with for this particular prediction. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-10-26 / Joel Falk-Dahlin / Creation -% -% 2012-11-06 / Joel Falk-Dahlin / Changed so that features are not -% recalculated, uses tSet vector instead. -% -% 2012-11-23 / Joel Falk-Dahlin / Removed handles since speeds now are in -% patRec -% -% 20xx-xx-xx / Author / Comment on update - -function patRec = ApplyProportionalControl(tSet,patRec) - -% If proportional control is being used, calculate desired speed -if isfield(patRec.control,'propControl') - - % Extract proportional Control variables - propMinThresh = patRec.control.propControl.propMinThresh; - propMaxThresh = patRec.control.propControl.propMaxThresh; - propSpeedMap = patRec.control.propControl.propSpeedMap; - - % Extract tSet indicies - nCh = length(patRec.nCh); - selFeature = patRec.control.propControl.propFeature; - idx = nCh*(selFeature-1)+1:nCh*(selFeature-1)+nCh; - - % Calculate Feature value - featureVal = abs( mean( tSet(idx) ) ); - - % Map Feature value to speed percent - speedPercent = CalculateDesiredSpeedPercent(featureVal, propMinThresh, ... - propMaxThresh, propSpeedMap); - - % Output Speeds - patRec.control.currentDegPerMov = patRec.control.maxDegPerMov.*speedPercent.*0.01; - -% If not proportional control is used, set speed to maximum speed -else - patRec.control.currentDegPerMov = patRec.control.maxDegPerMov; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% patRec = ApplyProportionalControl(tSet,patRec) +% +% Calculates degrees to move based upon the variables stored inside +% patRec.control.propControl. If propControl does not exist, the +% movements are performed at maximum degrees per movement, which is +% stored within patRec.control.maxDegPerMov +% +% INPUTS: +% +% tSet - feature set calculated from one time window, can be +% calculated by SignalProcessing_RealtimePatRec(). +% +% patRec - the pattern recognition structure +% +% OUTPUTS: +% +% patRec - updated pattern recognition structure containing +% patRec.control.currentDegPerMov, which are the degrees to move +% the device with for this particular prediction. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-26 / Joel Falk-Dahlin / Creation +% +% 2012-11-06 / Joel Falk-Dahlin / Changed so that features are not +% recalculated, uses tSet vector instead. +% +% 2012-11-23 / Joel Falk-Dahlin / Removed handles since speeds now are in +% patRec +% +% 20xx-xx-xx / Author / Comment on update + +function patRec = ApplyProportionalControl(tSet,patRec) + +% If proportional control is being used, calculate desired speed +if isfield(patRec.control,'propControl') + + % Extract proportional Control variables + propMinThresh = patRec.control.propControl.propMinThresh; + propMaxThresh = patRec.control.propControl.propMaxThresh; + propSpeedMap = patRec.control.propControl.propSpeedMap; + + % Extract tSet indicies + nCh = length(patRec.nCh); + selFeature = patRec.control.propControl.propFeature; + idx = nCh*(selFeature-1)+1:nCh*(selFeature-1)+nCh; + + % Calculate Feature value + featureVal = abs( mean( tSet(idx) ) ); + + % Map Feature value to speed percent + speedPercent = CalculateDesiredSpeedPercent(featureVal, propMinThresh, ... + propMaxThresh, propSpeedMap); + + % Output Speeds + patRec.control.currentDegPerMov = patRec.control.maxDegPerMov.*speedPercent.*0.01; + +% If not proportional control is used, set speed to maximum speed +else + patRec.control.currentDegPerMov = patRec.control.maxDegPerMov; end \ No newline at end of file diff --git a/Control/BayesianFusion.m b/Control/BayesianFusion.m new file mode 100644 index 0000000..5c5ad14 --- /dev/null +++ b/Control/BayesianFusion.m @@ -0,0 +1,62 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% BayesianFusion control algorithm. This algorithm uses the predicted +% probabilities in outVec in the buffer instead of the predicted outmoves. +% The idea is that EMG signals recorded from windows disjoint in time are +% weakly correlated and the conditional propabilites of each move can be +% described as a product of the probabilities each time. +% +% Not suitable for simulataneous control because it only output one +% movement, the one with highest probability. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-09 / Joel Falk-Dahlin / Creation +% 2013-01-25 / Joel Falk-Dahlin / Changed algorithm to not make +% predictions if classifier did not find +% any movement (pass 'rest' through). +% 20xx-xx-xx / Author / Comment on update + +function [patRec, outMov] = BayesianFusion(patRec, outMov, outVec) + +% Update buffer +patRec.control.outBuffer(1:end-1,:) = patRec.control.outBuffer(2:end,:); +patRec.control.outBuffer(end,:) = outVec; + +% Check if movement is Rest, if not make new prediction (Movement is rest +% if classifier did not find any motion, and no new prediction should be +% made) +if ismember(patRec.nOuts,outMov) && strcmp(patRec.mov{end},'Rest') + outMov = patRec.nOuts; + patRec.control.outBuffer(:) = 0; +else + % Read parameters + k = patRec.control.controlAlg.prop.k; + + % Calculate Joint probabilities + prob = prod(patRec.control.outBuffer + k); + %prob = prob/sum(prob,1); + + % Set outMov to movement with highest joint probability + if max(prob) > 0 + outMov = find( prob == max(prob) )'; + else + outMov = patRec.nOuts; + end +end \ No newline at end of file diff --git a/Control/BufferOutput.m b/Control/BufferOutput.m new file mode 100644 index 0000000..f328f47 --- /dev/null +++ b/Control/BufferOutput.m @@ -0,0 +1,38 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to compute the Majority voting control strategy. It looks at the +% latest prediction and extracts the most common +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-07-03 / Max Ortiz / Creation +% 2012-10-05 / Joel Falk-Dahlin / Changed input/ouputs to match controlAlg standard +% 20xx-xx-xx / Author / Comment on update + +function [patRec, outMov] = BufferOutput(patRec,outMov, outVec) + +% Remove the oldest prediction +patRec.control.outBuffer = patRec.control.outBuffer(2:end,:); +% Add the new prediction +patRec.control.outBuffer(end+1,outMov) = 1; + +%% Compute output +% Not suitable for simltaneous control +outMov = find(0.5 <= sum(patRec.control.outBuffer)./size(patRec.control.outBuffer,1)); + diff --git a/Control/CalculateDesiredSpeedPercent.m b/Control/CalculateDesiredSpeedPercent.m index af54d74..d855037 100644 --- a/Control/CalculateDesiredSpeedPercent.m +++ b/Control/CalculateDesiredSpeedPercent.m @@ -1,89 +1,89 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% -% desiredSpeedPercent = CalculateDesiredSpeedPercent(featureCurrent, featureMin,featureMax, map) -% -% Function to convert a feature value to a desired speed percentage. -% The conversion is done using the threshold values featuresMin, -% featuresMax and the mapping function map. -% -% The value of featureCurrent is mapped to the interval of [0,1] -% where values <= featureMin are mapped to zero and values >= -% featureMax are mapped to 1. The value that is retrieved is -% recalculated using the map function, remapping it to the interval -% [0,1]. -% -% This function is used in proportional control -% -% INPUTS: -% -% featureCurrent - A number, currently the mean value of the selected -% feature to use with proportional control across all channels. -% -% featureMin - The threshold when movement should begin, all values -% below featureMin will be mapped to the value 0. -% -% featureMax - Feature value where maximum speed should be outputted, -% all values above featureMax will be mapped to the value 1. -% -% featureMap - string determining which function to use when mapping -% the value in [0,1] back onto the interval [0,1]. Available options -% are currently; 'linear', 'quad', 'sqrt'. -% -% OUTPUTS: -% -% desiredSpeedPercent - a number from 0 - 100 that gives how many -% percent of the maximum degrees per movement that are to be -% outputted. -% -% --------------------------Updates-------------------------- -% [Contributors are welcome to add their email] -% 2012-10-26 / Joel Falk-Dahlin / Creation -% 20xx-xx-xx / Author / Comment on update - -function desiredSpeedPercent = CalculateDesiredSpeedPercent(featureCurrent, featureMin,featureMax, map) - - if featureCurrent > featureMax - featureCurrent = featureMax; - elseif featureCurrent < featureMin - featureCurrent = featureMin; - end - - % Map into [0,1] interval - xVal = featureCurrent ./ (featureMax - featureMin) - featureMin./(featureMax - featureMin); - - % Now we can use any mapping we want (f(0) should be minimum - % activation percentage (0.0 for threshold), f(1) should be 100 percent (1.0) - - % Linear mapping - if strcmp(map, 'linear') - desiredSpeedPercent = xVal; - % Quadratic mapping - elseif strcmp(map,'quad') - desiredSpeedPercent = xVal.^2; - % Square-root mapping - elseif strcmp(map,'sqrt') - desiredSpeedPercent = sqrt(xVal); - else - desiredSpeedPercent = xVal; - end - - desiredSpeedPercent = desiredSpeedPercent*100; - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% +% desiredSpeedPercent = CalculateDesiredSpeedPercent(featureCurrent, featureMin,featureMax, map) +% +% Function to convert a feature value to a desired speed percentage. +% The conversion is done using the threshold values featuresMin, +% featuresMax and the mapping function map. +% +% The value of featureCurrent is mapped to the interval of [0,1] +% where values <= featureMin are mapped to zero and values >= +% featureMax are mapped to 1. The value that is retrieved is +% recalculated using the map function, remapping it to the interval +% [0,1]. +% +% This function is used in proportional control +% +% INPUTS: +% +% featureCurrent - A number, currently the mean value of the selected +% feature to use with proportional control across all channels. +% +% featureMin - The threshold when movement should begin, all values +% below featureMin will be mapped to the value 0. +% +% featureMax - Feature value where maximum speed should be outputted, +% all values above featureMax will be mapped to the value 1. +% +% featureMap - string determining which function to use when mapping +% the value in [0,1] back onto the interval [0,1]. Available options +% are currently; 'linear', 'quad', 'sqrt'. +% +% OUTPUTS: +% +% desiredSpeedPercent - a number from 0 - 100 that gives how many +% percent of the maximum degrees per movement that are to be +% outputted. +% +% --------------------------Updates-------------------------- +% [Contributors are welcome to add their email] +% 2012-10-26 / Joel Falk-Dahlin / Creation +% 20xx-xx-xx / Author / Comment on update + +function desiredSpeedPercent = CalculateDesiredSpeedPercent(featureCurrent, featureMin,featureMax, map) + + if featureCurrent > featureMax + featureCurrent = featureMax; + elseif featureCurrent < featureMin + featureCurrent = featureMin; + end + + % Map into [0,1] interval + xVal = featureCurrent ./ (featureMax - featureMin) - featureMin./(featureMax - featureMin); + + % Now we can use any mapping we want (f(0) should be minimum + % activation percentage (0.0 for threshold), f(1) should be 100 percent (1.0) + + % Linear mapping + if strcmp(map, 'linear') + desiredSpeedPercent = xVal; + % Quadratic mapping + elseif strcmp(map,'quad') + desiredSpeedPercent = xVal.^2; + % Square-root mapping + elseif strcmp(map,'sqrt') + desiredSpeedPercent = sqrt(xVal); + else + desiredSpeedPercent = xVal; + end + + desiredSpeedPercent = desiredSpeedPercent*100; + end \ No newline at end of file diff --git a/Control/CombinedControl.m b/Control/CombinedControl.m new file mode 100644 index 0000000..e27cd46 --- /dev/null +++ b/Control/CombinedControl.m @@ -0,0 +1,13 @@ +function [patRec, outMov, handles] = CombinedControl(patRec, outMov, outVec, handles) + + % Apply first controller + p = patRec.control.controlAlg.prop.patRecOne; + [patRecOut, outMov] = ApplyControl(p, outMov, outVec); + patRec.controlAlg.prop.patRecOne = patRecOut; + + % Apply second controller + p = patRec.control.controlAlg.prop.patRecTwo; + [patRecOut, outMov] = ApplyControl(p, outMov, outVec); + patRec.control.controlAlg.prop.patRecTwo = patRecOut; + +end \ No newline at end of file diff --git a/Control/GUI_ControlParameters.m b/Control/GUI_ControlParameters.m index 7c0217b..bca6bb9 100644 --- a/Control/GUI_ControlParameters.m +++ b/Control/GUI_ControlParameters.m @@ -1,178 +1,178 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% -% GUI to get quick access to control algorithm parameters. Called from -% GUI_TestPatRec_Mov2Mov when options-button is clicked. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-10-05 / Joel Falk-Dahlin / Creation -% 2012-10-08 / Joel Falk-Dahlin / Added correct buffer setting from - % internal property (.prop.bufferSize) and - % not only from parameter - % (.parameter.bufferSize) allowing for - % different types of controlAlgs. -% 2012-10-10 / Joel Falk-Dahlin / Added check of previously init outBuffer - % This allows for external setting of - % outBuffer from InitFile. This way the - % outBuffer can be set to have any ammount - % of elements, not only patRec.nOuts -% 20xx-xx-xx / Author / Comment on update - -function figureH = GUI_ControlParameters(inObj) - -% Read data from MainGUI -handles = guidata(inObj); - -if isfield(handles,'patRec') - if isfield(handles.patRec.control,'controlAlg') - - % Create GUI-window - figureH = figure; - - % Set up Vars used by GUI-code - patRec = handles.patRec; - parameterNames = fieldnames(patRec.control.controlAlg.parameters); - nParameters = size(parameterNames,1); - - paramValBoxes = zeros(1,nParameters); - - % Set up position and sizes for the different GUI-fields - xPosNameBox = 20; - yPosNameBox = 70; - xSizeNameBox = 150; - ySizeNameBox = 20; - - xPosValBox = xPosNameBox + xSizeNameBox + 10; - yPosValBox = yPosNameBox; - xSizeValBox = 150; - ySizeValBox = ySizeNameBox; - - % Create UI objects for the parameters - for i = 1:nParameters - - % Create UI-name boxes from the parameters - uicontrol('Style', 'text', 'String', parameterNames{i},... - 'Position', [xPosNameBox yPosNameBox+(i-1)*ySizeNameBox, ... - xSizeNameBox ySizeNameBox]); - - % Save value of the parameters - parameterValue = patRec.control.controlAlg.parameters.(parameterNames{i}); - - % Create UI-value boxes from the parameters - paramValBoxes(i) = uicontrol('Style', 'edit', 'String', parameterValue,... - 'Position', [xPosValBox yPosValBox+(i-1)*ySizeValBox, ... - xSizeValBox ySizeValBox],... - 'Callback', {@SetParam,inObj}, ... - 'Tag',parameterNames{i}); - end - - % Create a UI-title - title = uicontrol('Style', 'text', 'String', ['Parameters for ', patRec.control.controlAlg.name{1}],... - 'Position', [(xPosNameBox+xPosValBox)/2 yPosNameBox+(i+1)*ySizeNameBox, ... - 250 80], ... - 'Tag', 'Title'); - - % Set title fontsize - set(title,'FontSize',15); - - % Create OK-button - uicontrol('Style', 'pushbutton', 'String', 'OK',... - 'Position', [xPosValBox+xSizeValBox+20 yPosValBox 50 40], ... - 'Callback', {@ExitGUI,inObj,paramValBoxes}); - - % Create Default-button - uicontrol('Style', 'pushbutton', 'String', 'Default',... - 'Position', [xPosValBox+xSizeValBox+20 yPosValBox+50 50 40], ... - 'Callback', {@SetDefault,inObj}); - - % Resize the GUI-window - figurePos = get(figureH,'Position'); - figurePos(3:4) = [xSizeNameBox+xSizeValBox+50+2*xPosNameBox+20,... - yPosNameBox+50+yPosNameBox+(i+1)*ySizeNameBox]; - set(figureH,'Position',figurePos,'MenuBar','none'); - - else - % If patRec does not contain any control algorithm print message - set(handles.t_msg,'String','No Control Algorithm set'); - return - end -else - % If the MainGUI-data does not contain any patRec print message - set(handles.t_msg,'String','No PatRec Loaded'); - return -end - -end - -% -------------- GUI CALLBACK FUNCTIONS ------------------ - -function SetParam(hObj, event, inObj) -% -------------- Function Description -------------------- -% Activated when enter press in any of the value fields, saves the value -% in the field to the patRec and saves patrec to mainGUI data. -%--------------------------------------------------------- - handles = guidata(inObj); - if ~isnan( str2double(get(hObj,'String') ) ) - handles.patRec.control.controlAlg.parameters.(get(hObj,'Tag')) = str2double(get(hObj,'String')); - else - handles.patRec.control.controlAlg.parameters.(get(hObj,'Tag')) = get(hObj,'String'); - end - guidata(inObj,handles); -end - -function ExitGUI(hObj, event, inObj,paramValBoxes) -% -------------- FUNCTION DESCRIPTION ---------------- -% Activated when OK-button is pressed. Sets values of valuefield and -% exits GUI window. -% ---------------------------------------------------- - - - % Set Parameters to current value of valueboxes, precaution if - % someone forgets to press ENTER when changing a value - for i = 1:size(paramValBoxes,2) - SetParam(paramValBoxes(i),[],inObj); - end - - % Update the handles so they exists with current instance - handles = guidata(inObj); - - % Update buffers using current parameters - handles.patRec = ReInitControl(handles.patRec); - - % Save PatRec with updated buffer size - guidata(inObj, handles); - - % Close the Parameter GUI - close(clf); -end - -function SetDefault(hObj,event,inObj) -% ---------------- Function Description -------------------------- -% Activates when Default-button is pressed, reinitilizes the controlAlg -% from the definition file, saves to MainGUI and closes and reopen -% parameter GUIwindow. -% ----------------------------------------------------------------- - handles = guidata(inObj); - patRec = handles.patRec; - handles.patRec = InitControl_new(patRec,patRec.control.controlAlg.name{1}); - guidata(inObj,handles); - close(clf); - GUI_ControlParameters(inObj); -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% GUI to get quick access to control algorithm parameters. Called from +% GUI_TestPatRec_Mov2Mov when options-button is clicked. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-05 / Joel Falk-Dahlin / Creation +% 2012-10-08 / Joel Falk-Dahlin / Added correct buffer setting from + % internal property (.prop.bufferSize) and + % not only from parameter + % (.parameter.bufferSize) allowing for + % different types of controlAlgs. +% 2012-10-10 / Joel Falk-Dahlin / Added check of previously init outBuffer + % This allows for external setting of + % outBuffer from InitFile. This way the + % outBuffer can be set to have any ammount + % of elements, not only patRec.nOuts +% 20xx-xx-xx / Author / Comment on update + +function figureH = GUI_ControlParameters(inObj) + +% Read data from MainGUI +handles = guidata(inObj); + +if isfield(handles,'patRec') + if isfield(handles.patRec.control,'controlAlg') + + % Create GUI-window + figureH = figure; + + % Set up Vars used by GUI-code + patRec = handles.patRec; + parameterNames = fieldnames(patRec.control.controlAlg.parameters); + nParameters = size(parameterNames,1); + + paramValBoxes = zeros(1,nParameters); + + % Set up position and sizes for the different GUI-fields + xPosNameBox = 20; + yPosNameBox = 70; + xSizeNameBox = 150; + ySizeNameBox = 20; + + xPosValBox = xPosNameBox + xSizeNameBox + 10; + yPosValBox = yPosNameBox; + xSizeValBox = 150; + ySizeValBox = ySizeNameBox; + + % Create UI objects for the parameters + for i = 1:nParameters + + % Create UI-name boxes from the parameters + uicontrol('Style', 'text', 'String', parameterNames{i},... + 'Position', [xPosNameBox yPosNameBox+(i-1)*ySizeNameBox, ... + xSizeNameBox ySizeNameBox]); + + % Save value of the parameters + parameterValue = patRec.control.controlAlg.parameters.(parameterNames{i}); + + % Create UI-value boxes from the parameters + paramValBoxes(i) = uicontrol('Style', 'edit', 'String', parameterValue,... + 'Position', [xPosValBox yPosValBox+(i-1)*ySizeValBox, ... + xSizeValBox ySizeValBox],... + 'Callback', {@SetParam,inObj}, ... + 'Tag',parameterNames{i}); + end + + % Create a UI-title + title = uicontrol('Style', 'text', 'String', ['Parameters for ', patRec.control.controlAlg.name{1}],... + 'Position', [(xPosNameBox+xPosValBox)/2 yPosNameBox+(i+1)*ySizeNameBox, ... + 250 80], ... + 'Tag', 'Title'); + + % Set title fontsize + set(title,'FontSize',15); + + % Create OK-button + uicontrol('Style', 'pushbutton', 'String', 'OK',... + 'Position', [xPosValBox+xSizeValBox+20 yPosValBox 50 40], ... + 'Callback', {@ExitGUI,inObj,paramValBoxes}); + + % Create Default-button + uicontrol('Style', 'pushbutton', 'String', 'Default',... + 'Position', [xPosValBox+xSizeValBox+20 yPosValBox+50 50 40], ... + 'Callback', {@SetDefault,inObj}); + + % Resize the GUI-window + figurePos = get(figureH,'Position'); + figurePos(3:4) = [xSizeNameBox+xSizeValBox+50+2*xPosNameBox+20,... + yPosNameBox+50+yPosNameBox+(i+1)*ySizeNameBox]; + set(figureH,'Position',figurePos,'MenuBar','none'); + + else + % If patRec does not contain any control algorithm print message + set(handles.t_msg,'String','No Control Algorithm set'); + return + end +else + % If the MainGUI-data does not contain any patRec print message + set(handles.t_msg,'String','No PatRec Loaded'); + return +end + +end + +% -------------- GUI CALLBACK FUNCTIONS ------------------ + +function SetParam(hObj, event, inObj) +% -------------- Function Description -------------------- +% Activated when enter press in any of the value fields, saves the value +% in the field to the patRec and saves patrec to mainGUI data. +%--------------------------------------------------------- + handles = guidata(inObj); + if ~isnan( str2double(get(hObj,'String') ) ) + handles.patRec.control.controlAlg.parameters.(get(hObj,'Tag')) = str2double(get(hObj,'String')); + else + handles.patRec.control.controlAlg.parameters.(get(hObj,'Tag')) = get(hObj,'String'); + end + guidata(inObj,handles); +end + +function ExitGUI(hObj, event, inObj,paramValBoxes) +% -------------- FUNCTION DESCRIPTION ---------------- +% Activated when OK-button is pressed. Sets values of valuefield and +% exits GUI window. +% ---------------------------------------------------- + + + % Set Parameters to current value of valueboxes, precaution if + % someone forgets to press ENTER when changing a value + for i = 1:size(paramValBoxes,2) + SetParam(paramValBoxes(i),[],inObj); + end + + % Update the handles so they exists with current instance + handles = guidata(inObj); + + % Update buffers using current parameters + handles.patRec = ReInitControl(handles.patRec); + + % Save PatRec with updated buffer size + guidata(inObj, handles); + + % Close the Parameter GUI + close(clf); +end + +function SetDefault(hObj,event,inObj) +% ---------------- Function Description -------------------------- +% Activates when Default-button is pressed, reinitilizes the controlAlg +% from the definition file, saves to MainGUI and closes and reopen +% parameter GUIwindow. +% ----------------------------------------------------------------- + handles = guidata(inObj); + patRec = handles.patRec; + handles.patRec = InitControl_new(patRec,patRec.control.controlAlg.name{1}); + guidata(inObj,handles); + close(clf); + GUI_ControlParameters(inObj); +end diff --git a/Control/GUI_ProportionalControl.fig b/Control/GUI_ProportionalControl.fig new file mode 100644 index 0000000000000000000000000000000000000000..5ff6ef75b1543780e1b220b152c0b98361dcba5d GIT binary patch literal 10596 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 pdr-(B+C#o<6G= (patRec.sF*patRec.tW) + + % Use only data size equal to the trained time window + tData = tempData(end-patRec.sF*patRec.tW+1:end,:); + + % Signal processing, to calculate signal feature vector + tSet = SignalProcessing_RealtimePatRec(tData, patRec); + + % Extract proportional Control variables from GUI textboxes + propMinThresh = str2double( get(handles.et_minValue,'String') ); + propMaxThresh = str2double( get(handles.et_thresholdValue,'String') ); + propSpeedMap = patRec.control.propControl.propSpeedMap; + + % Extract tSet indicies + nCh = length(patRec.nCh); + selFeature = get(handles.pm_featureSelection,'Value'); + idx = nCh*(selFeature-1)+1:nCh*(selFeature-1)+nCh; + + % Calculate Feature value + featureVal = abs( mean( tSet(idx) ) ); + + % Map Feature value to speed percent + speedPercent = CalculateDesiredSpeedPercent(featureVal, propMinThresh, ... + propMaxThresh, propSpeedMap); + + % Plot speed and feature bars + yLimVal = get(handles.featureAxes,'YLim'); + yLimVal = yLimVal(2); + if featureVal > yLimVal + set(handles.featureAxes,'YLim',[0 featureVal]) + end + set(handles.featurePatch,'YData',[0 0 featureVal featureVal]); + set(handles.speedPatch,'YData',[0 0 speedPercent speedPercent]); + + % If the main gui is open, predicted movement and update mainGUI + if ~isempty(handlesMainGUI) + + % Signal processing + tSet = SignalProcessing_RealtimePatRec(tData, handles.patRec); + % One shoot PatRec + outMov = OneShotPatRecClassifier(handles.patRec, tSet); + + % If no movement is predicted, predict rest + if outMov == 0 + outMov = handles.patRec.nOuts; + end + + % Draw predicted movement + set(handlesMainGUI.lb_movements,'Value',outMov); + drawnow; + end + +end + +% Save the tempData back to the GUI so it can be used next time DAQ fires +% DataAvailable event +setappdata(hObject,'tempData',tempData); + +% --- Executes during object creation, after setting all properties. +function t_speed_CreateFcn(hObject, eventdata, handles) +% hObject handle to t_speed (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + + +% --- Executes on selection change in pm_speedMap. +function pm_speedMap_Callback(hObject, eventdata, handles) +% hObject handle to pm_speedMap (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns pm_speedMap contents as cell array +% contents{get(hObject,'Value')} returns selected item from pm_speedMap +speedMapList = get(hObject,'String'); +val = get(hObject,'Value'); +map = speedMapList{val}; +map = strtrim( map ); + +handles.patRec.control.propControl.propSpeedMap = map; +guidata(hObject,handles); + +% If MainGUI exists, save selected map to handlesMainGUI +if isfield(handles,'mainGUI') + handlesMainGUI = guidata(handles.mainGUI); + handlesMainGUI.patRec.control.propControl.propSpeedMap = map; + guidata(handles.mainGUI, handlesMainGUI); +end + +% --- Executes during object creation, after setting all properties. +function pm_speedMap_CreateFcn(hObject, eventdata, handles) +% hObject handle to pm_speedMap (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_minValue_Callback(hObject, eventdata, handles) +% hObject handle to et_minValue (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_minValue as text +% str2double(get(hObject,'String')) returns contents of et_minValue as a double + +% Save selected values to PropGUI handles +set(handles.t_msg,'String',''); +handles.patRec.control.propControl.propMinThresh = str2double(get(handles.et_minValue,'String')); +guidata(hObject,handles); + +if isfield(handles,'mainGUI') + handlesMainGUI = guidata(handles.mainGUI); + handlesMainGUI.patRec.control.propControl.propMinThresh = str2double(get(handles.et_minValue,'String')); + guidata(handles.mainGUI, handlesMainGUI); +end + +% --- Executes during object creation, after setting all properties. +function et_minValue_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_minValue (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_testingTime_Callback(hObject, eventdata, handles) +% hObject handle to et_testingTime (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_testingTime as text +% str2double(get(hObject,'String')) returns contents of et_testingTime as a double + + +% --- Executes during object creation, after setting all properties. +function et_testingTime_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_testingTime (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end \ No newline at end of file diff --git a/Control/GUI_Sensors.fig b/Control/GUI_Sensors.fig new file mode 100644 index 0000000000000000000000000000000000000000..2b62b784984cebccc5f9f36a664211f2939ec527 GIT binary patch literal 4950 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 200) + handles.t = 1; + else + handles.t = t + 1; + end + + handles.data = data; + +% plot(handles.axes1, data(:,1)); +% plot(handles.axes1, data(:,2)); +% plot(handles.axes1, data(:,3)); +% plot(handles.axes1, data(:,4)); + + plotGain = handles.plotGain; + + K = ampPP/(2*(max(max(abs(data))))); + if K < plotGain + % if the signals in the different windows is getting bigger the gain + % must be reduced consequently, the channels plots must always fit + % the main plot + plotGain = K; + end + % plot a new tWs sized window + for j = 1 : handles.nrSensors + set(p_t0(j),'YData',data(:,j)*plotGain + offVector(j)); % add offsets to plot channels in same graph + end + drawnow + + handles.plotGain = plotGain; + guidata(handles.guifig, handles); + + +% --- Executes on selection change in listbox. +function listbox_Callback(hObject, eventdata, handles) +% hObject handle to listbox (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns listbox contents as cell array +% contents{get(hObject,'Value')} returns selected item from listbox + + +% --- Executes during object creation, after setting all properties. +function listbox_CreateFcn(hObject, eventdata, handles) +% hObject handle to listbox (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: listbox controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end diff --git a/Control/GUI_SingleMotorTest.fig b/Control/GUI_SingleMotorTest.fig new file mode 100644 index 0000000000000000000000000000000000000000..c114f9ffb7c2ad96c6aca43a8cf4cd2b7903ea2a GIT binary patch literal 30671 zcma&NWl$W^^8bxn5+Fzj!6CRyaF^ijZoz`PLvVKj2_7T_cb6r&FYd7T0t+m@EQ|be zfA>~BZ=ds`Yo{qjypRb9#Cymo_9Z|J@*7X2pg!_***Wx-@I(){I;!zW0fI4@Wu+4yA` z(jYcZemsDCen|F@_ZL9qAj{nY0QEkSMkqUAT=B2KP( ztu9olnBS@%E#}2uFkiksc+YJi(4bv1O4u|h&mXPUZl#mhWMwagv3A5=S|3RlU%U5N za#rp6m+5ILSMecw5RV_kTKP(X9Mu#qwH~Z9+q4TJ#`DA=sDB+bG`4EEi=SiiR;_&| zh4HW)G?QXiBOT1 zXe}2jv*EueUC;K?q0&H^-1{PLOJoZuoF`8ob8K_5*vg>mNaO_;8P0<_AV+YEQ- zuRo!(f7CDeiKeyD1QN09i!(SDNb5c@3d-_W(l5dM*whbTdCctxum-8`NDyu5-S_t6 z5ry1m?qEh2tVdJ8UW>GB21bFO)gg;jFD+Ef=FNr8pk}HT^OnLE5DW+SM76_s@WP16 zzzTGI&D7n&J5bC1rNMA_|_E#Sq99%U!z@W%nME z?>=r`yeYlM_{KOnYGhZ9({(*Dciyw!_1dS7H9cTLm>Q4oC|KM=PO$Ex-DYi3&1Eiy zZoD5Qtqv8C3U0T)BhFWSi>R~1n8d0ebkf-WsLsQb17WOSE4TDWKpNK zU__hqF5a=yyl7M^_4qLCw=lN-dhC!`r16Ma)!5A2xD$`)p6H176U7yVCL00bp6E*4 zhUG!<+d{rpv}H}zR(%?JD$zwo@A#N#pUWRd&?)m6q9Mlcj-kM$NP%)gSvv6sZ+fW*sCL&PL

)prDJU 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 0.9 % Safety routing + StopMotor(dio,ao); + end + mRIy(i) = motorRI(i); + set(handles.et_motorRI,'String',num2str(motorRI(i))); + set(p_motorRI,'YData',mRIy); + set(p_motorI,'YData',datam); + set(p_PWM,'YData',datapwm); + drawnow; + + + % Signal Characteristics for Amplitud + treated_data.trdata = analyze_signal(data(:,1:4),Fs,gcIdx); % Only Breakdown the Biosignals + allChrs = fieldnames(treated_data.trdata); + Chr = allChrs(ncIdx); + pv(:,i) = treated_data.trdata.(Chr{1}); + % Graphics + set(p_schr(1),'YData',pv(1,:)); + set(p_schr(2),'YData',pv(2,:)); + set(p_schr(3),'YData',pv(3,:)); + set(p_schr(4),'YData',pv(4,:)); + + if get(handles.cb_ann,'Value') + % filtering + if ANN.filters.PLH + data = BSbutterPLHarmonics(Fs, data); + elseif ANN.filters.BP + data = FilterBP_EMG(Fs, data); + end + treated_data.trdata = analyze_signal(data,Fs,cIdx); + trset = get_Sets(treated_data,ANN.Chrs); + + % Normalize + if ANN.normalize == 1 + allsets = get(handles.t_allsets,'UserData'); + allsets(length(allsets(:,1))+1 : length(allsets(:,1))+length(trset(:,1)),:) = trset; + mn = mean(allsets,1); + vr = var(allsets); + trset = (trset - mn(ones(size(trset,1), 1), :)) ./ vr(ones(size(trset,1), 1), :); + end + + ANN = evaluate_ann(trset, ANN); + + if round(ANN.o(1)) == 1 && round(ANN.o(2)) == 0 + dir = [0 1]; % 1 Open + csch = 2; + set(handles.t_msgdir,'String','Open'); + elseif round(ANN.o(1)) == 0 && round(ANN.o(2)) == 1 + dir = [1 0]; % 2 Close + csch = 1; + set(handles.t_msgdir,'String','Close'); + else + dir = [1 1]; % 2 Close + set(handles.t_msgdir,'String','Do nothing'); + end + + else + % Automatic direction + if get(handles.cb_ch1and2,'Value') % CH 1 and CH 2 Natural + if pv(2,i) > pv(1,i) + dir = [0 1]; % 1 Open + csch = 2; + set(handles.t_msgdir,'String','Open'); + else + dir = [1 0]; % 2 Close + csch = 1; + set(handles.t_msgdir,'String','Close'); + end + elseif get(handles.cb_ch2and3,'Value') % CH 2 and CH 3 Usual + if pv(2,i) > pv(3,i) + dir = [0 1]; % 1 Open + csch = 2; + set(handles.t_msgdir,'String','Open'); + else + dir = [1 0]; % 2 Close + csch = 3; + set(handles.t_msgdir,'String','Close'); + end + end + end + + % Scaling: First range is how you want the output, seconde range is where the raw-data comes + Amin = chmin(csch); + Amax = chmax(csch); + cv(i) = scale(pv(csch,i),0,100,Amin,Amax); + if cv(i) > 0 % Set direction only if there is control planned + putvalue(dio.Line([1 2]), dir); + else + putvalue(dio.Line([1 2]), [1 1]); + end + set(p_controls,'YData',cv); + + % Error + smotorRI = scale(motorRI(i),0,100,errmRImin,errmRImax); + ev(i) = cv(i) - smotorRI; % 1 Amp = 100% + set(p_error,'YData',ev); + + + % Scaling output 1 - 4 V and control signal from 0 - 100; + cvA(i) = scale(cv(i),Aoutmin,Aoutmax,0,100); + if get(handles.cb_linkcontrols,'Value') + putsample(ao,cvA(i)); % Exit according to slider + end + + i=i+1; + end + + [data t] = getdata(ai); + data(:,mIIdx) = data(:,mIIdx)./ str2double(get(handles.et_senseR,'String')); % change from V to I + set(handles.t_t,'UserData',t); + + set(handles.pb_pmotorRI,'UserData',motorRI); + + motorI = data(:,mIIdx); + set(p_motorI,'XData',t); + set(p_motorI,'YData',motorI); + set(handles.pb_pmotorI,'UserData',motorI); + + PWM = data(:,PWMIdx); + set(p_PWM,'XData',t); + set(p_PWM,'YData',PWM); + set(handles.pb_pPWM,'UserData',PWM); + + set(handles.pb_pschr,'UserData',pv); + set(handles.pb_pcontrols,'UserData',cv); + set(handles.pb_pcontrolsA,'UserData',cvA); + set(handles.pb_perror,'UserData',ev); + + StopMotor(dio,ao); % End by stoping the motor + + +% --- Executes on button press in pb_stopDAQ. +function pb_stopDAQ_Callback(hObject, eventdata, handles) +% hObject handle to pb_stopDAQ (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + ai = get(handles.t_ai,'UserData'); + ao = get(handles.t_ao,'UserData'); + dio = get(handles.t_dio,'UserData'); + + stop(ai); + stop(ao); + stop(dio); + + + +% --- Executes on slider movement. +function s_speed_Callback(hObject, eventdata, handles) +% hObject handle to s_speed (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'Value') returns position of slider +% get(hObject,'Min') and get(hObject,'Max') to determine range of slider + + set(handles.et_speed,'String',num2str(get(hObject,'Value'))); + ao = get(handles.t_ao,'UserData'); + putsample(ao,get(hObject,'Value')); % Exit according to slider + + +% --- Executes during object creation, after setting all properties. +function s_speed_CreateFcn(hObject, eventdata, handles) +% hObject handle to s_speed (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: slider controls usually have a light gray background. +if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor',[.9 .9 .9]); +end + + + +function et_speed_Callback(hObject, eventdata, handles) +% hObject handle to et_speed (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_speed as text +% str2double(get(hObject,'String')) returns contents of et_speed as a double + + set(handles.s_speed,'Value',str2double(get(hObject,'String'))); + ao = get(handles.t_ao,'UserData'); + putsample(ao,get(handles.s_speed,'Value')); % Exit according to slider + +% --- Executes during object creation, after setting all properties. +function et_speed_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_speed (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_motordir_Callback(hObject, eventdata, handles) +% hObject handle to et_motordir (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_motordir as text +% str2double(get(hObject,'String')) returns contents of et_motordir as a double + + +% --- Executes during object creation, after setting all properties. +function et_motordir_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_motordir (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + +function motordir_SelectionChangeFcn(hObject, eventdata) + +%retrieve GUI data, i.e. the handles structure +handles = guidata(hObject); + +dio = get(handles.t_dio,'UserData'); + +switch get(eventdata.NewValue,'Tag') % Get Tag of selected object + case 'rb_stop' + set(handles.et_motordir,'String','3'); + putvalue(dio.Line([1 2]), [1 1]); % Stop motor + + case 'rb_close' + set(handles.et_motordir,'String','2'); + putvalue(dio.Line([1 2]), [1 0]); + + case 'rb_open' + set(handles.et_motordir,'String','1'); + putvalue(dio.Line([1 2]), [0 1]); + + otherwise + set(handles.et_motordir,'String','3'); + putvalue(dio.Line([1 2]), [1 1]); +end +%updates the handles structure +guidata(hObject, handles); + + +% --- Executes on button press in pb_pmotorI. +function pb_pmotorI_Callback(hObject, eventdata, handles) + figure; + plot(get(handles.t_t,'UserData'),get(handles.pb_pmotorI,'UserData')); + title('Motor Current (I)') + + +% --- Executes on button press in pb_pPWM. +function pb_pPWM_Callback(hObject, eventdata, handles) + figure; + plot(get(handles.t_t,'UserData'),get(handles.pb_pPWM,'UserData')); + title('PWM') + + +% --- Executes on button press in pb_pmotorV. +function pb_pmotorV_Callback(hObject, eventdata, handles) +% hObject handle to pb_pmotorV (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + + + +function et_motorRI_Callback(hObject, eventdata, handles) + + +% --- Executes during object creation, after setting all properties. + +function et_motorRI_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_motorRI (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on button press in pb_pmotorRI. +function pb_pmotorRI_Callback(hObject, eventdata, handles) + figure; + plot(get(handles.pb_pmotorRI,'UserData')); + title('Motor R Current') + + + +function et_senseR_Callback(hObject, eventdata, handles) +% hObject handle to et_senseR (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_senseR as text +% str2double(get(hObject,'String')) returns contents of et_senseR as a double + + +% --- Executes during object creation, after setting all properties. +function et_senseR_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_senseR (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on button press in pb_pcontrols. +function pb_pcontrols_Callback(hObject, eventdata, handles) + figure; + plot(get(handles.pb_pcontrols,'UserData')); + title('Control Signal') + + +% --- Executes on button press in pb_pschr. +function pb_pschr_Callback(hObject, eventdata, handles) + figure; + plot(get(handles.pb_pschr,'UserData')'); + title('Signal Characteristic') + legend('1','2','3','4') + + + +function et_ch0min_Callback(hObject, eventdata, handles) +% hObject handle to et_ch0min (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_ch0min as text +% str2double(get(hObject,'String')) returns contents of et_ch0min as a double + + +% --- Executes during object creation, after setting all properties. +function et_ch0min_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_ch0min (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_ch0max_Callback(hObject, eventdata, handles) +% hObject handle to et_ch0max (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_ch0max as text +% str2double(get(hObject,'String')) returns contents of et_ch0max as a double + + +% --- Executes during object creation, after setting all properties. +function et_ch0max_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_ch0max (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on button press in cb_ch0. +function cb_ch0_Callback(hObject, eventdata, handles) +% hObject handle to cb_ch0 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of cb_ch0 + + +% --- Executes on button press in cb_ch1. +function cb_ch1_Callback(hObject, eventdata, handles) +% hObject handle to cb_ch1 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of cb_ch1 + + +% --- Executes on button press in cb_ch2. +function cb_ch2_Callback(hObject, eventdata, handles) +% hObject handle to cb_ch2 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of cb_ch2 + + +% --- Executes on button press in cb_ch3. +function cb_ch3_Callback(hObject, eventdata, handles) +% hObject handle to cb_ch3 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of cb_ch3 + + +% --- Executes on button press in pb_perror. +function pb_perror_Callback(hObject, eventdata, handles) + figure; + plot(get(handles.pb_perror,'UserData')); + title('Error') + + +% --- Executes on selection change in pm_chrs. +function pm_chrs_Callback(hObject, eventdata, handles) +% hObject handle to pm_chrs (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = get(hObject,'String') returns pm_chrs contents as cell array +% contents{get(hObject,'Value')} returns selected item from pm_chrs + + +% --- Executes during object creation, after setting all properties. +function pm_chrs_CreateFcn(hObject, eventdata, handles) +% hObject handle to pm_chrs (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on button press in cb_linkcontrols. +function cb_linkcontrols_Callback(hObject, eventdata, handles) +% hObject handle to cb_linkcontrols (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of cb_linkcontrols + + +% --- Executes on button press in pb_pcontrolsA. +function pb_pcontrolsA_Callback(hObject, eventdata, handles) + figure; + plot(get(handles.pb_pcontrolsA,'UserData')); + title('Analog Control Signal') + + + +function et_Aoutmin_Callback(hObject, eventdata, handles) +% hObject handle to et_Aoutmin (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_Aoutmin as text +% str2double(get(hObject,'String')) returns contents of et_Aoutmin as a double + + +% --- Executes during object creation, after setting all properties. +function et_Aoutmin_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_Aoutmin (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_Aoutmax_Callback(hObject, eventdata, handles) +% hObject handle to et_Aoutmax (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_Aoutmax as text +% str2double(get(hObject,'String')) returns contents of et_Aoutmax as a double + + +% --- Executes during object creation, after setting all properties. +function et_Aoutmax_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_Aoutmax (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_errmRImin_Callback(hObject, eventdata, handles) +% hObject handle to et_errmRImin (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_errmRImin as text +% str2double(get(hObject,'String')) returns contents of et_errmRImin as a double + + +% --- Executes during object creation, after setting all properties. +function et_errmRImin_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_errmRImin (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_errmRImax_Callback(hObject, eventdata, handles) +% hObject handle to et_errmRImax (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_errmRImax as text +% str2double(get(hObject,'String')) returns contents of et_errmRImax as a double + + +% --- Executes during object creation, after setting all properties. +function et_errmRImax_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_errmRImax (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_ch1min_Callback(hObject, eventdata, handles) +% hObject handle to et_ch1min (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_ch1min as text +% str2double(get(hObject,'String')) returns contents of et_ch1min as a double + + +% --- Executes during object creation, after setting all properties. +function et_ch1min_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_ch1min (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_ch1max_Callback(hObject, eventdata, handles) +% hObject handle to et_ch1max (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_ch1max as text +% str2double(get(hObject,'String')) returns contents of et_ch1max as a double + + +% --- Executes during object creation, after setting all properties. +function et_ch1max_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_ch1max (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_ch2min_Callback(hObject, eventdata, handles) +% hObject handle to et_ch2min (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_ch2min as text +% str2double(get(hObject,'String')) returns contents of et_ch2min as a double + + +% --- Executes during object creation, after setting all properties. +function et_ch2min_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_ch2min (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_ch2max_Callback(hObject, eventdata, handles) +% hObject handle to et_ch2max (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_ch2max as text +% str2double(get(hObject,'String')) returns contents of et_ch2max as a double + + +% --- Executes during object creation, after setting all properties. +function et_ch2max_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_ch2max (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_ch3min_Callback(hObject, eventdata, handles) +% hObject handle to et_ch3min (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_ch3min as text +% str2double(get(hObject,'String')) returns contents of et_ch3min as a double + + +% --- Executes during object creation, after setting all properties. +function et_ch3min_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_ch3min (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_ch3max_Callback(hObject, eventdata, handles) +% hObject handle to et_ch3max (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_ch3max as text +% str2double(get(hObject,'String')) returns contents of et_ch3max as a double + + +% --- Executes during object creation, after setting all properties. +function et_ch3max_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_ch3max (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on button press in cb_ch1and2. +function cb_ch1and2_Callback(hObject, eventdata, handles) +% hObject handle to cb_ch1and2 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of cb_ch1and2 + + +% --- Executes on button press in cb_ch2and3. +function cb_ch2and3_Callback(hObject, eventdata, handles) +% hObject handle to cb_ch2and3 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of cb_ch2and3 + + +% --- Executes on button press in cb_ann. +function cb_ann_Callback(hObject, eventdata, handles) +% hObject handle to cb_ann (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of cb_ann diff --git a/Control/InitBayesianFusion.m b/Control/InitBayesianFusion.m new file mode 100644 index 0000000..5c9d026 --- /dev/null +++ b/Control/InitBayesianFusion.m @@ -0,0 +1,44 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Initialization of BayesianFusion, sets up the weight matrix if weight +% parameters is 1. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-09 / Joel Falk-Dahlin / Creation +% 20xx-xx-xx / Author / Comment on update + +function patRec = InitBayesianFusion(patRec) + +weight = patRec.control.controlAlg.parameters.weight; +bufferSize = patRec.control.controlAlg.parameters.bufferSize; + +if weight == 1 + l = 1:bufferSize; + Z = sum( exp( -0.5/bufferSize.*l) ); + k = 10.*exp(-0.5/bufferSize.*l')./Z; + k = flipud(k); + k = repmat(k,1,patRec.nOuts); +else + k = zeros(bufferSize,patRec.nOuts); +end + +patRec.control.controlAlg.prop.k = k; + +end \ No newline at end of file diff --git a/Control/InitCombinedControl.m b/Control/InitCombinedControl.m new file mode 100644 index 0000000..10e732e --- /dev/null +++ b/Control/InitCombinedControl.m @@ -0,0 +1,50 @@ +function patRec = InitCombinedControl(patRec) + + controlOne = patRec.control.controlAlg.parameters.controlOne; + controlTwo = patRec.control.controlAlg.parameters.controlTwo; + + % Initialize first controller + patRecOne = patRec; + patRecOne = InitControl_new(patRecOne,controlOne); + + % Initialize second controller + patRecTwo = patRec; + patRecTwo = InitControl_new(patRecTwo,controlTwo); + + % Set parameters from patRec to patRecOne and Two if they exist + allParameters = fieldnames( patRec.control.controlAlg.parameters ); + for iParam = 1:size(allParameters,1) + name = allParameters{iParam}; + value = patRec.control.controlAlg.parameters.(allParameters{iParam}); + + if isfield( patRecOne.control.controlAlg.parameters, name ) + patRecOne.control.controlAlg.parameters.(name) = value; + end + + if isfield( patRecTwo.control.controlAlg.parameters, name ) + patRecTwo.control.controlAlg.parameters.(name) = value; + end + + end + + % Read parameters from patRecOne and patRecTwo and store in patRec + tmp = [{'One'},{'Two'}]; + for iPatRec = 1:2 + currentPatRec = eval(['patRec',tmp{iPatRec}]); + allParameters = fieldnames( currentPatRec.control.controlAlg.parameters ); + if ~isempty(allParameters) + for iParam = 1:size( allParameters, 1 ) + currentParameter = allParameters(iParam); + patRec.control.controlAlg.parameters.(currentParameter{1}) = currentPatRec.control.controlAlg.parameters.(currentParameter{1}); + end + end + end + + patRecOne = UpdateControl(patRecOne); + patRecTwo = UpdateControl(patRecTwo); + + % Store patRecs in control properties + patRec.control.controlAlg.prop.patRecOne = patRecOne; + patRec.control.controlAlg.prop.patRecTwo = patRecTwo; + +end \ No newline at end of file diff --git a/Control/InitControl.m b/Control/InitControl.m index 877a88c..56544be 100644 --- a/Control/InitControl.m +++ b/Control/InitControl.m @@ -1,38 +1,38 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% -% Funtion to concentrate the calling of control algorithms -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-07-20 / Max Ortiz / Creation (moved out from RealtimePatRec) -% 20xx-xx-xx / Author / Comment on update - -function patRec = InitControl(patRec) - - if strcmp(patRec.controlAlg,'Majority vote') - patRec.outBuffer = zeros(4,patRec.nOuts); - elseif strcmp(patRec.controlAlg,'Buffer output') - patRec.outBuffer = zeros(4,patRec.nOuts); - elseif strcmp(patRec.controlAlg,'Ramp') - - end - -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% Funtion to concentrate the calling of control algorithms +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-07-20 / Max Ortiz / Creation (moved out from RealtimePatRec) +% 20xx-xx-xx / Author / Comment on update + +function patRec = InitControl(patRec) + + if strcmp(patRec.controlAlg,'Majority vote') + patRec.outBuffer = zeros(4,patRec.nOuts); + elseif strcmp(patRec.controlAlg,'Buffer output') + patRec.outBuffer = zeros(4,patRec.nOuts); + elseif strcmp(patRec.controlAlg,'Ramp') + + end + +end \ No newline at end of file diff --git a/Control/InitControl_new.m b/Control/InitControl_new.m index 2c09cce..7481a7e 100644 --- a/Control/InitControl_new.m +++ b/Control/InitControl_new.m @@ -1,103 +1,103 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% -% patRec = InitControl_new(patRec,type) -% -% Initializes a control algorithm onto the patRec structure. The -% control algorithm is stored in a structure called controlAlg and is -% placed at patRec.control.controlAlg. -% -% The controlAlg structure contains the name of the control -% algorithm, a funciton handle to the control algorithm script, -% parameters set by the user and properties that are to be used only -% by the algorithm itself. -% -% Each control algorithm can have its own initialization script -% setting up the properties, this should be named -% Init'MyControlAlg' ( replace 'MyControlAlg' with the name of the -% algorithm ). -% -% INPUTS: -% -% patRec - pattern recognition structure that are to be appended with -% the controlAlg structure -% -% type - a string with the name of any of the valid control -% algorithms stored in \Control\ValidControlAlgs.txt -% If type is empty or 'None', any controlAlg structure is removed -% from the patRec -% -% OUTPUTS: -% -% patRec - new pattern recognition structure with controlAlg -% structure stored inside patRec.control -% -% -%% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-10-05 / Joel Falk-Dahlin / Creation -% 2012-10-08 / Joel Falk-Dahlin / Added correct buffer setting from - % internal property (.prop.bufferSize) and - % not only from parameter - % (.parameter.bufferSize) allowing for - % different types of controlAlgs. -% 2012-10-10 / Joel Falk-Dahlin / Added check for outBuffer before - % initializing it, this way the buffer can - % be initialized in another manor in - % init-file -% 20xx-xx-xx / Author / Comment on update - -function patRec = InitControl_new(patRec,type) - -% Check if function is called with empty or None type -% Remove current controlAlg and return the patRec -if isempty(type) || strcmp(type,'None') - if isfield(patRec.control,'controlAlg') - patRec.control = rmfield(patRec.control,'controlAlg'); - end - -% Else, if type is not 'None' or empty, Check if it is a valid algorithm -else - % Read PostProcessors from file ValidControlAlg.txt - validControlAlg = ReadValidControlAlgs; - - % Check that the inputed type is a valid controlAlg - validTypes = []; - for i = 1:size(validControlAlg,2) - validTypes = [validTypes, validControlAlg{i}.name]; - end - validatestring(type, validTypes); % Gives error if not true - - % Find desired algorithm - desiredControlAlg = strcmp(type,validTypes); - - % Set patRec.controlAlg.name / .fnc / .parameters - % Set chosen postprocessor - patRec.control.controlAlg = validControlAlg{desiredControlAlg}; - - % Remove any output buffer that may be initialized - if isfield(patRec.control,'outBuffer') - patRec.control = rmfield(patRec.control,'outBuffer'); - end - - % Update buffers using current parameters - patRec = ReInitControl(patRec); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% patRec = InitControl_new(patRec,type) +% +% Initializes a control algorithm onto the patRec structure. The +% control algorithm is stored in a structure called controlAlg and is +% placed at patRec.control.controlAlg. +% +% The controlAlg structure contains the name of the control +% algorithm, a funciton handle to the control algorithm script, +% parameters set by the user and properties that are to be used only +% by the algorithm itself. +% +% Each control algorithm can have its own initialization script +% setting up the properties, this should be named +% Init'MyControlAlg' ( replace 'MyControlAlg' with the name of the +% algorithm ). +% +% INPUTS: +% +% patRec - pattern recognition structure that are to be appended with +% the controlAlg structure +% +% type - a string with the name of any of the valid control +% algorithms stored in \Control\ValidControlAlgs.txt +% If type is empty or 'None', any controlAlg structure is removed +% from the patRec +% +% OUTPUTS: +% +% patRec - new pattern recognition structure with controlAlg +% structure stored inside patRec.control +% +% +%% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-05 / Joel Falk-Dahlin / Creation +% 2012-10-08 / Joel Falk-Dahlin / Added correct buffer setting from + % internal property (.prop.bufferSize) and + % not only from parameter + % (.parameter.bufferSize) allowing for + % different types of controlAlgs. +% 2012-10-10 / Joel Falk-Dahlin / Added check for outBuffer before + % initializing it, this way the buffer can + % be initialized in another manor in + % init-file +% 20xx-xx-xx / Author / Comment on update + +function patRec = InitControl_new(patRec,type) + +% Check if function is called with empty or None type +% Remove current controlAlg and return the patRec +if isempty(type) || strcmp(type,'None') + if isfield(patRec.control,'controlAlg') + patRec.control = rmfield(patRec.control,'controlAlg'); + end + +% Else, if type is not 'None' or empty, Check if it is a valid algorithm +else + % Read PostProcessors from file ValidControlAlg.txt + validControlAlg = ReadValidControlAlgs; + + % Check that the inputed type is a valid controlAlg + validTypes = []; + for i = 1:size(validControlAlg,2) + validTypes = [validTypes, validControlAlg{i}.name]; + end + validatestring(type, validTypes); % Gives error if not true + + % Find desired algorithm + desiredControlAlg = strcmp(type,validTypes); + + % Set patRec.controlAlg.name / .fnc / .parameters + % Set chosen postprocessor + patRec.control.controlAlg = validControlAlg{desiredControlAlg}; + + % Remove any output buffer that may be initialized + if isfield(patRec.control,'outBuffer') + patRec.control = rmfield(patRec.control,'outBuffer'); + end + + % Update buffers using current parameters + patRec = ReInitControl(patRec); + end % end "if type is None" \ No newline at end of file diff --git a/Control/InitMF_Hand_DC_Hardcoded.m b/Control/InitMF_Hand_DC_Hardcoded.m new file mode 100644 index 0000000..75336cf --- /dev/null +++ b/Control/InitMF_Hand_DC_Hardcoded.m @@ -0,0 +1,57 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to initialize the vector to be used for control of a +% multifuctional prosthetic device using dc motors +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2013-04-21 / Max Ortiz / Creation (removed from RealtimePatRec +% 20xx-xx-xx / Author / Comment on update + +function [pwmIDs pwmAs pwmBs] = InitMF_Hand_DC_Hardcoded(handles) + + %Initialize + motorIdx = zeros(1,10); + pwmAs = zeros(1,10); + pwmBs = zeros(1,10); + movSpeeds = zeros(1,10); + + % Get the links to the motors + for i = 1 : size(motorIdx,2) + pmID = ['handles.pm_m' num2str(i)]; + motorIdx(i) = get(eval(pmID),'Value'); + speedID = ['handles.et_speed' num2str(i)]; + movSpeeds(i) = str2double(get(eval(speedID),'String')); + end + + % Init variables for control + pwmIDs = ['A';'A';'B';'B';'C';'C';'D';'D';'E';'E']; + for i = 1 : 2 : size(pwmIDs) + pwmAs(i) = movSpeeds(i); + pwmBs(i) = 0; + pwmAs(i+1) = 0; + pwmBs(i+1) = movSpeeds(i+1); + end; + + % Arrenge according to selection + pwmIDs = pwmIDs(motorIdx); + pwmAs = pwmAs(motorIdx); + pwmBs = pwmBs(motorIdx); + +end \ No newline at end of file diff --git a/Control/InitMajorityVoteSimultaneous.m b/Control/InitMajorityVoteSimultaneous.m new file mode 100644 index 0000000..53be1e7 --- /dev/null +++ b/Control/InitMajorityVoteSimultaneous.m @@ -0,0 +1,32 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% Initialization of the MajorityVoteSimultaneous control strategy. This +% strategy uses a buffer with more elements then patRec.nOuts and thus +% have to be initialized in a seperate way. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-10 / Joel Falk-Dahlin / Creation + +function patRec = InitMajorityVoteSimultaneous(patRec) + +%patRec.control.outBuffer = zeros(patRec.control.controlAlg.parameters.bufferSize, size(patRec.movOutIdx,2)); + +end \ No newline at end of file diff --git a/Comm/InitMotors.m b/Control/InitMotors.m similarity index 97% rename from Comm/InitMotors.m rename to Control/InitMotors.m index c4b489d..c49303c 100644 --- a/Comm/InitMotors.m +++ b/Control/InitMotors.m @@ -1,38 +1,38 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Reads the file motors.def and loads the data into motor objects. -% --------------------------Updates-------------------------- -% 2012-05-29 / Nichlas Sander / Creation -% 2012-07-19 / Max Ortiz / Added fclose which is necessary to prevent -% matlab crashes due to many files open. -% 20xx-xx-xx / Author / Comment on update - -function obj = InitMotors -fid = fopen('motors.def'); -tline = fgetl(fid); -i = 1; -while(ischar(tline)) - t = textscan(tline,'%s','delimiter',','); - t = t{1}; - obj(i) = motor(t(1),str2double(t(2)),str2double(t(3))); - tline = fgetl(fid); - i = i + 1; -end - -fclose(fid); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Reads the file motors.def and loads the data into motor objects. +% --------------------------Updates-------------------------- +% 2012-05-29 / Nichlas Sander / Creation +% 2012-07-19 / Max Ortiz / Added fclose which is necessary to prevent +% matlab crashes due to many files open. +% 20xx-xx-xx / Author / Comment on update + +function obj = InitMotors +fid = fopen('motors.def'); +tline = fgetl(fid); +i = 1; +while(ischar(tline)) + t = textscan(tline,'%s','delimiter',','); + t = t{1}; + obj(i) = motor(t(1),str2double(t(2)),str2double(t(3))); + tline = fgetl(fid); + i = i + 1; +end + +fclose(fid); diff --git a/Comm/InitMovements.m b/Control/InitMovements.m similarity index 79% rename from Comm/InitMovements.m rename to Control/InitMovements.m index 26d53ec..9ac0cc7 100644 --- a/Comm/InitMovements.m +++ b/Control/InitMovements.m @@ -1,45 +1,51 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Reads the file movements.def and loads the data into movement objects. -% --------------------------Updates-------------------------- -% 2012-05-29 / Nichlas Sander / Creation -% 20xx-xx-xx / Author / Comment on update - -function obj = InitMovements -fid = fopen('movements.def'); -tline = fgetl(fid); -i = 1; -while(ischar(tline)) - %Go through the string - t = textscan(tline,'%s','delimiter',','); - t = t{1}; - - %process t(5) here - m = textscan(cell2mat(t(5)),'%s','delimiter',' '); - j = 1; - clear motors; - while(j <= size(m{1},1)) - motors(j) = str2double(m{1}(j)); - j = j + 1; - end - obj(i) = movement(str2double(t(1)),t(2),str2double(t(3)),str2double(t(4)),motors); - tline = fgetl(fid); - i = i+1; -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Reads the file movements.def and loads the data into movement objects. +% --------------------------Updates-------------------------- +% 2012-05-29 / Nichlas Sander / Creation +% 2015-06-11 / Sebastian Karlsson / The directions of each motor in each +% movement are stored in motors matrix. +% 20xx-xx-xx / Author / Comment on update + +function obj = InitMovements +fid = fopen('movements.def'); +tline = fgetl(fid); +i = 1; +while(ischar(tline)) + %Go through the string + t = textscan(tline,'%s','delimiter',','); + t = t{1}; + + %process t(5) here + m = textscan(cell2mat(t(5)),'%s','delimiter',' '); + j = 1; + k = 1; + clear motors; + motors = zeros(2,size(m{1},1)/2); + while(j <= size(m{1},1)) + motors(1,k) = str2double(m{1}(j)); %Motor ID + motors(2,k) = str2double(m{1}(j+1)); %Motor Direction + j = j + 2; + k = k + 1; + end + obj(i) = movement(str2double(t(1)),t(2),str2double(t(3)),str2double(t(4)),motors); + tline = fgetl(fid); + i = i+1; +end fclose(fid); \ No newline at end of file diff --git a/Control/InitOutBuffer.m b/Control/InitOutBuffer.m index 69e84f6..341744b 100644 --- a/Control/InitOutBuffer.m +++ b/Control/InitOutBuffer.m @@ -1,94 +1,94 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% -% patRec = InitOutBuffer(patRec) -% -% Initializes the outBuffer stored in patRec.control. This buffer can -% be used by the control algorithms. Initialization of the buffer -% looks if the patRec has an attached control algorithm, if that -% control algorithm has either a parameter or a property called -% bufferSize. If not, the outBuffer is either created to contains one -% row and patRec.nOuts columns. If the buffer already exists it is -% reflushed to contain only zeros. -% -% INPUTS: -% -% patRec - pattern recognition structure that are to be initialized -% with an outBuffer -% -% OUTPUTS: -% -% patRec - new, updated patRec structure, that has an empty outBuffer -% stored in patRec.control.outBuffer -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-10-10 / Joel Falk-Dahlin / Creation - -function patRec = InitOutBuffer(patRec) - -% Is there a parameter bufferSize? -if isfield(patRec.control.controlAlg,'parameters') - if isfield(patRec.control.controlAlg.parameters,'bufferSize') - bufferSize = patRec.control.controlAlg.parameters.bufferSize; - % Is the outBuffer already initialized? - if isfield(patRec.control,'outBuffer') - % Is the size of the outBuffer correct? - if size(patRec.control.outBuffer,1) ~= bufferSize - patRec = InitBuffer(patRec,[bufferSize, patRec.nOuts]); - end - % If not, initialize the outBuffer - else - patRec = InitBuffer(patRec,[bufferSize, patRec.nOuts]); - end - end -end - -% Is there a property bufferSize? -if isfield(patRec.control.controlAlg,'prop') - if isfield(patRec.control.controlAlg.prop,'bufferSize') - bufferSize = patRec.control.controlAlg.prop.bufferSize; - % Is the outBuffer already initialized? - if isfield(patRec.control,'outBuffer') - % Is the size of the outBuffer correct? - if size(patRec.control.outBuffer,1) ~= bufferSize - patRec = InitBuffer(patRec,[bufferSize, patRec.nOuts]); - end - % If not, initialize the outBuffer - else - patRec = InitBuffer(patRec,[bufferSize, patRec.nOuts]); - end - end -end - -% If bufferSize in neither a parameter of property, check if already exist -% Initialize it to be 1, if it does not exists -if ~isfield(patRec.control,'outBuffer') - patRec = InitBuffer(patRec,[1, patRec.nOuts]); -% Reinitialize it to the same size if it already exists -else - bufferSize = size(patRec.control.outBuffer); - patRec = InitBuffer(patRec,bufferSize); -end - -end - -function patRec = InitBuffer(patRec,bufferSize) - patRec.control.outBuffer = zeros(bufferSize); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% patRec = InitOutBuffer(patRec) +% +% Initializes the outBuffer stored in patRec.control. This buffer can +% be used by the control algorithms. Initialization of the buffer +% looks if the patRec has an attached control algorithm, if that +% control algorithm has either a parameter or a property called +% bufferSize. If not, the outBuffer is either created to contains one +% row and patRec.nOuts columns. If the buffer already exists it is +% reflushed to contain only zeros. +% +% INPUTS: +% +% patRec - pattern recognition structure that are to be initialized +% with an outBuffer +% +% OUTPUTS: +% +% patRec - new, updated patRec structure, that has an empty outBuffer +% stored in patRec.control.outBuffer +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-10 / Joel Falk-Dahlin / Creation + +function patRec = InitOutBuffer(patRec) + +% Is there a parameter bufferSize? +if isfield(patRec.control.controlAlg,'parameters') + if isfield(patRec.control.controlAlg.parameters,'bufferSize') + bufferSize = patRec.control.controlAlg.parameters.bufferSize; + % Is the outBuffer already initialized? + if isfield(patRec.control,'outBuffer') + % Is the size of the outBuffer correct? + if size(patRec.control.outBuffer,1) ~= bufferSize + patRec = InitBuffer(patRec,[bufferSize, patRec.nOuts]); + end + % If not, initialize the outBuffer + else + patRec = InitBuffer(patRec,[bufferSize, patRec.nOuts]); + end + end +end + +% Is there a property bufferSize? +if isfield(patRec.control.controlAlg,'prop') + if isfield(patRec.control.controlAlg.prop,'bufferSize') + bufferSize = patRec.control.controlAlg.prop.bufferSize; + % Is the outBuffer already initialized? + if isfield(patRec.control,'outBuffer') + % Is the size of the outBuffer correct? + if size(patRec.control.outBuffer,1) ~= bufferSize + patRec = InitBuffer(patRec,[bufferSize, patRec.nOuts]); + end + % If not, initialize the outBuffer + else + patRec = InitBuffer(patRec,[bufferSize, patRec.nOuts]); + end + end +end + +% If bufferSize in neither a parameter of property, check if already exist +% Initialize it to be 1, if it does not exists +if ~isfield(patRec.control,'outBuffer') + patRec = InitBuffer(patRec,[1, patRec.nOuts]); +% Reinitialize it to the same size if it already exists +else + bufferSize = size(patRec.control.outBuffer); + patRec = InitBuffer(patRec,bufferSize); +end + +end + +function patRec = InitBuffer(patRec,bufferSize) + patRec.control.outBuffer = zeros(bufferSize); end \ No newline at end of file diff --git a/Control/InitPropControl.m b/Control/InitPropControl.m index 9637f3c..b1588e8 100644 --- a/Control/InitPropControl.m +++ b/Control/InitPropControl.m @@ -1,50 +1,50 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% -% patRec = InitPropControl(patRec) -% -% Function to intialize the propControl structure to a patRec. -% Initialization is done with default parameters -% -% INPUTS: -% -% patRec - pattern recognition structure that are to use proprotional -% control -% -% OUTPUTS: -% -% patRec - patRec structure with propControl in patRec.control, using -% default parameters. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-11-27 / Joel Falk-Dahlin / Creation -% 20xx-xx-xx / Author / Comment on update - -function patRec = InitPropControl(patRec) - - % Initialize prop control Vars if they don't exist - if ~isfield(patRec.control,'propControl') - patRec.control.propControl.propFeature = 1; - patRec.control.propControl.propMaxThresh = 5; - patRec.control.propControl.propMinThresh = 0; - patRec.control.propControl.propSpeedMap = 'linear'; - end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% patRec = InitPropControl(patRec) +% +% Function to intialize the propControl structure to a patRec. +% Initialization is done with default parameters +% +% INPUTS: +% +% patRec - pattern recognition structure that are to use proprotional +% control +% +% OUTPUTS: +% +% patRec - patRec structure with propControl in patRec.control, using +% default parameters. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-11-27 / Joel Falk-Dahlin / Creation +% 20xx-xx-xx / Author / Comment on update + +function patRec = InitPropControl(patRec) + + % Initialize prop control Vars if they don't exist + if ~isfield(patRec.control,'propControl') + patRec.control.propControl.propFeature = 1; + patRec.control.propControl.propMaxThresh = 5; + patRec.control.propControl.propMinThresh = 0; + patRec.control.propControl.propSpeedMap = 'linear'; + end + end \ No newline at end of file diff --git a/Control/InitRamp.m b/Control/InitRamp.m index 01947e7..e8fb253 100644 --- a/Control/InitRamp.m +++ b/Control/InitRamp.m @@ -1,41 +1,41 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Initialization of Ramp controlAlg. Sets up internal properties that are -% used by the algorithm. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-10-08 / Joel Falk-Dahlin / Creation -% 20xx-xx-xx / Author / Comment on update - -function patRec = InitRamp(patRec) - -motors = InitMotors; -maxPct = zeros(size(motors)); -%nMisclassComp = patRec.controlAlg.parameters.nMisclassComp; - -for i = 1:size(maxPct,2) - maxPct(i) = motors(i).pct; -end - -patRec.control.controlAlg.prop.maxPct = maxPct; -patRec.control.controlAlg.prop.nPredicted = zeros(1,patRec.nOuts); -%patRec.controlAlg.prop.nPredictedBuffer = zeros(nMisclassComp+2,patRec.nOuts); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Initialization of Ramp controlAlg. Sets up internal properties that are +% used by the algorithm. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-08 / Joel Falk-Dahlin / Creation +% 20xx-xx-xx / Author / Comment on update + +function patRec = InitRamp(patRec) + +motors = InitMotors; +maxPct = zeros(size(motors)); +%nMisclassComp = patRec.controlAlg.parameters.nMisclassComp; + +for i = 1:size(maxPct,2) + maxPct(i) = motors(i).pct; +end + +patRec.control.controlAlg.prop.maxPct = maxPct; +patRec.control.controlAlg.prop.nPredicted = zeros(1,patRec.nOuts); +%patRec.controlAlg.prop.nPredictedBuffer = zeros(nMisclassComp+2,patRec.nOuts); + end \ No newline at end of file diff --git a/Control/InitRampModified.m b/Control/InitRampModified.m new file mode 100644 index 0000000..feb38da --- /dev/null +++ b/Control/InitRampModified.m @@ -0,0 +1,40 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Initialization of RampModified controlAlg. Sets up internal properties that are +% used by the algorithm. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-17 / Joel Falk-Dahlin / Creation +% 20xx-xx-xx / Author / Comment on update + +function patRec = InitRampModified(patRec) + +motors = InitMotors; +maxPct = zeros(size(motors)); +nMisclassComp = patRec.control.controlAlg.parameters.nMisclassComp; + +for i = 1:size(maxPct,2) + maxPct(i) = motors(i).pct; +end + +patRec.control.controlAlg.prop.maxPct = maxPct; +patRec.control.controlAlg.prop.nPredictedBuffer = zeros(nMisclassComp+2,patRec.nOuts); + +end \ No newline at end of file diff --git a/Control/InitRampModified2.m b/Control/InitRampModified2.m new file mode 100644 index 0000000..cc2bab7 --- /dev/null +++ b/Control/InitRampModified2.m @@ -0,0 +1,4 @@ +function patRec = InitRampModified2(patRec) + +patRec.control.controlAlg.prop.timesPredicted = zeros(1,patRec.nOuts); +patRec.control.controlAlg.prop.timesNotPredicted = zeros(1,patRec.nOuts); \ No newline at end of file diff --git a/Control/InitRampModified3.m b/Control/InitRampModified3.m new file mode 100644 index 0000000..a3f6a38 --- /dev/null +++ b/Control/InitRampModified3.m @@ -0,0 +1,3 @@ +function patRec = InitRampModified3(patRec) + +patRec.control.controlAlg.prop.timesPredicted = zeros(1,patRec.nOuts); \ No newline at end of file diff --git a/Control/InitSensors.m b/Control/InitSensors.m new file mode 100644 index 0000000..9534fbf --- /dev/null +++ b/Control/InitSensors.m @@ -0,0 +1,46 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Reads the file sensors.def and loads the data into sensor objects. +% --------------------------Updates-------------------------- +% 2015-06-11 / Sebastian Karlsson / Creation + +function obj = InitSensors + +global ctrl_dir + +sensorspath = 'sensors.def'; +if ctrl_dir + sensorspath = [ctrl_dir filesep 'sensors.def']; +end + +fid = fopen(sensorspath); +if fid == -1 + obj = -1; +else + tline = fgetl(fid); + i = 1; + while(ischar(tline)) + t = textscan(tline,'%s','delimiter',','); + t = t{1}; + obj(i) = sensor(t(1), t(2)); + tline = fgetl(fid); + i = i + 1; + end + fclose(fid); +end diff --git a/Control/MajorityVote.m b/Control/MajorityVote.m index 0c0b356..777cbe3 100644 --- a/Control/MajorityVote.m +++ b/Control/MajorityVote.m @@ -1,43 +1,43 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to compute the Majority voting control strategy. It looks at the -% latest prediction and extracts the most common -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-07 / Max Ortiz / Creation -% 2012-10-05 / Joel Falk-Dahlin / Added input and output arguments to -% match controlAlg standard. Added find -% function so outMov can be several -% outputs if there is equal ammount of -% predictions for two movements -% (simultaneous control) -% 20xx-xx-xx / Author / Comment on update - -function [patRec, outMov] = MajorityVote(patRec, outMov, outVec) - -% Remove the oldest prediction -patRec.control.outBuffer = patRec.control.outBuffer(2:end,:); -% Add the new prediction -patRec.control.outBuffer(end+1,outMov') = 1; - -%% Compute output -% Not suitable for simltaneous control -outMax = max(sum(patRec.control.outBuffer)); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to compute the Majority voting control strategy. It looks at the +% latest prediction and extracts the most common +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-07 / Max Ortiz / Creation +% 2012-10-05 / Joel Falk-Dahlin / Added input and output arguments to +% match controlAlg standard. Added find +% function so outMov can be several +% outputs if there is equal ammount of +% predictions for two movements +% (simultaneous control) +% 20xx-xx-xx / Author / Comment on update + +function [patRec, outMov] = MajorityVote(patRec, outMov, outVec) + +% Remove the oldest prediction +patRec.control.outBuffer = patRec.control.outBuffer(2:end,:); +% Add the new prediction +patRec.control.outBuffer(end+1,outMov') = 1; + +%% Compute output +% Not suitable for simltaneous control +outMax = max(sum(patRec.control.outBuffer)); outMov = find(sum(patRec.control.outBuffer) == outMax )'; \ No newline at end of file diff --git a/Control/MajorityVoteSimultaneous.m b/Control/MajorityVoteSimultaneous.m new file mode 100644 index 0000000..dfd5936 --- /dev/null +++ b/Control/MajorityVoteSimultaneous.m @@ -0,0 +1,65 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function that extends the use of the MajorityVote strategy by remapping +% movements involving multiple DoFs to their corresponding movOutIdx, +% e.g. the outMov [2 3 6]' are mapped to the index i that corresponds to +% the same movement given by patRec.movOutIdx{i}. This way the movement +% containing all three DoFs are stored in a single number that can fill +% the buffer. Otherwise if one of the DoFs are misclassified the buffer +% will need to be filled all the way for that DoF to be predicted again. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-10 / Joel Falk-Dahlin / Creation +% 20xx-xx-xx / Author / Comment on update + +function [patRec, outMov] = MajorityVoteSimultaneous(patRec, outMov, outVec) + +if ismember(patRec.nOuts,outMov) && strcmp(patRec.mov{end},'Rest') + outMov = patRec.nOuts; + % Reset buffer if rest is predicted + patRec.control.outBuffer(:) = 0; +else + % Update output buffer with the newly predicted movement + patRec.control.outBuffer(1:end-1,:) = patRec.control.outBuffer(2:end,:); + patRec.control.outBuffer(end,:) = zeros(1,size(patRec.control.outBuffer,2)); + patRec.control.outBuffer(end,outMov) = 1; + + % Count the occurances of the output vectors in outBuffer + outBuffer = patRec.control.outBuffer; + emptyRow = ( sum(outBuffer,2) == 0 ); % Remove all rows in buffer without prediction + outBuffer(emptyRow,:) = []; + counterMat = []; + while ~isempty(outBuffer) + currentMov = outBuffer(end,:); + eqMat = bsxfun(@eq, currentMov, outBuffer); + eqIdx = all(eqMat'); + + % Remove all entries that are equal and count them + counterMat = [counterMat; sum(eqIdx), currentMov]; + outBuffer(eqIdx,:) = []; + end + + % Find movement that is counted most times in buffer, if several movements + % are counted the same ammount of times, this gives the most recent + [~,I] = max(counterMat(:,1)); + outMovIdx = counterMat(I,2:end) > 0; + numberVector = 1:size(outBuffer,2); + outMov = numberVector(outMovIdx); +end diff --git a/Control/MotorsOff.m b/Control/MotorsOff.m index 31e2555..1d88231 100644 --- a/Control/MotorsOff.m +++ b/Control/MotorsOff.m @@ -1,74 +1,68 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Function to activate the DC or servo motors for some degrees. In the c -% of the DC motors this is given by time and the PWM duty cycle. In the -% case of servor motors, this is given only by the duty cycle. -% -% --------------------------Updates-------------------------- -% 2012-05-22 / Max Ortiz / Creation (MoveMotor) -% 2012-07-03 / Max Ortiz / Creation of specific routines for activation -% and deactivation to improve speed. - -function [motors, movement] = MotorsOff(com, movement, motors) - -if strcmp(movement.name,'Rest') - return; -end - -%pwmIDs = cell2mat(movement.idMotor); -movMotors = movement.motor; -noPIDs = size(movMotors,2); - -motorPct = []; -pwmIDs = []; - -% Get the IDs of al PWM involved in the selected movement -for i=1:length(movMotors) - motorPct = [motorPct motors(movMotors(i)).pct]; - pwmIDs = [pwmIDs cell2mat(motors(movMotors(i)).id)]; -end - - -%This only works when movements have motors with the same (motor)type -%% DC motors -if (motors(movMotors(1)).type == 0) % DC Motors - - % Get the pwm vectors according to the direction of the movement - if movement.vreDir - pwmA = zeros(1,noPIDs); - pwmB = motorPct; - else - pwmA = motorPct; - pwmB = zeros(1,noPIDs); - end - - % Stop - for i = 1 : noPIDs - if ~Update2PWMusingSCI(com, pwmIDs(i), 0, 0); - disp('Failed'); - fclose(com.io); - end - end - -%% Servo Motors -elseif motors(movMotors(1)).type == 1 - % Servor don't need to be stoped, since they will stop automatically at - % the last given position. -end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to activate the DC or servo motors for some degrees. In the c +% of the DC motors this is given by time and the PWM duty cycle. In the +% case of servor motors, this is given only by the duty cycle. +% +% --------------------------Updates-------------------------- +% 2012-05-22 / Max Ortiz / Creation (MoveMotor) +% 2012-07-03 / Max Ortiz / Creation of specific routines for activation +% and deactivation to improve speed. +% 2015-06-11 / Sebastian Karlsson / Moved to motor-type identification +% to be implemented on the connected devices +% firmware instead of handled by +% this function, according to the standardized framework. +% 2015-07-07 / Enzo Mastinu / The Update2PWMusingSCI function has been +% replaced with the general SendMotorCommand, +% used to implement the control framework + + + +function [motors, movement] = MotorsOff(com, movement, motors) + +if strcmp(movement.name,'Rest') + return; +end + +noPIDs = size(movement.motor,2); + +% Activate and update +for i = 1 : noPIDs + % Check the speed Updating + motors(movement.motor(1,i)).pct = 0; %%%SERVE? + % Extract control type (defined in "motors.def" file) + ctrl_type = motors(movement.motor(1,i)).type; + % Extract motor index (defined in "motors.def" file) + motor_index = cell2mat(motors(movement.motor(1,i)).id); + % Extract movement direction (for speed control) + mov_dir = movement.motor(2,i); + + % Send motor commands + if(ctrl_type == 0) + % if in speed control, PWM must be set to 0 + if ~SendMotorCommand(com, ctrl_type, motor_index, mov_dir, 0); + disp('Failed'); + fclose(com.io); + end + + elseif(ctrl_type == 1) + % if in position control, no commands are needed for the device to + % stop + end +end \ No newline at end of file diff --git a/Control/MotorsOn.m b/Control/MotorsOn.m index d40ad13..22f8f5c 100644 --- a/Control/MotorsOn.m +++ b/Control/MotorsOn.m @@ -1,99 +1,61 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Function to activate the DC or servo motors. The speed of -% DC motors this is given by the PWM duty cycle. In the -% case of servor motors, the position is simply incremented. -% -% --------------------------Updates-------------------------- -% 2012-05-22 / Max Ortiz / Creation (MoveMotor) -% 2012-07-03 / Max Ortiz / Creation of specific routines for activation -% and deactivation to improve speed. - -function [motors, movement] = MotorsOn(com, movement, motors, degrees) - -if strcmp(movement.name,'Rest') - return; -end - -%pwmIDs = cell2mat(movement.idMotor); -movMotors = movement.motor; -noPIDs = size(movMotors,2); - -motorPct = []; -pwmIDs = []; - -% Get the IDs of al PWM involved in the selected movement -for i=1:length(movMotors) - motorPct = [motorPct motors(movMotors(i)).pct]; - pwmIDs = [pwmIDs cell2mat(motors(movMotors(i)).id)]; -end - -% THe following code doesn't work because this routines is only called when -% the prediction is changed, therefore the effect of the ramp cannot be -% observed in the current setup. -% Code for variable speed according to "degrees" -% mPct = 10*degrees; -% if mPct >= 100 -% mPct = 100; -% end -% motorPct(1:length(movMotors)) = mPct; - -%This only works when movements have motors with the same (motor)type -%% DC motors -if (motors(movMotors(1)).type == 0) - - % Get the pwm vectors according to the direction of the movement - if movement.vreDir - pwmA = zeros(1,noPIDs); - pwmB = motorPct; - else - pwmA = motorPct; - pwmB = zeros(1,noPIDs); - end - - % Activate them - for i = 1 : noPIDs - % Send motor values - if ~Update2PWMusingSCI(com, pwmIDs(i), pwmA(i), pwmB(i)); - disp('Failed'); - fclose(com.io); - end - end - -%% Servo Motors -elseif motors(movMotors(1)).type == 1 - - % Add the position - if(movement.vreDir) - movDeg = -degrees; - else - movDeg = degrees; - end - - motors(movMotors(1)).pct = motors(movMotors(1)).pct + movDeg; - - % Send PWM - [result motors(movMotors(1)).pct] = UpdatePWMusingSCI_PanTilt(com, pwmIDs, motors(movMotors(1)).pct); - % If no problems where encountered, the ALC must return 1 - if ~result - disp('Failed'); - fclose(handles.com.io); - end - -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to activate the DC or servo motors. The speed of +% DC motors this is given by the PWM duty cycle. In the +% case of servor motors, the position is simply incremented. +% +% --------------------------Updates-------------------------- +% 2012-05-22 / Max Ortiz / Creation (MoveMotor) +% 2012-07-03 / Max Ortiz / Creation of specific routines for activation +% and deactivation to improve speed. +% 2015-06-11 / Sebastian Karlsson / Changed to update the current +% speed/position of all types of motors. +% Moved to motor-type identification +% to be implemented on the connected devices +% firmware instead of handled by +% this function, according to the standardized framework. +% 2015-07-07 / Enzo Mastinu / The Update2PWMusingSCI function has been +% replaced with the general SendMotorCommand, +% used to implement the control framework + +function [motors, movement] = MotorsOn(com, movement, motors, ctrl_val) + +if strcmp(movement.name,'Rest') + return; +end + +noPIDs = size(movement.motor,2); + +% Activate and update +for i = 1 : noPIDs + % Check the speed Updating + motors(movement.motor(1,i)).pct = ctrl_val; %%% SERVE? + % Extract control type (defined in "motors.def" file) + ctrl_type = motors(movement.motor(1,i)).type; + % Extract motor index (defined in "motors.def" file) + motor_index = cell2mat(motors(movement.motor(1,i)).id); + % Extract movement direction (for speed control) + mov_dir = movement.motor(2,i); + + % Send motor commands + if ~SendMotorCommand(com, ctrl_type, motor_index, mov_dir, ctrl_val); + disp('Failed'); % CHECK + fclose(com); + end +end diff --git a/Control/MoveMotor.m b/Control/MoveMotor.m index f953f5e..74f9602 100644 --- a/Control/MoveMotor.m +++ b/Control/MoveMotor.m @@ -1,103 +1,103 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Function to activate the DC or servo motors for some degrees. In the c -% of the DC motors this is given by time and the PWM duty cycle. In the -% case of servor motors, this is given only by the duty cycle. -% -% --------------------------Updates-------------------------- -% 2012-05-22 / Max Ortiz / Creation - -function [motors, movement] = MoveMotor(com, movement, movDeg,motors) - -%pwmIDs = cell2mat(movement.idMotor); -movMotors = movement.motor; -noPIDs = size(movMotors,2); - -motorPct = []; -pwmIDs = []; -motorActive = []; - -% Get the IDs of al PWM involved in the selected movement -for i=1:length(movMotors) - motorPct = [motorPct motors(movMotors(i)).pct]; - pwmIDs = [pwmIDs cell2mat(motors(movMotors(i)).id)]; -end - - -%This only works when movements have motors with the same (motor)type -%% DC motors -if (motors(movMotors(1)).type == 0) % DC Motors - pause on; - - % Get the pwm vectors according to the direction of the movement - if movement.vreDir - pwmA = zeros(1,noPIDs); - pwmB = motorPct; - else - pwmA = motorPct; - pwmB = zeros(1,noPIDs); - end - - % Relationship between degrees and time. - movTime = movDeg / 100; - - % Activate them - for i = 1 : noPIDs - % Send motor values - if ~Update2PWMusingSCI(com, pwmIDs(i), pwmA(i), pwmB(i)); - disp('Failed'); - fclose(com.io); - end - end - - % wait for the DoF to move - pause(movTime); - - % Stop - for i = 1 : noPIDs - if ~Update2PWMusingSCI(com, pwmIDs(i), 0, 0); - disp('Failed'); - fclose(com.io); - end - end - pause off; - -%% Servo Motors -elseif motors(movMotors(1)).type == 1 - - % Add the position - if(movement.vreDir) - movDeg = movDeg * -1; - end - - motors(movMotors(1)).pct = motors(movMotors(1)).pct + movDeg; - - % If no problems where encountered, the ALC must return 1 - % Send PWM - - [result motors(movMotors(1)).pct] = UpdatePWMusingSCI_PanTilt(com, pwmIDs, motors(movMotors(1)).pct); - - if ~result - disp('Failed'); - fclose(handles.com.io); - end - -end -%% End of the routines -motorsLast = motors; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to activate the DC or servo motors for some degrees. In the c +% of the DC motors this is given by time and the PWM duty cycle. In the +% case of servor motors, this is given only by the duty cycle. +% +% --------------------------Updates-------------------------- +% 2012-05-22 / Max Ortiz / Creation + +function [motors, movement] = MoveMotor(com, movement, movDeg,motors) + +%pwmIDs = cell2mat(movement.idMotor); +movMotors = movement.motor; +noPIDs = size(movMotors,2); + +motorPct = []; +pwmIDs = []; +motorActive = []; + +% Get the IDs of al PWM involved in the selected movement +for i=1:length(movMotors) + motorPct = [motorPct motors(movMotors(i)).pct]; + pwmIDs = [pwmIDs cell2mat(motors(movMotors(i)).id)]; +end + + +%This only works when movements have motors with the same (motor)type +%% DC motors +if (motors(movMotors(1)).type == 0) % DC Motors + pause on; + + % Get the pwm vectors according to the direction of the movement + if movement.vreDir + pwmA = zeros(1,noPIDs); + pwmB = motorPct; + else + pwmA = motorPct; + pwmB = zeros(1,noPIDs); + end + + % Relationship between degrees and time. + movTime = movDeg / 100; + + % Activate them + for i = 1 : noPIDs + % Send motor values + if ~Update2PWMusingSCI(com, pwmIDs(i), pwmA(i), pwmB(i)); + disp('Failed'); + fclose(com.io); + end + end + + % wait for the DoF to move + pause(movTime); + + % Stop + for i = 1 : noPIDs + if ~Update2PWMusingSCI(com, pwmIDs(i), 0, 0); + disp('Failed'); + fclose(com.io); + end + end + pause off; + +%% Servo Motors +elseif motors(movMotors(1)).type == 1 + + % Add the position + if(movement.vreDir) + movDeg = movDeg * -1; + end + + motors(movMotors(1)).pct = motors(movMotors(1)).pct + movDeg; + + % If no problems where encountered, the ALC must return 1 + % Send PWM + + [result motors(movMotors(1)).pct] = UpdatePWMusingSCI_PanTilt(com, pwmIDs, motors(movMotors(1)).pct); + + if ~result + disp('Failed'); + fclose(handles.com.io); + end + +end +%% End of the routines +motorsLast = motors; diff --git a/Control/MoveMotorWifi.m b/Control/MoveMotorWifi.m new file mode 100644 index 0000000..99453fa --- /dev/null +++ b/Control/MoveMotorWifi.m @@ -0,0 +1,49 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to activate the standard prosthetic components for +% a defined amount of time. +% +% --------------------------Updates-------------------------- +% 2013-06-03 / Max Ortiz / Creation + +function [motors, movement] = MoveMotorWifi(obj, movement, movDeg, motors) + + movMotors = movement.motor; + motorPct = motors(movMotors).pct; + + % Decode PWM id by looking at the motor and its direction + pwmID = cell2mat(motors(movMotors).id); + if pwmID == 'A' && movement.vreDir == 0 + pwmID = 'A'; + elseif pwmID == 'A' && movement.vreDir == 1 + pwmID = 'B'; + elseif pwmID == 'B' && movement.vreDir == 0 + pwmID = 'C'; + elseif pwmID == 'B' && movement.vreDir == 1 + pwmID = 'D'; + elseif pwmID == 'C' && movement.vreDir == 0 + pwmID = 'E'; + elseif pwmID == 'C' && movement.vreDir == 1 + pwmID = 'F'; + end + + time = movDeg; + ActivateSP_FixedTime(obj,pwmID,motorPct,time); + +end diff --git a/Control/Ramp.m b/Control/Ramp.m index c3224f1..216b397 100644 --- a/Control/Ramp.m +++ b/Control/Ramp.m @@ -1,98 +1,98 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Ramp control strategy. Sets speed for VRE and PWMs depending on how many -% consecutive predictions a motions has. Still to be implemented, -% misclassification protection that saves nPredicted in the case of -% intermittent misclassifications. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-10-16 / Joel Falk-Dahlin / Creation -% 2012-10-26 / Joel Falk-Dahlin / Changed maxSpeed to be set from desired -% output speed instead of a separate -% variable for the Ramp algorithm. This -% was necessary for prop.control -% 20xx-xx-xx / Author / Comment on update - -function [patRec, outMov] = Ramp(patRec, outMov, outVec) - -% Read Values from patRec object -maxSpeed = patRec.control.currentDegPerMov; -rampLength = patRec.control.controlAlg.parameters.rampLength; -downCount = patRec.control.controlAlg.parameters.downCount; -nPredicted = patRec.control.controlAlg.prop.nPredicted; -maxPct = patRec.control.controlAlg.prop.maxPct; - -% Save movement in index vector rather then as numbers -outMovIndex = zeros(size(nPredicted)); -outMovIndex(outMov) = 1; -outMovIndex = outMovIndex > 0; - -% Add one to counter if movement is predicted -nPredicted(outMovIndex) = nPredicted(outMovIndex) + 1; - -% Remove downCount from counter if movement is not predicted -nPredicted( ~outMovIndex ) = nPredicted(~outMovIndex) - downCount; - -% Make sure that counter is not more then rampLength or less then zero -nPredicted( nPredicted > rampLength ) = rampLength; -nPredicted( nPredicted < 0 ) = 0; - -% Save nPredicted into the patRec struct -patRec.control.controlAlg.prop.nPredicted = nPredicted; - -%% Calculate movement speeds - -% Calculate ramp gain for each movement depending on how many times they -% have been predicted -rampGain = nPredicted./ rampLength; - -% Set movement speed to maxSpeed*rampGain for now (maxSpeed can be changed -% to desired speed when proportional control is implemented) -movSpeed = maxSpeed.*rampGain; - -% Set non-predicted movements to zero-speed -movSpeed(~outMovIndex) = 0; - -%% Update VRE/ARE/Motor speeds - -% Update motor speeds -% if isfield(handles,'movList') && isfield(handles,'motors') -% movements = handles.movList(outMov); -% for i = 1:size(movements,2) -% movMotors = movements(i).motor; -% for j = 1:size(movMotors,2) -% handles.motors(movMotors(j)).pct = ... -% Speed2Pct(movSpeed(outMov(i)), maxSpeed, maxPct(movMotors(j))); -% end -% end -% end - -% Update VRE/ARE speeds -if isfield(patRec.control, 'currentDegPerMov') - patRec.control.currentDegPerMov = movSpeed; - %handles.speeds = round(handles.speeds); -end - -end - -%% Internal Speed to Pct converter -function pct = Speed2Pct(movSpeed, maxSpeed, maxPct) - pct = maxPct./maxSpeed .* movSpeed; -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Ramp control strategy. Sets speed for VRE and PWMs depending on how many +% consecutive predictions a motions has. Still to be implemented, +% misclassification protection that saves nPredicted in the case of +% intermittent misclassifications. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-16 / Joel Falk-Dahlin / Creation +% 2012-10-26 / Joel Falk-Dahlin / Changed maxSpeed to be set from desired +% output speed instead of a separate +% variable for the Ramp algorithm. This +% was necessary for prop.control +% 20xx-xx-xx / Author / Comment on update + +function [patRec, outMov] = Ramp(patRec, outMov, outVec) + +% Read Values from patRec object +maxSpeed = patRec.control.currentDegPerMov; +rampLength = patRec.control.controlAlg.parameters.rampLength; +downCount = patRec.control.controlAlg.parameters.downCount; +nPredicted = patRec.control.controlAlg.prop.nPredicted; +maxPct = patRec.control.controlAlg.prop.maxPct; + +% Save movement in index vector rather then as numbers +outMovIndex = zeros(size(nPredicted)); +outMovIndex(outMov) = 1; +outMovIndex = outMovIndex > 0; + +% Add one to counter if movement is predicted +nPredicted(outMovIndex) = nPredicted(outMovIndex) + 1; + +% Remove downCount from counter if movement is not predicted +nPredicted( ~outMovIndex ) = nPredicted(~outMovIndex) - downCount; + +% Make sure that counter is not more then rampLength or less then zero +nPredicted( nPredicted > rampLength ) = rampLength; +nPredicted( nPredicted < 0 ) = 0; + +% Save nPredicted into the patRec struct +patRec.control.controlAlg.prop.nPredicted = nPredicted; + +%% Calculate movement speeds + +% Calculate ramp gain for each movement depending on how many times they +% have been predicted +rampGain = nPredicted./ rampLength; + +% Set movement speed to maxSpeed*rampGain for now (maxSpeed can be changed +% to desired speed when proportional control is implemented) +movSpeed = maxSpeed.*rampGain; + +% Set non-predicted movements to zero-speed +movSpeed(~outMovIndex) = 0; + +%% Update VRE/ARE/Motor speeds + +% Update motor speeds +% if isfield(handles,'movList') && isfield(handles,'motors') +% movements = handles.movList(outMov); +% for i = 1:size(movements,2) +% movMotors = movements(i).motor; +% for j = 1:size(movMotors,2) +% handles.motors(movMotors(j)).pct = ... +% Speed2Pct(movSpeed(outMov(i)), maxSpeed, maxPct(movMotors(j))); +% end +% end +% end + +% Update VRE/ARE speeds +if isfield(patRec.control, 'currentDegPerMov') + patRec.control.currentDegPerMov = movSpeed; + %handles.speeds = round(handles.speeds); +end + +end + +%% Internal Speed to Pct converter +function pct = Speed2Pct(movSpeed, maxSpeed, maxPct) + pct = maxPct./maxSpeed .* movSpeed; +end diff --git a/Control/RampModified.m b/Control/RampModified.m new file mode 100644 index 0000000..64bae8d --- /dev/null +++ b/Control/RampModified.m @@ -0,0 +1,131 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Handles misclassifications differently than Ramp. Instead of a +% downcounter it resumes the counter if the movement is predicted within +% nMisclassificationComp + 1 iterations. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-17 / Joel Falk-Dahlin / Creation +% 2012-10-26 / Joel Falk-Dahlin / Changed maxSpeed to be set from desired +% output speed instead of a separate +% variable for the Ramp algorithm. This +% was necessary for prop.control +% 20xx-xx-xx / Author / Comment on update + +function [patRec, outMov] = RampModified(patRec, outMov, outVec) + +%% Read Values from patRec object +maxSpeed = patRec.control.currentDegPerMov; +rampLength = patRec.control.controlAlg.parameters.rampLength; +nPredicted = patRec.control.controlAlg.prop.nPredictedBuffer(end,:); +maxPct = patRec.control.controlAlg.prop.maxPct; + +%% Update nPrediction buffer + +% Save movement in index vector rather then as numbers +outMovIndex = zeros(size(nPredicted)); +outMovIndex(outMov) = 1; +outMovIndex = outMovIndex > 0; + +% Add one to counter if movement is predicted +nPredicted(outMovIndex) = nPredicted(outMovIndex) + 1; + +% Set current nPredicted to zero for non-predicted movements +nPredicted(~outMovIndex) = 0; + +% Make sure that counter is not more then rampLength or less then zero +nPredicted( nPredicted > rampLength ) = rampLength; +nPredicted( nPredicted < 0 ) = 0; + +% Save nPredicted to nPredictedBuffer +patRec.control.controlAlg.prop.nPredictedBuffer(1:end-1,:) = patRec.control.controlAlg.prop.nPredictedBuffer(2:end,:); +patRec.control.controlAlg.prop.nPredictedBuffer(end,:) = nPredicted; + +%% Check for Misclassification compensation + +% Find all zeros in nPredicted buffer +zeroMat = patRec.control.controlAlg.prop.nPredictedBuffer == 0; +% Find all movements that contain atleast one zero +containZero = sum(zeroMat) > 0; +% Find all movements that does not only contain zeros in the buffer +notOnlyZeros = sum(zeroMat) < size(patRec.control.controlAlg.prop.nPredictedBuffer,1)-1; +% Find all movements that contain atleast one, but not only, zeros +movWithZero = containZero.*notOnlyZeros > 0; +% Find all movements with atleast one, but not only, zeros with non +% zero input (these are misclassified) +misclassifiedMoves = movWithZero.*nPredicted > 0; +% Find index of misclassified moves +misclassifiedMoves = find(misclassifiedMoves); + +% For all misclassified moves +for i = 1:length(misclassifiedMoves) + % Find all non-zero elements in buffer, except newest + iRow = find(patRec.control.controlAlg.prop.nPredictedBuffer(1:end-1,misclassifiedMoves(i))); + % Set current output to previous + 1 + patRec.control.controlAlg.prop.nPredictedBuffer(end,misclassifiedMoves(i)) = ... + patRec.control.controlAlg.prop.nPredictedBuffer(iRow(end),misclassifiedMoves(i))+1; +end + +% Make sure that the buffer never has larger values than allowed +index = patRec.control.controlAlg.prop.nPredictedBuffer(end,:) > rampLength; +patRec.control.controlAlg.prop.nPredictedBuffer(end,index) = rampLength; + +% Re-read the current nPredicted from the buffer (after misclass +% compenstion in done) +nPredicted = patRec.control.controlAlg.prop.nPredictedBuffer(end,:); + +%% Calculate movement speeds + +% Calculate ramp gain for each movement depending on how many times they +% have been predicted +rampGain = nPredicted./ rampLength; + +% Set movement speed to maxSpeed*rampGain for now (maxSpeed can be changed +% to desired speed when proportional control is implemented) +movSpeed = maxSpeed.*rampGain; + +% Set non-predicted movements to zero-speed +movSpeed(~outMovIndex) = 0; + +%% Update VRE/ARE/Motor speeds + +% Update motor speeds +% if isfield(handles,'movList') && isfield(handles,'motors') +% movements = handles.movList(outMov); +% for i = 1:size(movements,2) +% movMotors = movements(i).motor; +% for j = 1:size(movMotors,2) +% handles.motors(movMotors(j)).pct = ... +% Speed2Pct(movSpeed(outMov(i)), maxSpeed, maxPct(movMotors(j))); +% end +% end +% end + +% Update VRE/ARE speeds +if isfield(patRec.control, 'currentDegPerMov') + patRec.control.currentDegPerMov = movSpeed; + %handles.speeds = round(handles.speeds); +end +end + +%% Internal Speed to Pct converter +function pct = Speed2Pct(movSpeed, maxSpeed, maxPct) + pct = maxPct./maxSpeed .* movSpeed; +end diff --git a/Control/RampModified2.m b/Control/RampModified2.m new file mode 100644 index 0000000..71b0a13 --- /dev/null +++ b/Control/RampModified2.m @@ -0,0 +1,61 @@ +function [patRec, outMov] = RampModified2(patRec, outMov, outVec) + +% Read speeds +maxDegPerMov = patRec.control.currentDegPerMov; + +% Read counters +timesPredicted = patRec.control.controlAlg.prop.timesPredicted; +timesNotPredicted = patRec.control.controlAlg.prop.timesNotPredicted; + +% Read controlAlg parameters +nMisclassificationCompensation = patRec.control.controlAlg.parameters.nMisclassificationCompensation; +downCount = patRec.control.controlAlg.parameters.downCount; +nPredictionsToSteadyPhase = patRec.control.controlAlg.parameters.nPredictionsToSteadyPhase; +rampLength = patRec.control.controlAlg.parameters.rampLength; + +% Create vector with boolean indicies of predicted movements +predictedMovementsIndex = false(1,patRec.nOuts); +predictedMovementsIndex(outMov) = true; + +% Update Counters +timesPredicted(predictedMovementsIndex) = timesPredicted(predictedMovementsIndex) + 1; +timesNotPredicted(~predictedMovementsIndex) = timesNotPredicted(~predictedMovementsIndex) + 1; +timesNotPredicted(predictedMovementsIndex) = 0; + +% Catergorize movements in initial and steady phases +initialPhaseMovementsIdx = timesPredicted < nPredictionsToSteadyPhase; +steadyPhaseMovementsIdx = timesPredicted >= nPredictionsToSteadyPhase; + +% If movement is in initial phase and not classified, reset velocity +timesPredicted( initialPhaseMovementsIdx & ~predictedMovementsIndex ) = 0; + +% If movement is in steady phase and not classified, only reset velocity if +% enough number of predictions has been made without movement +steadyPhaseNotPredictedIdx = (steadyPhaseMovementsIdx & ~predictedMovementsIndex); +steadyPhaseToDecreaseLongIdx = steadyPhaseNotPredictedIdx & (timesNotPredicted == nMisclassificationCompensation+1); +steadyPhaseToDecreaseShortIdx = steadyPhaseNotPredictedIdx & (timesNotPredicted > nMisclassificationCompensation+1); +steadyPhaseToIncrease = steadyPhaseNotPredictedIdx & (timesNotPredicted <= nMisclassificationCompensation); +timesPredicted(steadyPhaseToDecreaseLongIdx) = timesPredicted(steadyPhaseToDecreaseLongIdx)-(nMisclassificationCompensation+1)*downCount; +timesPredicted(steadyPhaseToDecreaseShortIdx) = timesPredicted(steadyPhaseToDecreaseShortIdx)-downCount; +timesPredicted(steadyPhaseToIncrease) = timesPredicted(steadyPhaseToIncrease)+1; + +% If predicted movement is rest, reset all counters +if outMov == patRec.nOuts + timesPredicted(:) = 0; + timesNotPredicted(:) = 0; +end + +% Limit Counters so they don't count to inifinity +timesPredicted( timesPredicted > rampLength ) = rampLength; +timesPredicted( timesPredicted < 0 ) = 0; +timesNotPredicted( timesNotPredicted > nMisclassificationCompensation+2 ) = nMisclassificationCompensation+2; +timesNotPredicted( timesNotPredicted < 0 ) = 0; + +% Update Speeds, only output the speed of the predicted movement +rampGain = timesPredicted./rampLength; +patRec.control.currentDegPerMov = rampGain .* maxDegPerMov; +patRec.control.currentDegPerMov(~predictedMovementsIndex) = 0; + +% Save Counters +patRec.control.controlAlg.prop.timesPredicted = timesPredicted; +patRec.control.controlAlg.prop.timesNotPredicted = timesNotPredicted; \ No newline at end of file diff --git a/Control/RampModified3.m b/Control/RampModified3.m new file mode 100644 index 0000000..13fcaa3 --- /dev/null +++ b/Control/RampModified3.m @@ -0,0 +1,44 @@ +function [patRec, outMov] = RampModified3(patRec, outMov, outVec) + +if ismember(patRec.nOuts,outMov) && strcmp(patRec.mov{end},'Rest') + outMov = patRec.nOuts; + % Reset buffer if rest is predicted + patRec.control.controlAlg.prop.timesPredicted(:) = 0; +else + % Smooth the prediction with Store Last Prediction from buffer + lastPredictionInBuffer = patRec.control.outBuffer(1,:) > 0; + + % Update output buffer with the newly predicted movement + patRec.control.outBuffer(1:end-1,:) = patRec.control.outBuffer(2:end,:); + patRec.control.outBuffer(end,:) = zeros(1,size(patRec.control.outBuffer,2)); + patRec.control.outBuffer(end,outMov) = 1; + + % Save index of all movements occuring in buffer + increaseSpeedIdx = sum(patRec.control.outBuffer,1) > 0; + decreaseSpeedBigIdx = (lastPredictionInBuffer > 0) &(lastPredictionInBuffer ~= increaseSpeedIdx); + decreaseSpeedSmallIdx = ~increaseSpeedIdx & ~decreaseSpeedBigIdx; + + % Read controlAlg parameters and properties + timesPredicted = patRec.control.controlAlg.prop.timesPredicted; + downCount = patRec.control.controlAlg.parameters.downCount; + rampLength = patRec.control.controlAlg.parameters.rampLength; + bufferSize = patRec.control.controlAlg.parameters.bufferSize; + maxDegPerMov = patRec.control.currentDegPerMov; + + % Update time predicted vector. + timesPredicted( increaseSpeedIdx ) = timesPredicted( increaseSpeedIdx )+1; + timesPredicted( decreaseSpeedSmallIdx ) = timesPredicted( decreaseSpeedSmallIdx )-downCount; + timesPredicted( decreaseSpeedBigIdx ) = timesPredicted( decreaseSpeedBigIdx )- (bufferSize-1)*downCount; + + % Limit Counters so they don't count to inifinity + timesPredicted( timesPredicted > rampLength ) = rampLength; + timesPredicted( timesPredicted < 0 ) = 0; + + % Update Speeds, only output the speed of the predicted movement + rampGain = timesPredicted./rampLength; + patRec.control.currentDegPerMov(:) = 0; + patRec.control.currentDegPerMov(outMov) = rampGain(outMov) .* maxDegPerMov(outMov); + + % Save Counters + patRec.control.controlAlg.prop.timesPredicted = timesPredicted; +end \ No newline at end of file diff --git a/Control/ReInitControl.m b/Control/ReInitControl.m index e04b90a..d00a6da 100644 --- a/Control/ReInitControl.m +++ b/Control/ReInitControl.m @@ -1,56 +1,56 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% -% patRec = ReInitControl(patRec) -% -% Function to reinitialize the control algorithm using the parameters -% stored in the structure controlAlg.parameters. -% -% This function should be called whenever any of these parameters are -% changed inorder to make sure that the internal properties and the -% output buffer are set correctly. -% -% INPUTS: -% -% patRec - pattern recognition structure -% -% OUTPUTS: -% -% patRec - updated pattern recognition structure with reinitialized -% controlAlg using the parameters stored in controlAlg.parameters. -% -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-10-15 / Joel Falk-Dahlin / Creation (Moved from InitControl_new) -% 20xx-xx-xx / Author / Comment on update - -function patRec = ReInitControl(patRec) - - % Check if additional initialization file exists for control - % algorithm, if Init'ControlAlg'.m exists, execute it. - if isfield(patRec.control,'controlAlg') - if exist(['Init', patRec.control.controlAlg.name{1}],'file') == 2 - patRec = feval(['Init', patRec.control.controlAlg.name{1}], patRec); - end - - % Check if the current Control has a Buffer size, otherwise set to 1 - patRec = InitOutBuffer(patRec); - end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% patRec = ReInitControl(patRec) +% +% Function to reinitialize the control algorithm using the parameters +% stored in the structure controlAlg.parameters. +% +% This function should be called whenever any of these parameters are +% changed inorder to make sure that the internal properties and the +% output buffer are set correctly. +% +% INPUTS: +% +% patRec - pattern recognition structure +% +% OUTPUTS: +% +% patRec - updated pattern recognition structure with reinitialized +% controlAlg using the parameters stored in controlAlg.parameters. +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-15 / Joel Falk-Dahlin / Creation (Moved from InitControl_new) +% 20xx-xx-xx / Author / Comment on update + +function patRec = ReInitControl(patRec) + + % Check if additional initialization file exists for control + % algorithm, if Init'ControlAlg'.m exists, execute it. + if isfield(patRec.control,'controlAlg') + if exist(['Init', patRec.control.controlAlg.name{1}],'file') == 2 + patRec = feval(['Init', patRec.control.controlAlg.name{1}], patRec); + end + + % Check if the current Control has a Buffer size, otherwise set to 1 + patRec = InitOutBuffer(patRec); + end end \ No newline at end of file diff --git a/Control/ReadSensors.m b/Control/ReadSensors.m new file mode 100644 index 0000000..ec97292 --- /dev/null +++ b/Control/ReadSensors.m @@ -0,0 +1,44 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function serving as an interface between the control framework +% of BioPatRec © and IH2 Azzurra by Prensilia SRL. The function allows +% to read the External Sensors from the hardware. Please refer +% to the IH2 Azzurra manual for more information about External Sensors +% +% --------------------------Updates-------------------------- +% 20xx-xx-xx / Author / Comment on update + +function data = ReadSensors(obj, SensorsIDs, SensorsNo) + +data = NaN*ones(1,SensorsNo); + +for n = 1:SensorsNo + + id = cell2mat(SensorsIDs(n).id); + fwrite(obj,'S'); + fwrite(obj,id); + + reply = char(fread(obj,1,'char')); + if strcmp(reply,id) + rx = fread(obj, 1, 'char'); + data(1,n) = rx; + else + % set(handles.t_msg,'String','Error on A'); + end +end \ No newline at end of file diff --git a/Control/ReadValidControlAlgs.m b/Control/ReadValidControlAlgs.m index 52909d8..b02fa3e 100644 --- a/Control/ReadValidControlAlgs.m +++ b/Control/ReadValidControlAlgs.m @@ -1,101 +1,101 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% -% controlAlgs = ReadValidControlAlgs() -% -% Function to read control algorithms and default parameters from the -% text file \Control\ValidControlAlgs.txt -% -% This function is used in the initialization of a control algorithm -% (InitControl.m). -% -% INPUTS: -% -% None -% -% OUTPUTS: -% -% controlAlg - a cell containing all available controlAlg structures, -% using their default parameters. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-10-08 / Joel Falk-Dahlin / Creation -% 2012-10-09 / Joel Falk-Dahlin / Changed so parameters can be floats -% 20xx-xx-xx / Author / Comment on update - -function controlAlgs = ReadValidControlAlgs() -fid = fopen('/Control/ValidControlAlgs.txt'); - -controlAlgs = []; - -% Read the fist line of file, making sure it is not empty -% or the end of file (-1) -tline = fgetl(fid); -while isempty(tline) && sum( tline == -1 ) == 0 - tline = fgetl(fid); -end - -while sum( tline == -1 ) == 0 - - % If { character is found, read ControlAlg structure - if strcmp('{',tline(1)) - currentControlAlg = struct; - tline = fgetl(fid); - name = textscan(tline,'name = %s'); - currentControlAlg.name = name{1}; - fncH = str2func(name{1}{1}); - currentControlAlg.fnc = fncH; - tline = fgetl(fid); - tline = fgetl(fid); - - % Read parameters and default values until } character is found - parameters = []; - while ~strcmp('}',tline(1)) - currentParameter = textscan(tline,'%s = %f'); - if isempty(currentParameter{2}) - currentParameter = textscan(tline,'%s = %s'); - end - - if isnumeric(currentParameter{1,2}) - parameters.(currentParameter{1,1}{1}) = double(currentParameter{1,2}); - elseif ischar(currentParameter{1,2}{1}) - parameters.(currentParameter{1,1}{1}) = currentParameter{1,2}{1}; - end - tline = fgetl(fid); - end - - % Set read parameters to current ControlAlg - currentControlAlg.parameters = parameters; - - % Store current ControlAlg - controlAlgs = [controlAlgs, {currentControlAlg}]; - - end - - tline = fgetl(fid); - while isempty(tline) && sum( tline == -1 ) == 0 - tline = fgetl(fid); - end - -end - -fclose(fid); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% controlAlgs = ReadValidControlAlgs() +% +% Function to read control algorithms and default parameters from the +% text file \Control\ValidControlAlgs.txt +% +% This function is used in the initialization of a control algorithm +% (InitControl.m). +% +% INPUTS: +% +% None +% +% OUTPUTS: +% +% controlAlg - a cell containing all available controlAlg structures, +% using their default parameters. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-08 / Joel Falk-Dahlin / Creation +% 2012-10-09 / Joel Falk-Dahlin / Changed so parameters can be floats +% 20xx-xx-xx / Author / Comment on update + +function controlAlgs = ReadValidControlAlgs() +fid = fopen('/Control/ValidControlAlgs.txt'); + +controlAlgs = []; + +% Read the fist line of file, making sure it is not empty +% or the end of file (-1) +tline = fgetl(fid); +while isempty(tline) && sum( tline == -1 ) == 0 + tline = fgetl(fid); +end + +while sum( tline == -1 ) == 0 + + % If { character is found, read ControlAlg structure + if strcmp('{',tline(1)) + currentControlAlg = struct; + tline = fgetl(fid); + name = textscan(tline,'name = %s'); + currentControlAlg.name = name{1}; + fncH = str2func(name{1}{1}); + currentControlAlg.fnc = fncH; + tline = fgetl(fid); + tline = fgetl(fid); + + % Read parameters and default values until } character is found + parameters = []; + while ~strcmp('}',tline(1)) + currentParameter = textscan(tline,'%s = %f'); + if isempty(currentParameter{2}) + currentParameter = textscan(tline,'%s = %s'); + end + + if isnumeric(currentParameter{1,2}) + parameters.(currentParameter{1,1}{1}) = double(currentParameter{1,2}); + elseif ischar(currentParameter{1,2}{1}) + parameters.(currentParameter{1,1}{1}) = currentParameter{1,2}{1}; + end + tline = fgetl(fid); + end + + % Set read parameters to current ControlAlg + currentControlAlg.parameters = parameters; + + % Store current ControlAlg + controlAlgs = [controlAlgs, {currentControlAlg}]; + + end + + tline = fgetl(fid); + while isempty(tline) && sum( tline == -1 ) == 0 + tline = fgetl(fid); + end + +end + +fclose(fid); + end \ No newline at end of file diff --git a/Control/SPC/motors.def b/Control/SPC/motors.def new file mode 100644 index 0000000..0c89706 --- /dev/null +++ b/Control/SPC/motors.def @@ -0,0 +1,3 @@ +A,0,60 +B,0,80 +C,0,20 \ No newline at end of file diff --git a/Control/SPC/movements.def b/Control/SPC/movements.def new file mode 100644 index 0000000..a724d57 --- /dev/null +++ b/Control/SPC/movements.def @@ -0,0 +1,25 @@ +0,Rest,0,0,0 0 +1,Open Hand,9,1,1 1 +2,Close Hand,9,0,1 0 +3,Flex Hand,7,0,0 1 +4,Ext Hand,7,1,0 0 +5,Pronation,8,0,2 0 +6,Supination,8,1,2 1 +24,Side Grip,13,1,0 0 +23,Fine Grip,12,1,0 0 +22,Agree,11,1,0 0 +17,Point,10,1,0 0 +7,Thumb Ext,5,1,0 0 +8,Thumb Flex,5,0,0 0 +19,Thumb Yaw Ext,6,1,0 0 +18,Thumb Yaw Flex,6,0,0 0 +20,Flex Elbow,15,1,3 0 +21,Ext Elbow,15,0,3 1 +9,Index Ext,4,1,0 0 +10,Index Flex,4,0,0 0 +11,Middle Ext,3,1,0 0 +12,Middle Flex,3,0,0 0 +13,Ring Ext,2,1,0 0 +14,Ring Flex,2,0,0 0 +15,Little Ext,1,1,0 0 +16,Little Flex,1,0,0 0 diff --git a/Control/SendMotorCommand.m b/Control/SendMotorCommand.m new file mode 100644 index 0000000..24b1b4b --- /dev/null +++ b/Control/SendMotorCommand.m @@ -0,0 +1,51 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to implement the Control Framework +% +% It waits until the ALC acknowledges the comunication, otherwise +% it exits with a failure (result = 0). +% +% --------------------------Updates-------------------------- +% 2015-07-07 / Enzo Mastinu / Created accordingly to the standardized +% framework. It sends the motor command to the +% external device. +% 20xx-xx-xx / Author / Comment + + +function result = SendMotorCommand(obj, type, index, dir, pwm) + % Send the message type + fwrite(obj,'M','char'); + % Send the motor type + fwrite(obj,type); + % Send PWM ID + fwrite(obj,index); + % Send direction + fwrite(obj,dir) + % Send speed/position + fwrite(obj,pwm) + + % If no problems where encountered, the ALC must return ID + reply = fread(obj,1); + if reply == index + result=1; + else + result=0; + return; + end +end diff --git a/Control/ShortMotorActivation.m b/Control/ShortMotorActivation.m index 8dedd93..5ecccbd 100644 --- a/Control/ShortMotorActivation.m +++ b/Control/ShortMotorActivation.m @@ -1,93 +1,93 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Funtion to activate the motors in a selected direction and by -% a selected time. It will only activate one motor. -% -% Using the comunication object (com) move the motor (motorDir) -% in direction (motorDir), at speed (movSpeed) for a time (movTime) -% -% The motor diretion is coded by integers (1,2,3...) -% motorDir = 1 = motor 1 forward -% motorDir = 2 = motor 1 backwars -% motorDir = 3 = motor 2 forward -% motorDir = 4 = motor 2 backwars -% motorDir = . = motor . . -% motorDir = . = motor . . -% -% Each motor is identified by A, B, ... and they are -% activated by two PWMs. -% --------------------------Updates-------------------------- -% 2011-11-17 / Max Ortiz / Creation - -function ShortMotorActivation(com, movSpeed, movTime, motorDir) - -switch motorDir - case 1 - pwmID = 'A'; - pwmA = 0; - pwmB = movSpeed; - case 2 - pwmID = 'A'; - pwmA = movSpeed; - pwmB = 0; - case 3 - pwmID = 'B'; - pwmA = 0; - pwmB = movSpeed; - case 4 - pwmID = 'B'; - pwmA = movSpeed; - pwmB = 0; - case 5 - pwmID = 'C'; - pwmA = 0; - pwmB = movSpeed; - case 6 - pwmID = 'C'; - pwmA = movSpeed; - pwmB = 0; - case 7 - pwmID = 'D'; - pwmA = 0; - pwmB = movSpeed; - case 8 - pwmID = 'D'; - pwmA = movSpeed; - pwmB = 0; - case 9 - pwmID = 'E'; - pwmA = 0; - pwmB = movSpeed; - case 10 - pwmID = 'E'; - pwmA = movSpeed; - pwmB = 0; -end - -% Send motor values -if Update2PWMusingSCI(com, pwmID, pwmA, pwmB); - % Wait for a little move to take place - pause(movTime); - % Stop the movement - Update2PWMusingSCI(com, pwmID, 0, 0); - %disp('Moved'); -else - disp('Failed'); - fclose(com.io); -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Funtion to activate the motors in a selected direction and by +% a selected time. It will only activate one motor. +% +% Using the comunication object (com) move the motor (motorDir) +% in direction (motorDir), at speed (movSpeed) for a time (movTime) +% +% The motor diretion is coded by integers (1,2,3...) +% motorDir = 1 = motor 1 forward +% motorDir = 2 = motor 1 backwars +% motorDir = 3 = motor 2 forward +% motorDir = 4 = motor 2 backwars +% motorDir = . = motor . . +% motorDir = . = motor . . +% +% Each motor is identified by A, B, ... and they are +% activated by two PWMs. +% --------------------------Updates-------------------------- +% 2011-11-17 / Max Ortiz / Creation + +function ShortMotorActivation(com, movSpeed, movTime, motorDir) + +switch motorDir + case 1 + pwmID = 'A'; + pwmA = 0; + pwmB = movSpeed; + case 2 + pwmID = 'A'; + pwmA = movSpeed; + pwmB = 0; + case 3 + pwmID = 'B'; + pwmA = 0; + pwmB = movSpeed; + case 4 + pwmID = 'B'; + pwmA = movSpeed; + pwmB = 0; + case 5 + pwmID = 'C'; + pwmA = 0; + pwmB = movSpeed; + case 6 + pwmID = 'C'; + pwmA = movSpeed; + pwmB = 0; + case 7 + pwmID = 'D'; + pwmA = 0; + pwmB = movSpeed; + case 8 + pwmID = 'D'; + pwmA = movSpeed; + pwmB = 0; + case 9 + pwmID = 'E'; + pwmA = 0; + pwmB = movSpeed; + case 10 + pwmID = 'E'; + pwmA = movSpeed; + pwmB = 0; +end + +% Send motor values +if Update2PWMusingSCI(com, pwmID, pwmA, pwmB); + % Wait for a little move to take place + pause(movTime); + % Stop the movement + Update2PWMusingSCI(com, pwmID, 0, 0); + %disp('Moved'); +else + disp('Failed'); + fclose(com.io); +end diff --git a/Control/StopMotor.m b/Control/StopMotor.m index cb50582..3c9a823 100644 --- a/Control/StopMotor.m +++ b/Control/StopMotor.m @@ -1,33 +1,33 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to prevent the motor to move -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-07-10 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - - - -function StopMotor(dio, ao) - - putvalue(dio.Line([1 2]), [1 1]); % Stop motor - if exist('ao','var') - putsample(ao,0); % Exit according to slider - end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to prevent the motor to move +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-07-10 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + + + +function StopMotor(dio, ao) + + putvalue(dio.Line([1 2]), [1 1]); % Stop motor + if exist('ao','var') + putsample(ao,0); % Exit according to slider + end diff --git a/Control/TestConnection.m b/Control/TestConnection.m new file mode 100644 index 0000000..a28687d --- /dev/null +++ b/Control/TestConnection.m @@ -0,0 +1,17 @@ +function result = TestConnection(com) + + % Open connection + if ~strcmp(com.status,'open') + fopen(com); + end + % Send test message + fwrite(com,'A','char'); + fwrite(com,'C','char') + replay = char(fread(com,1,'char')); + if strcmp(replay,'C'); + result = 1; + else + result = 0; + end + +end \ No newline at end of file diff --git a/Control/VREActivation.m b/Control/VREActivation.m index 889c047..d3c108f 100644 --- a/Control/VREActivation.m +++ b/Control/VREActivation.m @@ -1,60 +1,60 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Function to move the VRE in a selected direction, given distance and a -% given amount of time. -% -% The VRE communication object (vre_Com) will be used to move the DoF (movDof) -% in the specified direction (movDir) for the given distance (movDist) over -% the specified amount of time (movTime). -% -% The movDir is specified in the following manner; -% 1 - Extension -% 0 - Flexion -% -% movDof is a character indicating which DoF to activate. -% -% movDist is a percentage of distance to move. -% -% Each motor is identified by A, B, ... and they are -% activated by two PWMs. -% --------------------------Updates-------------------------- -% 2011-12-07 / Nichlas Sander / Creation -% 20xx-xx-xx / Author / Comment on update - -function tac = VREActivation(vre_Com, movSpeed, movTime, movDof, movDir, moveTac) - if ~moveTac - %Move the normal hand. - handMoved = 1; - else - %Move the TAC hand. - handMoved = 2; - end - if ~isnumeric(movSpeed) - [movSpeed, fraction] = rat(str2double(movSpeed)); - else - [movSpeed, fraction] = rat(movSpeed); - end - tac = 0; - fwrite(vre_Com,sprintf('%c%c%c%c%c',char(handMoved),char(movDof),char(movDir),char(movSpeed), char(fraction))); - ack = fread(vre_Com,1); - if ack == 't' - %If the user is within set allowance the returned ack == 't' - tac = 1; - end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to move the VRE in a selected direction, given distance and a +% given amount of time. +% +% The VRE communication object (vre_Com) will be used to move the DoF (movDof) +% in the specified direction (movDir) for the given distance (movDist) over +% the specified amount of time (movTime). +% +% The movDir is specified in the following manner; +% 1 - Extension +% 0 - Flexion +% +% movDof is a character indicating which DoF to activate. +% +% movDist is a percentage of distance to move. +% +% Each motor is identified by A, B, ... and they are +% activated by two PWMs. +% --------------------------Updates-------------------------- +% 2011-12-07 / Nichlas Sander / Creation +% 20xx-xx-xx / Author / Comment on update + +function tac = VREActivation(vre_Com, movSpeed, movTime, movDof, movDir, moveTac) + if ~moveTac + %Move the normal hand. + handMoved = 1; + else + %Move the TAC hand. + handMoved = 2; + end + if ~isnumeric(movSpeed) + [movSpeed, fraction] = rat(str2double(movSpeed)); + else + [movSpeed, fraction] = rat(movSpeed); + end + tac = 0; + fwrite(vre_Com,sprintf('%c%c%c%c%c',char(handMoved),char(movDof),char(movDir),char(movSpeed), char(fraction))); + ack = fread(vre_Com,1); + if ack == 't' + %If the user is within set allowance the returned ack == 't' + tac = 1; + end end \ No newline at end of file diff --git a/Control/ValidControlAlgs.txt b/Control/ValidControlAlgs.txt index 344c328..ab18a27 100644 --- a/Control/ValidControlAlgs.txt +++ b/Control/ValidControlAlgs.txt @@ -1,28 +1,90 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% -% Create new ControlAlgorithms here -% -% Add parameters and default values that -% are to be read upon structure creation -% -% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -{ -name = None -Default parameters -} - -{ -name = MajorityVote -Default parameters - bufferSize = 3 -} - -{ -name = Ramp -Default parameters - downCount = 2 - rampLength = 10 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Create new PostProcessors here +% +% Add parameters and default values that +% are to be read upon object creation +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Standard Parameters +% ----------------------------------------------------- +% +% bufferSize - used by PostProcessor class to know +% how many data point to feed the current +% algorithm. It is set to 1 unless explictly +% set to something else. +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +{ +name = None +Default parameters +} + +{ +name = MajorityVote +Default parameters + bufferSize = 3 +} + +{ +name = MajorityVoteSimultaneous +Default parameters + bufferSize = 4 +} + +{ +name = BayesianFusion +Default parameters + bufferSize = 3 + weight = 1 +} + +{ +name = BufferOutput +Default parameters + bufferSize = 3 +} + +{ +name = Ramp +Default properties + downCount = 2 + rampLength = 10 +} + +{ +name = RampModified +Default properties + nMisclassComp = 2 + rampLength = 10 +} + +{ +name = RampModified2 +Default properties + nPredictionsToSteadyPhase = 3 + nMisclassificationCompensation = 2 + downCount = 2 + rampLength = 10 +} + +{ +name = RampModified3 +Default properties + bufferSize = 3 + downCount = 2 + rampLength = 10 +} + +{ +name = CombinedControl +Default parameters + controlOne = MajorityVoteSimultaneous + controlTwo = Ramp } \ No newline at end of file diff --git a/Comm/motor.m b/Control/motor.m similarity index 97% rename from Comm/motor.m rename to Control/motor.m index 6480282..3224605 100644 --- a/Comm/motor.m +++ b/Control/motor.m @@ -1,40 +1,40 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% A class used to keep track of which motor, pwmID/motorID, is what type of -% motor. This to differentiate between the ones in the hand and the ones -% for the pan/tilt. It also keeps track of the current position of the -% movement. -% --------------------------Updates-------------------------- -% 2012-05-25 / Nichlas Sander / Creation -% 20xx-xx-xx / Author / Comment on update - -classdef motor - properties - id - type - pct - end - methods - function obj = motor(motorId,motorType,position) - obj.id = motorId; - obj.type = motorType; - obj.pct = position; - end - end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% A class used to keep track of which motor, pwmID/motorID, is what type of +% motor. This to differentiate between the ones in the hand and the ones +% for the pan/tilt. It also keeps track of the current position of the +% movement. +% --------------------------Updates-------------------------- +% 2012-05-25 / Nichlas Sander / Creation +% 20xx-xx-xx / Author / Comment on update + +classdef motor + properties + id + type + pct + end + methods + function obj = motor(motorId,motorType,position) + obj.id = motorId; + obj.type = motorType; + obj.pct = position; + end + end end \ No newline at end of file diff --git a/Comm/movement.m b/Control/movement.m similarity index 97% rename from Comm/movement.m rename to Control/movement.m index ba010c9..0b960c1 100644 --- a/Comm/movement.m +++ b/Control/movement.m @@ -1,55 +1,55 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% This is a class to keep track of the movements which are performed. It -% holds the id and direction needed to move the VRE, and the ids of the -% motors which need to be activated. -% --------------------------Updates-------------------------- -% 2012-05-25 / Nichlas Sander / Creation -% 20xx-xx-xx / Author / Comment on update - -classdef movement - properties - id - name - idVRE - vreDir - motor - end - - methods - function obj = movement(id,name,idVRE,direction,motor) - obj.id = id; - obj.name = name; - obj.idVRE = idVRE; - obj.vreDir = direction; - obj.motor = motor; - end - - function [Movements, Motors] = Activate(obj, VRE, Motor, Distance, Movements, Motors) - if(VRE) - %Activate VRE with correct Direction and Distance - disp('vre'); - end - if(Motor) - %Activate Motor with correct Direction and Distance - disp('motor'); - end - end - end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% This is a class to keep track of the movements which are performed. It +% holds the id and direction needed to move the VRE, and the ids of the +% motors which need to be activated. +% --------------------------Updates-------------------------- +% 2012-05-25 / Nichlas Sander / Creation +% 20xx-xx-xx / Author / Comment on update + +classdef movement + properties + id + name + idVRE + vreDir + motor + end + + methods + function obj = movement(id,name,idVRE,direction,motor) + obj.id = id; + obj.name = name; + obj.idVRE = idVRE; + obj.vreDir = direction; + obj.motor = motor; + end + + function [Movements, Motors] = Activate(obj, VRE, Motor, Distance, Movements, Motors) + if(VRE) + %Activate VRE with correct Direction and Distance + disp('vre'); + end + if(Motor) + %Activate Motor with correct Direction and Distance + disp('motor'); + end + end + end end \ No newline at end of file diff --git a/Control/sensor.m b/Control/sensor.m new file mode 100644 index 0000000..7d5a7ab --- /dev/null +++ b/Control/sensor.m @@ -0,0 +1,35 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% A class used to keep track of sensors. +% --------------------------Updates-------------------------- +% 2012-05-25 / Sebastian Karlsson / Creation +% 20xx-xx-xx / Author / Comment on update + +classdef sensor + properties + id + name + end + methods + function obj = sensor(sensorId, sensorName) + obj.id = sensorId; + obj.name = sensorName; + end + end +end \ No newline at end of file diff --git a/GUI_BioPatRec.fig b/GUI_BioPatRec.fig index c613f2fd6d77f4c7da10aabe2a7046b2717bf001..919368956c800be0d2e001b1d1e7f0cdc7153e68 100644 GIT binary patch delta 4661 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 delta 4491 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 diff --git a/GUI_BioPatRec.m b/GUI_BioPatRec.m index da7c04f..31ca01f 100644 --- a/GUI_BioPatRec.m +++ b/GUI_BioPatRec.m @@ -45,7 +45,7 @@ % Edit the above text to modify the response to help GUI_BioPatRec -% Last Modified by GUIDE v2.5 22-Jun-2011 14:55:52 +% Last Modified by GUIDE v2.5 27-Jan-2015 08:52:57 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; @@ -107,13 +107,14 @@ function GUI_BioPatRec_OpeningFcn(hObject, eventdata, handles, varargin) varargout{1} = handles.output; -% --- Executes on button press in pb_Recordings. -function pb_Recordings_Callback(hObject, eventdata, handles) -% hObject handle to pb_Recordings (see GCBO) +% --- Executes on button press in pb_FastRecording. +function pb_FastRecording_Callback(hObject, eventdata, handles) +% hObject handle to pb_FastRecording (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) +fast = 1; +GUI_Recordings(fast); -GUI_Recordings; % --- Executes on button press in pb_PatRec. @@ -129,3 +130,19 @@ function pb_RecordingSession_Callback(hObject, eventdata, handles) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) GUI_RecordingSession; + + +% --- Executes on button press in pb_PatRec_Default. +function pb_PatRec_Default_Callback(hObject, eventdata, handles) +% hObject handle to pb_PatRec_Default (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +UseDefaults(handles.pb_PatRec_Default, 'false'); + + +% --- Executes on button press in pb_PatRec_DefaultSimul. +function pb_PatRec_DefaultSimul_Callback(hObject, eventdata, handles) +% hObject handle to pb_PatRec_DefaultSimul (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +UseDefaults(handles.pb_PatRec_DefaultSimul, 'true'); diff --git a/LICENSE.txt b/LICENSE.txt index f54ebdf..1490f54 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,21 +1,21 @@ - BioPatRec, Copyright (C) 2009 Max J. Ortiz C. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 3 of the - License, or any later version. - - 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. - - The full GNU GLP and LGPL licenses are available at - . - - This software was made open to the public with the hope that through - community contributions, we can all make the prosthetic developments - required to help patients in need. - - Everybody is welcome to sum efforts and contribute to this project. + BioPatRec, Copyright (C) 2009 Max J. Ortiz C. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or any later version. + + 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. + + The full GNU GLP and LGPL licenses are available at + . + + This software was made open to the public with the hope that through + community contributions, we can all make the prosthetic developments + required to help patients in need. + + Everybody is welcome to sum efforts and contribute to this project. Please contact: maxo@chalmers.se for more information. \ No newline at end of file diff --git a/PatRec/ANN/ANN_Accuracy.m b/PatRec/ANN/ANN_Accuracy.m index 44c3eab..6f779cc 100644 --- a/PatRec/ANN/ANN_Accuracy.m +++ b/PatRec/ANN/ANN_Accuracy.m @@ -1,74 +1,74 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Evaluates the accuracy for the ANN MLP -% -% ------------------------- Updates & Contributors ------------------------ -% 2011-09-20 / Max Ortiz / New routine for BioPatRec based in previous implementation -% for EMG_AQ (maxo@chalmers.se) -% 2011-10-09 / Max Ortiz / -% - -function [accP rmse accN] = ANN_Accuracy(patRec, ANN, x, y) - - nSets = length(x(:,1)); - nM = size(y,2); % Number of movements - nSpM = nSets/nM ; % Number of sets per movement. - accN = zeros(nM,1); - accP = zeros(nM,1); - rmse = zeros(nM,1); - - if strcmp(ANN.Type,'Perceptron') - setIdx = 1; - for i = 1 : nM % Run through number of movements - cP = 0; - cN = 0; - mse = 0; - for j = 1 : nSpM % Run trhough the number of sets per movement - - %Normalize set - tSet = NormalizeSet(x(setIdx,:), patRec); - - % Evaluate ANN - ANN = EvaluateANN(tSet,ANN); - - % Decide output - rO = round(ANN.o); - - % Compute performance - cP = cP + sum(and(rO, y(setIdx,:)')); % Correct Patterns - cN = cN + sum((rO == y(setIdx,:)')); % correct neurons - - mse = mse + sum((ANN.o' - y(setIdx,:)).^2); %mean square error - - setIdx = setIdx + 1; - end - accP(i) = cP / nSpM; - accN(i) = cN / (nSpM * nM); - rmse(i) = sqrt(mse/(nSpM * nM)); % Root mean square error - end - - accP(end + 1) = mean(accP); - accN(end + 1) = mean(accN); - rmse(end + 1) = mean(rmse); - end - - % Validation routine - % 110920 MO NOTE: To be implemented - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Evaluates the accuracy for the ANN MLP +% +% ------------------------- Updates & Contributors ------------------------ +% 2011-09-20 / Max Ortiz / New routine for BioPatRec based in previous implementation +% for EMG_AQ (maxo@chalmers.se) +% 2011-10-09 / Max Ortiz / +% + +function [accP rmse accN] = ANN_Accuracy(patRec, ANN, x, y) + + nSets = length(x(:,1)); + nM = size(y,2); % Number of movements + nSpM = nSets/nM ; % Number of sets per movement. + accN = zeros(nM,1); + accP = zeros(nM,1); + rmse = zeros(nM,1); + + if strcmp(ANN.Type,'Perceptron') + setIdx = 1; + for i = 1 : nM % Run through number of movements + cP = 0; + cN = 0; + mse = 0; + for j = 1 : nSpM % Run trhough the number of sets per movement + + %Normalize set + tSet = NormalizeSet(x(setIdx,:), patRec); + + % Evaluate ANN + ANN = EvaluateANN(tSet,ANN); + + % Decide output + rO = round(ANN.o); + + % Compute performance + cP = cP + sum(and(rO, y(setIdx,:)')); % Correct Patterns + cN = cN + sum((rO == y(setIdx,:)')); % correct neurons + + mse = mse + sum((ANN.o' - y(setIdx,:)).^2); %mean square error + + setIdx = setIdx + 1; + end + accP(i) = cP / nSpM; + accN(i) = cN / (nSpM * nM); + rmse(i) = sqrt(mse/(nSpM * nM)); % Root mean square error + end + + accP(end + 1) = mean(accP); + accN(end + 1) = mean(accN); + rmse(end + 1) = mean(rmse); + end + + % Validation routine + % 110920 MO NOTE: To be implemented + end \ No newline at end of file diff --git a/PatRec/ANN/ANN_Perceptron.m b/PatRec/ANN/ANN_Perceptron.m index 81b06ec..f9b760a 100644 --- a/PatRec/ANN/ANN_Perceptron.m +++ b/PatRec/ANN/ANN_Perceptron.m @@ -1,212 +1,221 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to train the Artificial Neural Network using the so called Back -% Propagation Algorithm -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-07-22 / Max Ortiz / Creation -% 2009-10-27 / Max Ortiz / Reset update -% 2011-09-10 / Max Ortiz / New routine for BioPatRec based in previous implementation -% for EMG_AQ -% 2011-09-10 / Max Ortiz / Optimized for speed. Creationg of FastTestANN and -% FullTestANN routines -% 2012-04-01 / Max Ortiz / Change condition of passV to break training -% faster -% 20xx-xx-xx / Author / Comment on update - -function [ANN acc] = ANN_Perceptron(trSet, trOut, vSet, vOut, tType) - -%% Settings -graph2d = 1; % Show 2D graph -nRepF = 400; % Number of repeated rmse before restart -maxItr = 200; % Maximum number iterations -batchTr = 0; % Batch training (1) or sthocastic training (0) - -%% Initialize Artificial Neural Network, PSO and EA -nOn = size(trOut,2); % Number of output neurons -nIn = size(trSet,2); % Number of input neurons -nHn = [nIn nIn]; % Number of hiden neurons -%nHn = [nIn*2 nIn*2 nIn*2 nIn*2]; -%nHn = nIn*3; % Number of hiden neurons - -ANN = InitANN_Perceptron(nIn,nHn,nOn); - -% Randomize weights -+1 -ANN.w(1:max([nHn nIn nOn]),1:max([nHn nIn nOn]),1:length(nHn)+1) = -1 + rand(max([nHn nIn nOn]),max([nHn nIn nOn]),length(nHn)+1) .* 2; % weight -ANN.b(1:max(nHn),1:length(nHn)+1) = -1 + rand(max(nHn),length(nHn)+1) .* 2; % bias -tempANN = ANN; - -% For Brackpropagation -eta = 0.1; % learning rate -alpha = 0.1; -% For PSO -if strcmp(tType,'PSO') - PSO = InitPSO_MLP(ANN); -end - - -%% Train and test the ANN -% Variables for the run -rmse = zeros(1,maxItr); -sim = 0; -eval = 0; -passV = 0; % Pass test of the validation test -resetFlag = 0; -nTrSets = length(trOut(:,1)); - -if batchTr - nEvalRun = nTrSets; -else - nEvalRun = fix(nTrSets*.7); -end - -% Graph ------------------------- -if graph2d - hfig = figure; - hold on - set(hfig, 'DoubleBuffer','on'); - axis([1 maxItr 0 .3]); - hbestplot = plot(1:maxItr+1,zeros(1,maxItr+1)); - htext = text(50,0.2,sprintf('best: %4.3f',0.0)); - xlabel('simulations'); - ylabel('rmse'); - hold off - drawnow; -end -% -------------------------------- - -while sim <= maxItr && passV == 0 - sim = sim + 1; - - %% Training - % randomize the training sets - p = randperm(nTrSets); - % train ANN - if strcmp(tType,'Backpropagation') - for i = 1 : nEvalRun - ANN = EvaluateANN(trSet(p(i),:), ANN); - ANN = Backpropagation(trSet(p(i),:),trOut(p(i),:),ANN, eta, alpha); - end - elseif strcmp(tType,'PSO') - - [PSO, ANN] = PSO_MLP(PSO, ANN, trSet(p(1:nEvalRun),:),trOut(p(1:nEvalRun),:)); - - end - % Quantify the number of evaluations - eval = eval + nEvalRun; - - %% Validaion - [apV, rmse(sim)] = FastTestANN(ANN,vSet,vOut); - ANN.apV = apV; - ANN.fV = rmse(sim); - - % Save best bpANN so far - if ANN.apV >= tempANN.apV && ANN.fV <= tempANN.fV - tempANN = ANN; - end - - % Stop training if fitness is less than 0.1 and no progress is observed - if ANN.fV < 0.1 - if mean(rmse(sim-fix(sim/5):sim)) < mean(rmse(sim-fix(sim/15):sim)) - %if mean(rmse(sim-fix(sim/3):sim)) < mean(rmse(sim-fix(sim/15):sim)) - %if mean(rmse(sim-10:sim)) < mean(rmse(sim-5:sim)) % Not good for fast trainings - passV = 1; - end - end - - % Stop training if rmse is les than 1 for both Tr and V - % Validation of trSet - %[apTr, rmseTr] = FastTestANN(ANN,trSet,trOut); - %ANN.apTr = apTr; - %ANN.fTr = rmseTr; - %if ANN.fTr < 0.1 && ANN.fV < 0.1 - % passV = 1; - %end - - % Reset Back P - if sim >= 1 + nRepF * (resetFlag + 1) - if mean(rmse(sim-fix(nRepF/4):sim)) <= mean(rmse(sim-10:sim)) % Consider the last 1/4 of the training to - if ANN.fTr < 0.1 && (min(rmse(sim-5)) < 0.1 || ANN.apV > 90) - passV = 1; - else - %set(handles.t_msg,'String','Reset BP...'); - disp('Reset training...'); - resetFlag = resetFlag + 1; - %set(handles.t_bpRes,'String',num2str(resetFlag)); - ANN.w(1:max([nHn nIn nOn]),1:max([nHn nIn nOn]),1:length(nHn)+1) = -1 + rand(max([nHn nIn nOn]),max([nHn nIn nOn]),length(nHn)+1) .* 2; % weight - ANN.b(1:max(nHn),1:length(nHn)+1) = -1 + rand(max(nHn),length(nHn)+1) .* 2; % bias - end - end - end - - % 2D Graph - if graph2d - plotvector = get(hbestplot,'YData'); - plotvector(sim) = rmse(sim); - set(hbestplot,'YData',plotvector); - set(htext,'String',sprintf('best: %4.3f',tempANN.fV)); - drawnow; - end -end - -% Accept training if the fitness of the validation set < 0.1 and acc > 95 -if ANN.fV < 0.1 || ANN.apV > 95 - passV = 1; -end - -% Full TEST of the ANNs -ANN = tempANN; -[apTr, fTr, aTr] = FullTestANN(ANN,trSet,trOut); -ANN.aTr = aTr; -ANN.apTr = apTr; -ANN.fTr = fTr; - -[apV, fV, aV] = FullTestANN(ANN,vSet,vOut); -ANN.aV = aV; -ANN.apV = apV; -ANN.fV = fV; - -% General info on training performance -ANN.eval = eval; -ANN.sim = sim; - -% Return accuracy for the validation set -acc = apV; - -% Messages - -disp(['Simulations: ' num2str(sim)]); -disp(['General rmse: ' num2str(rmse(sim))]); -disp('RMES V: '); -disp(ANN.fV); -disp('Acc V: '); -disp(ANN.apV); - - -if passV - disp('%%%%%%%%%%% ANN training completed %%%%%%%%%%%%%'); -% set(handles.t_msg,'String','ANNs training done'); -else -% set(handles.t_msg,'String','ANNs training not successful'); - disp('%%%%%%%%%%% ANN training failed %%%%%%%%%%%%%'); -end - -close(hfig); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to train the Artificial Neural Network using the so called Back +% Propagation Algorithm +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-07-22 / Max Ortiz / Creation +% 2009-10-27 / Max Ortiz / Reset update +% 2011-09-10 / Max Ortiz / New routine for BioPatRec based in previous implementation +% for EMG_AQ +% 2011-09-10 / Max Ortiz / Optimized for speed. Creationg of FastTestANN and +% FullTestANN routines +% 2012-04-01 / Max Ortiz / Change condition of passV to break training +% faster +% 20xx-xx-xx / Author / Comment on update + +function [ANN acc] = ANN_Perceptron(trSet, trOut, vSet, vOut, tType) + +%% Settings +graph2d = 1; % Show 2D graph +nRepF = 400; % Number of repeated rmse before restart +maxItr = 200; % Maximum number iterations +batchTr = 0; % Batch training (1) or sthocastic training (0) + +%% Initialize Artificial Neural Network, PSO and EA +nOn = size(trOut,2); % Number of output neurons +nIn = size(trSet,2); % Number of input neurons +nHn = [nIn nIn]; % Number of hiden neurons +%nHn = [nIn*2 nIn*2 nIn*2 nIn*2]; +%nHn = nIn*3; % Number of hiden neurons + +ANN = InitANN_Perceptron(nIn,nHn,nOn); + +% Randomize weights -+1 +ANN.w(1:max([nHn nIn nOn]),1:max([nHn nIn nOn]),1:length(nHn)+1) = -1 + rand(max([nHn nIn nOn]),max([nHn nIn nOn]),length(nHn)+1) .* 2; % weight +ANN.b(1:max(nHn),1:length(nHn)+1) = -1 + rand(max(nHn),length(nHn)+1) .* 2; % bias +tempANN = ANN; + +% For Brackpropagation +eta = 0.1; % learning rate +alpha = 0.1; +% For PSO +if strcmp(tType,'PSO') + PSO = InitPSO_MLP(ANN); +end + + +%% Train and test the ANN +% Variables for the run +rmse = zeros(1,maxItr); +sim = 0; +eval = 0; +passV = 0; % Pass test of the validation test +resetFlag = 0; +nTrSets = length(trOut(:,1)); + +if batchTr + nEvalRun = nTrSets; +else + nEvalRun = fix(nTrSets*.7); +end + +% Graph ------------------------- +if graph2d + hfig = figure; + hold on + set(hfig, 'DoubleBuffer','on'); + axis([1 maxItr 0 .3]); + hbestplot = plot(1:maxItr+1,zeros(1,maxItr+1)); + htext = text(50,0.2,sprintf('best: %4.3f',0.0)); + xlabel('simulations'); + ylabel('rmse'); + hold off + drawnow; +end +% -------------------------------- + +while sim <= maxItr && passV == 0 + sim = sim + 1; + + %% Training + % randomize the training sets + p = randperm(nTrSets); + % train ANN + if strcmp(tType,'Backpropagation') + for i = 1 : nEvalRun + ANN = EvaluateANN(trSet(p(i),:), ANN); + ANN = Backpropagation(trSet(p(i),:),trOut(p(i),:),ANN, eta, alpha); + end + elseif strcmp(tType,'PSO') + + [PSO, ANN] = PSO_MLP(PSO, ANN, trSet(p(1:nEvalRun),:),trOut(p(1:nEvalRun),:)); + + elseif strcmp(tType,'BP+PSO') + % Run backpropagation + for i = 1 : nEvalRun + ANN = EvaluateANN(trSet(p(i),:), ANN); + ANN = Backpropagation(trSet(p(i),:),trOut(p(i),:),ANN, eta, alpha); + end + % funtion to W2Pos + % Run PSO + [PSO, ANN] = PSO_MLP(PSO, ANN, trSet(p(1:nEvalRun),:),trOut(p(1:nEvalRun),:)); + end + % Quantify the number of evaluations + eval = eval + nEvalRun; + + %% Validaion + [apV, rmse(sim)] = FastTestANN(ANN,vSet,vOut); + ANN.apV = apV; + ANN.fV = rmse(sim); + + % Save best bpANN so far + if ANN.apV >= tempANN.apV && ANN.fV <= tempANN.fV + tempANN = ANN; + end + + % Stop training if fitness is less than 0.1 and no progress is observed + if ANN.fV < 0.1 + if mean(rmse(sim-fix(sim/5):sim)) < mean(rmse(sim-fix(sim/15):sim)) + %if mean(rmse(sim-fix(sim/3):sim)) < mean(rmse(sim-fix(sim/15):sim)) + %if mean(rmse(sim-10:sim)) < mean(rmse(sim-5:sim)) % Not good for fast trainings + passV = 1; + end + end + + % Stop training if rmse is les than 1 for both Tr and V + % Validation of trSet + %[apTr, rmseTr] = FastTestANN(ANN,trSet,trOut); + %ANN.apTr = apTr; + %ANN.fTr = rmseTr; + %if ANN.fTr < 0.1 && ANN.fV < 0.1 + % passV = 1; + %end + + % Reset Back P + if sim >= 1 + nRepF * (resetFlag + 1) + if mean(rmse(sim-fix(nRepF/4):sim)) <= mean(rmse(sim-10:sim)) % Consider the last 1/4 of the training to + if ANN.fTr < 0.1 && (min(rmse(sim-5)) < 0.1 || ANN.apV > 90) + passV = 1; + else + %set(handles.t_msg,'String','Reset BP...'); + disp('Reset training...'); + resetFlag = resetFlag + 1; + %set(handles.t_bpRes,'String',num2str(resetFlag)); + ANN.w(1:max([nHn nIn nOn]),1:max([nHn nIn nOn]),1:length(nHn)+1) = -1 + rand(max([nHn nIn nOn]),max([nHn nIn nOn]),length(nHn)+1) .* 2; % weight + ANN.b(1:max(nHn),1:length(nHn)+1) = -1 + rand(max(nHn),length(nHn)+1) .* 2; % bias + end + end + end + + % 2D Graph + if graph2d + plotvector = get(hbestplot,'YData'); + plotvector(sim) = rmse(sim); + set(hbestplot,'YData',plotvector); + set(htext,'String',sprintf('best: %4.3f',tempANN.fV)); + drawnow; + end +end + +% Accept training if the fitness of the validation set < 0.1 and acc > 95 +if ANN.fV < 0.1 || ANN.apV > 95 + passV = 1; +end + +% Full TEST of the ANNs +ANN = tempANN; +[apTr, fTr, aTr] = FullTestANN(ANN,trSet,trOut); +ANN.aTr = aTr; +ANN.apTr = apTr; +ANN.fTr = fTr; + +[apV, fV, aV] = FullTestANN(ANN,vSet,vOut); +ANN.aV = aV; +ANN.apV = apV; +ANN.fV = fV; + +% General info on training performance +ANN.eval = eval; +ANN.sim = sim; + +% Return accuracy for the validation set +acc = apV; + +% Messages + +disp(['Simulations: ' num2str(sim)]); +disp(['General rmse: ' num2str(rmse(sim))]); +disp('RMES V: '); +disp(ANN.fV); +disp('Acc V: '); +disp(ANN.apV); + + +if passV + disp('%%%%%%%%%%% ANN training completed %%%%%%%%%%%%%'); +% set(handles.t_msg,'String','ANNs training done'); +else +% set(handles.t_msg,'String','ANNs training not successful'); + disp('%%%%%%%%%%% ANN training failed %%%%%%%%%%%%%'); +end + +close(hfig); + diff --git a/PatRec/ANN/Backp/Backpropagation.m b/PatRec/ANN/Backp/Backpropagation.m index 4ab7b73..85ef7e5 100644 --- a/PatRec/ANN/Backp/Backpropagation.m +++ b/PatRec/ANN/Backp/Backpropagation.m @@ -1,73 +1,109 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to compute the local gradient for the back-propagation algorithm -% and back-propagate -% The right most index is the layer -% The middle is the neuron -% The left most is the input -% -% ------------------------- Updates & Contributors ------------------------ -% 2011-09-10 / Max Ortiz / New routine for BioPatRec based in previous implementation -% for EMG_AQ - -function ANN = Backpropagation(in, out, ANN, eta, alpha) - - if strcmp(ANN.Type,'Perceptron') - ll = length(ANN.nHn); - ol = ll + 1; - - %Output layer neuron - for i=1:ANN.nOn - % Local gradient - ANN.lg(i,ol) = ANN.a .* (out(i) - ANN.o(i)) .* ANN.o(i) .* (1 - ANN.o(i)); - % Weight correction - %ANN.dw(1:ll,i,ol) = (alpha .* ANN.dw(1:ll,i,ol)) + (eta .* ANN.lg(i,ol) .* ANN.phi(1:ll,ll)); - ANN.dw(:,i,ol) = (alpha .* ANN.dw(:,i,ol)) + (eta .* ANN.lg(i,ol) .* ANN.phi(:,ll)); - ANN.db(i,ol) = (alpha .* ANN.db(i,ol)) + (eta .* ANN.lg(i,ol)); - end - %ANN.w(1:ll,:,ol) = ANN.w(1:ll,:,ol) + ANN.dw(1:ll,:,ol); - ANN.w(:,:,ol) = ANN.w(:,:,ol) + ANN.dw(:,:,ol); - ANN.b(:,ol) = ANN.b(:,ol) + ANN.db(:,ol); - - %Hiden Layers Layer - for l=ll : -1 : 2 - for i=1:ANN.nHn(l) - % Local gradient - ANN.lg(i,l) = ANN.a * (1 - ANN.phi(i,l)) * ANN.phi(i,l) * sum(ANN.lg(:,l+1) .* ANN.w(i,:,l+1)'); - % Weight correction - ANN.dw(:,i,l) = (alpha .* ANN.dw(:,i,l)) + (eta .* ANN.lg(i,l) .* ANN.phi(:,l-1)); - ANN.db(i,l) = (alpha .* ANN.db(i,l)) + (eta .* ANN.lg(i,l)); - ANN.w(:,i,l) = ANN.w(:,i,l) + ANN.dw(:,i,l); - ANN.b(i,l) = ANN.b(i,l) + ANN.db(i,l); - end - end - - %First layer neuron - l = 1; - lin = length(in); - for i=1:ANN.nHn(l) - % Local gradient - ANN.lg(i,l) = ANN.a * (1 - ANN.phi(i,l)) * ANN.phi(i,l) * sum(ANN.lg(:,l+1) .* ANN.w(i,:,l+1)'); - % Weight correction - ANN.dw(1:lin,i,l) = (alpha .* ANN.dw(1:lin,i,l)) + (eta .* ANN.lg(i,l) .* in'); - ANN.db(i,l) = (alpha .* ANN.db(i,l)) + (eta .* ANN.lg(i,l)); - ANN.w(:,i,l) = ANN.w(:,i,l) + ANN.dw(:,i,l); - ANN.b(i,l) = ANN.b(i,l) + ANN.db(i,l); - end - end \ No newline at end of file +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to compute the local gradient for the back-propagation algorithm +% and back-propagate +% The right most index is the layer +% The middle is the neuron +% The left most is the input +% +% ------------------------- Updates & Contributors ------------------------ +% 2011-09-10 / Max Ortiz / New routine for BioPatRec based in previous implementation +% for EMG_AQ +% 2013-01-15 / Joel Falk-Dahlin / Vectorized calculations, now faster + +function ANN = Backpropagation(in, out, ANN, eta, alpha) + + if strcmp(ANN.Type,'Perceptron') + ll = length(ANN.nHn); + ol = ll + 1; + + %Output layer neuron + % Local gradient + ANN.lg(1:ANN.nOn,ol) = ANN.a .* (out' - ANN.o) .* ANN.o .* (1 - ANN.o); + % Weight correction + ANN.dw(:,1:ANN.nOn,ol) = (alpha .* ANN.dw(:,1:ANN.nOn,ol)) + (eta .* bsxfun(@times, ANN.lg(1:ANN.nOn,ol)', ANN.phi(:,ll)) ); + ANN.db(1:ANN.nOn,ol) = (alpha .* ANN.db(1:ANN.nOn,ol)) + (eta .* ANN.lg(1:ANN.nOn,ol)); + ANN.w(:,:,ol) = ANN.w(:,:,ol) + ANN.dw(:,:,ol); + ANN.b(:,ol) = ANN.b(:,ol) + ANN.db(:,ol); + + % OLD CODE + %Output layer neuron +% for i=1:ANN.nOn +% %Local gradient +% ANN.lg(i,ol) = ANN.a .* (out(i) - ANN.o(i)) .* ANN.o(i) .* (1 - ANN.o(i)); +% %Weight correction +% ANN.dw(1:ll,i,ol) = (alpha .* ANN.dw(1:ll,i,ol)) + (eta .* ANN.lg(i,ol) .* ANN.phi(1:ll,ll)); +% ANN.dw(:,i,ol) = (alpha .* ANN.dw(:,i,ol)) + (eta .* ANN.lg(i,ol) .* ANN.phi(:,ll)); +% ANN.db(i,ol) = (alpha .* ANN.db(i,ol)) + (eta .* ANN.lg(i,ol)); +% end +% ANN.w(1:ll,:,ol) = ANN.w(1:ll,:,ol) + ANN.dw(1:ll,:,ol); +% ANN.w(:,:,ol) = ANN.w(:,:,ol) + ANN.dw(:,:,ol); +% ANN.b(:,ol) = ANN.b(:,ol) + ANN.db(:,ol); + + + %Hiden Layers Layer + for l=ll : -1 : 2 + + % Local gradient + ANN.lg(1:ANN.nHn(l),l) = ANN.a * (1 - ANN.phi(1:ANN.nHn(l),l)) .* ANN.phi(1:ANN.nHn(l),l) .* ( ANN.w(1:ANN.nHn(l),:,l+1) * ANN.lg(:,l+1) ); + % Weight correction + ANN.dw(:,1:ANN.nHn(l),l) = (alpha .* ANN.dw(:,1:ANN.nHn(l),l)) + (eta .* bsxfun(@times, ANN.lg(1:ANN.nHn(l),l)', ANN.phi(:,l-1)) ); + ANN.db(1:ANN.nHn(l),l) = (alpha .* ANN.db(1:ANN.nHn(l),l)) + (eta .* ANN.lg(1:ANN.nHn(l),l)); + ANN.w(:,1:ANN.nHn(l),l) = ANN.w(:,1:ANN.nHn(l),l) + ANN.dw(:,1:ANN.nHn(l),l); + ANN.b(1:ANN.nHn(l),l) = ANN.b(1:ANN.nHn(l),l) + ANN.db(1:ANN.nHn(l),l); + + % OLD CODE +% for i=1:ANN.nHn(l) +% % Local gradient +% ANN.lg(i,l) = ANN.a * (1 - ANN.phi(i,l)) * ANN.phi(i,l) * sum(ANN.lg(:,l+1) .* ANN.w(i,:,l+1)'); +% % Weight correction +% ANN.dw(:,i,l) = (alpha .* ANN.dw(:,i,l)) + (eta .* ANN.lg(i,l) .* ANN.phi(:,l-1)); +% ANN.db(i,l) = (alpha .* ANN.db(i,l)) + (eta .* ANN.lg(i,l)); +% ANN.w(:,i,l) = ANN.w(:,i,l) + ANN.dw(:,i,l); +% ANN.b(i,l) = ANN.b(i,l) + ANN.db(i,l); +% end + + end + + %First layer neuron + l = 1; + lin = length(in); + + %Local gradient + ANN.lg(1:ANN.nHn(l),l) = ANN.a * (1 - ANN.phi(1:ANN.nHn(l),l)) .* ANN.phi(1:ANN.nHn(l),l) .* (ANN.w(1:ANN.nHn(l),:,l+1) * ANN.lg(:,l+1)); + %Weight correction + ANN.dw(1:lin,1:ANN.nHn(l),l) = (alpha .* ANN.dw(1:lin,1:ANN.nHn(l),l)) + (eta .* bsxfun(@times, ANN.lg(1:ANN.nHn(l),l)', in' ) ); + ANN.db(1:ANN.nHn(l),l) = (alpha .* ANN.db(1:ANN.nHn(l),l)) + (eta .* ANN.lg(1:ANN.nHn(l),l)); + ANN.w(:,1:ANN.nHn(l),l) = ANN.w(:,1:ANN.nHn(l),l) + ANN.dw(:,1:ANN.nHn(l),l); + ANN.b(1:ANN.nHn(l),l) = ANN.b(1:ANN.nHn(l),l) + ANN.db(1:ANN.nHn(l),l); + + % OLD CODE +% for i=1:ANN.nHn(l) +% % Local gradient +% ANN.lg(i,l) = ANN.a * (1 - ANN.phi(i,l)) * ANN.phi(i,l) * sum(ANN.lg(:,l+1) .* ANN.w(i,:,l+1)'); +% % Weight correction +% ANN.dw(1:lin,i,l) = (alpha .* ANN.dw(1:lin,i,l)) + (eta .* ANN.lg(i,l) .* in'); +% ANN.db(i,l) = (alpha .* ANN.db(i,l)) + (eta .* ANN.lg(i,l)); +% ANN.w(:,i,l) = ANN.w(:,i,l) + ANN.dw(:,i,l); +% ANN.b(i,l) = ANN.b(i,l) + ANN.db(i,l); +% end + end + +end + \ No newline at end of file diff --git a/PatRec/ANN/Eval/EvaluateANN.m b/PatRec/ANN/Eval/EvaluateANN.m index 1ab2835..757209b 100644 --- a/PatRec/ANN/Eval/EvaluateANN.m +++ b/PatRec/ANN/Eval/EvaluateANN.m @@ -1,80 +1,80 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Evaluation of the ANN which comprises the sum of the weights and bias per -% neuron. The neuron output is given by the activation function (Phi). -% -% Activation function: Log-sigmoid or logistic function which provides an -% output between 0 and 1. -% -% ANN.a Amplitude of the activation function -% ANN.v Vector containing th sum of neuron inputs -% ANN.phi Activation function -% ANN.o ANN output per neuron using the activation function Phi -% -% ------------------------- Updates & Contributors ------------------------ -% 2009-07-22 / Max Ortiz / Modify to hanlde the new ANN MLP -% 2011-09-10 / Max Ortiz / New routine for BioPatRec based in previous implementation -% for EMG_AQ -% 2011-10-09 / Max Ortiz / Improved documentation -% 2011-11-23 / Joel Falk-Dahlin / Vectorized implementation for great speed -% improvement - -function ANN = EvaluateANN(inputs, ANN) - - if strcmp(ANN.Type,'Perceptron') - ll = length(ANN.nHn); - - %First Inpute Layer - - ANN.v(:,1) = ANN.w(1:length(inputs),:,1)'*inputs'+ANN.b(:,1); - ANN.phi(:,1) = 1 ./ (1 + exp(-ANN.a .* ANN.v(:,1))); - -% for i=1:ANN.nHn(1) -% ANN.v(i,1) = sum(ANN.w(1:length(inputs),i,1) .* inputs') + ANN.b(i,1); % Induced local field -% ANN.phi(i,1) = 1 /(1 + exp(-ANN.a * ANN.v(i,1))); -% end - - %Hiden Layers Layer - for j=2:ll - - ANN.v(:,j) = ANN.w(1:ANN.nHn(j-1),:,j)' * ANN.phi(1:ANN.nHn(j-1),j-1) + ANN.b(:,j); - ANN.phi(:,j) = 1 ./ (1 + exp(-ANN.a .* ANN.v(:,j))); - -% for i=1:ANN.nHn(j) -% ANN.v(i,j) = sum(ANN.w(1:ANN.nHn(j-1),i,j) .* ANN.phi(1:ANN.nHn(j-1),j-1)) + ANN.b(i,j); % Induced local field -% ANN.phi(i,j) = 1 /(1 + exp(-ANN.a * ANN.v(i,j))); -% end - - end - - %Output layer neuron - ol = ll + 1; - - ANN.v(:,ol) = ANN.w(1:ANN.nHn(end),:,ol)' * ANN.phi(1:ANN.nHn(end),ll) + ANN.b(:,ol); - ANN.o = 1 ./ (1 + exp(-ANN.a .* ANN.v(1:ANN.nOn,ol))); - -% for i=1:ANN.nOn -% ANN.v(i,ol) = sum(ANN.w(1:ANN.nHn(end),i,ol) .* ANN.phi(1:ANN.nHn(end),ll)) + ANN.b(i,ol); % Induced local field -% ANN.o(i) = 1 /(1 + exp(-ANN.a * ANN.v(i,ol))); -% end - - elseif strcmp(ANN.Type,'Other') - - end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Evaluation of the ANN which comprises the sum of the weights and bias per +% neuron. The neuron output is given by the activation function (Phi). +% +% Activation function: Log-sigmoid or logistic function which provides an +% output between 0 and 1. +% +% ANN.a Amplitude of the activation function +% ANN.v Vector containing th sum of neuron inputs +% ANN.phi Activation function +% ANN.o ANN output per neuron using the activation function Phi +% +% ------------------------- Updates & Contributors ------------------------ +% 2009-07-22 / Max Ortiz / Modify to hanlde the new ANN MLP +% 2011-09-10 / Max Ortiz / New routine for BioPatRec based in previous implementation +% for EMG_AQ +% 2011-10-09 / Max Ortiz / Improved documentation +% 2011-11-23 / Joel Falk-Dahlin / Vectorized implementation for great speed +% improvement + +function ANN = EvaluateANN(inputs, ANN) + + if strcmp(ANN.Type,'Perceptron') + ll = length(ANN.nHn); + + %First Inpute Layer + + ANN.v(:,1) = ANN.w(1:length(inputs),:,1)'*inputs'+ANN.b(:,1); + ANN.phi(:,1) = 1 ./ (1 + exp(-ANN.a .* ANN.v(:,1))); + +% for i=1:ANN.nHn(1) +% ANN.v(i,1) = sum(ANN.w(1:length(inputs),i,1) .* inputs') + ANN.b(i,1); % Induced local field +% ANN.phi(i,1) = 1 /(1 + exp(-ANN.a * ANN.v(i,1))); +% end + + %Hiden Layers Layer + for j=2:ll + + ANN.v(:,j) = ANN.w(1:ANN.nHn(j-1),:,j)' * ANN.phi(1:ANN.nHn(j-1),j-1) + ANN.b(:,j); + ANN.phi(:,j) = 1 ./ (1 + exp(-ANN.a .* ANN.v(:,j))); + +% for i=1:ANN.nHn(j) +% ANN.v(i,j) = sum(ANN.w(1:ANN.nHn(j-1),i,j) .* ANN.phi(1:ANN.nHn(j-1),j-1)) + ANN.b(i,j); % Induced local field +% ANN.phi(i,j) = 1 /(1 + exp(-ANN.a * ANN.v(i,j))); +% end + + end + + %Output layer neuron + ol = ll + 1; + + ANN.v(:,ol) = ANN.w(1:ANN.nHn(end),:,ol)' * ANN.phi(1:ANN.nHn(end),ll) + ANN.b(:,ol); + ANN.o = 1 ./ (1 + exp(-ANN.a .* ANN.v(1:ANN.nOn,ol))); + +% for i=1:ANN.nOn +% ANN.v(i,ol) = sum(ANN.w(1:ANN.nHn(end),i,ol) .* ANN.phi(1:ANN.nHn(end),ll)) + ANN.b(i,ol); % Induced local field +% ANN.o(i) = 1 /(1 + exp(-ANN.a * ANN.v(i,ol))); +% end + + elseif strcmp(ANN.Type,'Other') + + end \ No newline at end of file diff --git a/PatRec/ANN/Eval/FastTestANN.m b/PatRec/ANN/Eval/FastTestANN.m index a00fb8b..384759b 100644 --- a/PatRec/ANN/Eval/FastTestANN.m +++ b/PatRec/ANN/Eval/FastTestANN.m @@ -1,65 +1,65 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Purpose: -% -% Variables: -% accP Accuracy Patterns (in 100%) -% rmse Root mean square error -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Author / Creation -% 2011-09-20 /Max Ortiz / New routine for BioPatRec based in previous implementation -% for EMG_AQ -% - -function [accP rmse] = FastTestANN(ANN,x,y) - - nSets = length(x(:,1)); - nOuts = ANN.nOn; - %Get the deviation - d = zeros(nSets,nOuts); % This vector is used to validate output - - - dsum=0; - for i = 1:nSets - ANN = EvaluateANN(x(i,:),ANN); - d(i,:)= ANN.o; - dsum = dsum + sum((d(i,:)-y(i,:)).^2); - end - - % Correct neuron firing only / total sets - rD = round(d); - rY = round(y); - er = find(sum(abs(rD-rY),2) >= 1); %Find the index of incorrect patterns - accP = (1 - (length(er)/nSets)) * 100; - - % An alternative algorithm to compute pattern accuracy - % eq1 = (rD == rY); - % eq2 = sum(eq1,2); - % vY(1:nSets) = nOuts; - % eq3 = (eq2' == vY); - % ap = (sum(eq3)/nSets) * 100; - - rmse = sqrt(dsum/(nSets*nOuts)); % Root mean squere error - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Purpose: +% +% Variables: +% accP Accuracy Patterns (in 100%) +% rmse Root mean square error +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Author / Creation +% 2011-09-20 /Max Ortiz / New routine for BioPatRec based in previous implementation +% for EMG_AQ +% + +function [accP rmse] = FastTestANN(ANN,x,y) + + nSets = length(x(:,1)); + nOuts = ANN.nOn; + %Get the deviation + d = zeros(nSets,nOuts); % This vector is used to validate output + + + dsum=0; + for i = 1:nSets + ANN = EvaluateANN(x(i,:),ANN); + d(i,:)= ANN.o; + dsum = dsum + sum((d(i,:)-y(i,:)).^2); + end + + % Correct neuron firing only / total sets + rD = round(d); + rY = round(y); + er = find(sum(abs(rD-rY),2) >= 1); %Find the index of incorrect patterns + accP = (1 - (length(er)/nSets)) * 100; + + % An alternative algorithm to compute pattern accuracy + % eq1 = (rD == rY); + % eq2 = sum(eq1,2); + % vY(1:nSets) = nOuts; + % eq3 = (eq2' == vY); + % ap = (sum(eq3)/nSets) * 100; + + rmse = sqrt(dsum/(nSets*nOuts)); % Root mean squere error + + + + \ No newline at end of file diff --git a/PatRec/ANN/Eval/FitnessANN.m b/PatRec/ANN/Eval/FitnessANN.m index ffed5da..dc5ed23 100644 --- a/PatRec/ANN/Eval/FitnessANN.m +++ b/PatRec/ANN/Eval/FitnessANN.m @@ -1,51 +1,51 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Particle Validation or in this case, validation of the ANN's weights -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Ortiz / Creation -% 2010-05-16 / Max Ortiz / New version to return both Accuracies -% 2011-09-10 / Max Ortiz / New routine for BioPatRec based in previous implementation -% for EMG_AQ -% 2012-06-02 / Max Ortiz / Created from ValidateANN - -function fitness = FitnessANN(ANN,x,y) - - nSets = length(x(:,1)); - nOuts = ANN.nOn; - - %Get the deviation - d = zeros(nSets,nOuts); % This vector is used to validate output - - dsum=0; - for i = 1:nSets - ANN = EvaluateANN(x(i,:),ANN); - d(i,:)= ANN.o; - dsum = dsum + sum((d(i,:)-y(i,:)).^2); - end - - fitness = sqrt(dsum/(nSets*nOuts)); % for minimum - %fitness = 1/(sqrt(dsum/Nsets)) for maximum - - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Particle Validation or in this case, validation of the ANN's weights +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Ortiz / Creation +% 2010-05-16 / Max Ortiz / New version to return both Accuracies +% 2011-09-10 / Max Ortiz / New routine for BioPatRec based in previous implementation +% for EMG_AQ +% 2012-06-02 / Max Ortiz / Created from ValidateANN + +function fitness = FitnessANN(ANN,x,y) + + nSets = length(x(:,1)); + nOuts = ANN.nOn; + + %Get the deviation + d = zeros(nSets,nOuts); % This vector is used to validate output + + dsum=0; + for i = 1:nSets + ANN = EvaluateANN(x(i,:),ANN); + d(i,:)= ANN.o; + dsum = dsum + sum((d(i,:)-y(i,:)).^2); + end + + fitness = sqrt(dsum/(nSets*nOuts)); % for minimum + %fitness = 1/(sqrt(dsum/Nsets)) for maximum + + + + + \ No newline at end of file diff --git a/PatRec/ANN/Eval/FullTestANN.m b/PatRec/ANN/Eval/FullTestANN.m index 4a3fdcc..dbbce9f 100644 --- a/PatRec/ANN/Eval/FullTestANN.m +++ b/PatRec/ANN/Eval/FullTestANN.m @@ -1,66 +1,66 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% accN Accuracy Neurons -% accP Accuracy Patterns -% rmse Root mean square error -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Ortiz / Creation -% 2011-09-20 / Max Ortiz / New routine for BioPatRec based in previous implementation -% for EMG_AQ -% - -function [accP rmse accN] = FullTestANN(ANN,x,y) - - nSets = length(x(:,1)); - nM = size(y,2); % Number of movements - nSpM = nSets/nM ; % Number of sets per movement. - accN = zeros(nM,1); - accP = zeros(nM,1); - rmse = zeros(nM,1); - - setIdx = 1; - for i = 1 : nM % Run through number of movements - cP = 0; - cN = 0; - mse = 0; - for j = 1 : nSpM % Run trhough the number of sets per movement - ANN = EvaluateANN(x(setIdx,:),ANN); - rO = round(ANN.o); - cP = cP + sum(and(rO, y(setIdx,:)')); % Correct Patterns - cN = cN + sum((rO == y(setIdx,:)')); % correct neurons - - mse = mse + sum((ANN.o' - y(setIdx,:)).^2); %mean square error - - setIdx = setIdx + 1; - end - accP(i) = cP / nSpM; - accN(i) = cN / (nSpM * nM); - rmse(i) = sqrt(mse/(nSpM * nM)); % Root mean square error - end - - accP(end + 1) = mean(accP); - accN(end + 1) = mean(accN); - rmse(end + 1) = mean(rmse); - - % Validation routine - % 110920 MO NOTE: To be implemented - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% accN Accuracy Neurons +% accP Accuracy Patterns +% rmse Root mean square error +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Ortiz / Creation +% 2011-09-20 / Max Ortiz / New routine for BioPatRec based in previous implementation +% for EMG_AQ +% + +function [accP rmse accN] = FullTestANN(ANN,x,y) + + nSets = length(x(:,1)); + nM = size(y,2); % Number of movements + nSpM = nSets/nM ; % Number of sets per movement. + accN = zeros(nM,1); + accP = zeros(nM,1); + rmse = zeros(nM,1); + + setIdx = 1; + for i = 1 : nM % Run through number of movements + cP = 0; + cN = 0; + mse = 0; + for j = 1 : nSpM % Run trhough the number of sets per movement + ANN = EvaluateANN(x(setIdx,:),ANN); + rO = round(ANN.o); + cP = cP + sum(and(rO, y(setIdx,:)')); % Correct Patterns + cN = cN + sum((rO == y(setIdx,:)')); % correct neurons + + mse = mse + sum((ANN.o' - y(setIdx,:)).^2); %mean square error + + setIdx = setIdx + 1; + end + accP(i) = cP / nSpM; + accN(i) = cN / (nSpM * nM); + rmse(i) = sqrt(mse/(nSpM * nM)); % Root mean square error + end + + accP(end + 1) = mean(accP); + accN(end + 1) = mean(accN); + rmse(end + 1) = mean(rmse); + + % Validation routine + % 110920 MO NOTE: To be implemented + \ No newline at end of file diff --git a/PatRec/ANN/Eval/ValidateANN.m b/PatRec/ANN/Eval/ValidateANN.m index 111dc4e..fa120d1 100644 --- a/PatRec/ANN/Eval/ValidateANN.m +++ b/PatRec/ANN/Eval/ValidateANN.m @@ -1,75 +1,75 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Particle Validation or in this case, validation of the ANN's weights -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Ortiz / Creation -% 2010-05-16 / Max Ortiz / New vertion to return both Accuracies -% 2011-09-10 / Max Ortiz / New routine for BioPatRec based in previous implementation -% for EMG_AQ - -function [v, fitness, d, an, ap] = ValidateANN(ANN,x,y) - - nSets = length(x(:,1)); - nOuts = ANN.nOn; - - %Get the deviation - d = zeros(nSets,nOuts); % This vector is used to validate output - - %Assume that it startes validated - v = 1; - - - dsum=0; - for i = 1:nSets - ANN = EvaluateANN(x(i,:),ANN); - d(i,:)= ANN.o; - dsum = dsum + sum((d(i,:)-y(i,:)).^2); - end - - if ~isempty(find(abs(d-y) > 0.45,1)) - v=0; - end - - rD = round(d); - rY = round(y); - - % Each output firing / total firings - an = (1 - (length(find(rD-rY)) / (size(d,1)*size(d,2)))) * 100; - - % Correct neuron firing only / total sets - er = find(sum(abs(rD-rY),2) >= 1); - ap = (1 - (length(er)/nSets)) * 100; - - % An alternative algorithm to compute pattern accuracy - % eq1 = (rD == rY); - % eq2 = sum(eq1,2); - % vY(1:nSets) = nOuts; - % eq3 = (eq2' == vY); - % ap = (sum(eq3)/nSets) * 100; - - fitness = sqrt(dsum/(nSets*nOuts)); % for minimum - %fitness = 1/(sqrt(dsum/Nsets)) for maximum - - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Particle Validation or in this case, validation of the ANN's weights +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Ortiz / Creation +% 2010-05-16 / Max Ortiz / New vertion to return both Accuracies +% 2011-09-10 / Max Ortiz / New routine for BioPatRec based in previous implementation +% for EMG_AQ + +function [v, fitness, d, an, ap] = ValidateANN(ANN,x,y) + + nSets = length(x(:,1)); + nOuts = ANN.nOn; + + %Get the deviation + d = zeros(nSets,nOuts); % This vector is used to validate output + + %Assume that it startes validated + v = 1; + + + dsum=0; + for i = 1:nSets + ANN = EvaluateANN(x(i,:),ANN); + d(i,:)= ANN.o; + dsum = dsum + sum((d(i,:)-y(i,:)).^2); + end + + if ~isempty(find(abs(d-y) > 0.45,1)) + v=0; + end + + rD = round(d); + rY = round(y); + + % Each output firing / total firings + an = (1 - (length(find(rD-rY)) / (size(d,1)*size(d,2)))) * 100; + + % Correct neuron firing only / total sets + er = find(sum(abs(rD-rY),2) >= 1); + ap = (1 - (length(er)/nSets)) * 100; + + % An alternative algorithm to compute pattern accuracy + % eq1 = (rD == rY); + % eq2 = sum(eq1,2); + % vY(1:nSets) = nOuts; + % eq3 = (eq2' == vY); + % ap = (sum(eq3)/nSets) * 100; + + fitness = sqrt(dsum/(nSets*nOuts)); % for minimum + %fitness = 1/(sqrt(dsum/Nsets)) for maximum + + + + + \ No newline at end of file diff --git a/PatRec/ANN/InitANN_Perceptron.m b/PatRec/ANN/InitANN_Perceptron.m index 3773236..4abc8e1 100644 --- a/PatRec/ANN/InitANN_Perceptron.m +++ b/PatRec/ANN/InitANN_Perceptron.m @@ -1,64 +1,64 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to initialize the Artificial Neural Network -% Note: The struct and evaluation algorithms could be shorter but less -% comprehensible -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-07-23 / Max Ortiz / Creation -% 2009-11-09 / Max Ortiz / Initialization of v, phi and lg to consider a number -% of output higer than the number of neurons in the hiden layer -% 2011-09-10 / Max Ortiz / New routine for BioPatRec based in previous -% implementation for EMG_AQ -% 20xx-xx-xx / Author / Comment on update - -function ANN = InitANN_Perceptron(nIn,nHn,nOn) - -ANN.Type = 'Perceptron'; -ANN.nIn = nIn; -ANN.nHn = nHn; -ANN.nOn = nOn; -ANN.w = zeros(max([nHn nIn nOn]),max([nHn nIn nOn]),length(nHn)+1); % weight -ANN.b = zeros(max([nHn nOn]),length(nHn)+1); % bias -ANN.o = zeros(nOn,1); % output -ANN.a = 1; % amplitud for activation function - -% Back Propagation Algorithm -ANN.v = zeros(max([nHn nOn]),length(nHn)+1); % induced activation field -ANN.phi = zeros(max([nHn nOn]),length(nHn)); % activation function or output -ANN.lg = zeros(max([nHn nOn]),length(nHn)+1); % local gradient (lower delta) -ANN.dw = zeros(max([nHn nIn nOn]),max([nHn nIn nOn]),length(nHn)+1); % capital delta weight or change in weight -ANN.db = zeros(max([nHn nOn]),length(nHn)+1); % capital delta weight or change in weight of bias - -% Evolutionary Algorithm and PSO -ANN.aTr = 0; -ANN.apTr = 0; -ANN.fTr = inf; - -ANN.aV = 0; -ANN.apV = 0; -ANN.fV = inf; - -ANN.aT = 0; -ANN.apT = 0; -ANN.fT = inf; - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to initialize the Artificial Neural Network +% Note: The struct and evaluation algorithms could be shorter but less +% comprehensible +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-07-23 / Max Ortiz / Creation +% 2009-11-09 / Max Ortiz / Initialization of v, phi and lg to consider a number +% of output higer than the number of neurons in the hiden layer +% 2011-09-10 / Max Ortiz / New routine for BioPatRec based in previous +% implementation for EMG_AQ +% 20xx-xx-xx / Author / Comment on update + +function ANN = InitANN_Perceptron(nIn,nHn,nOn) + +ANN.Type = 'Perceptron'; +ANN.nIn = nIn; +ANN.nHn = nHn; +ANN.nOn = nOn; +ANN.w = zeros(max([nHn nIn nOn]),max([nHn nIn nOn]),length(nHn)+1); % weight +ANN.b = zeros(max([nHn nOn]),length(nHn)+1); % bias +ANN.o = zeros(nOn,1); % output +ANN.a = 1; % amplitud for activation function + +% Back Propagation Algorithm +ANN.v = zeros(max([nHn nOn]),length(nHn)+1); % induced activation field +ANN.phi = zeros(max([nHn nOn]),length(nHn)); % activation function or output +ANN.lg = zeros(max([nHn nOn]),length(nHn)+1); % local gradient (lower delta) +ANN.dw = zeros(max([nHn nIn nOn]),max([nHn nIn nOn]),length(nHn)+1); % capital delta weight or change in weight +ANN.db = zeros(max([nHn nOn]),length(nHn)+1); % capital delta weight or change in weight of bias + +% Evolutionary Algorithm and PSO +ANN.aTr = 0; +ANN.apTr = 0; +ANN.fTr = inf; + +ANN.aV = 0; +ANN.apV = 0; +ANN.fV = inf; + +ANN.aT = 0; +ANN.apT = 0; +ANN.fT = inf; + + + diff --git a/PatRec/ANN/MLPTest.m b/PatRec/ANN/MLPTest.m index 73f86d6..c21b580 100644 --- a/PatRec/ANN/MLPTest.m +++ b/PatRec/ANN/MLPTest.m @@ -1,41 +1,41 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Test the ANN MLP. This function was split from OneShotPatRec. -% -% ------------------------- Updates & Contributors ------------------------ -% 2012-02-26 / Max Ortiz / Creation -% 2012-03-04 / Max Ortiz / Modify outMov to be round(outVector) - -function [outMov outVector] = MLPTest(patRecTrained, tSet) - - ANN = EvaluateANN(tSet,patRecTrained.ANN); - outVector = ANN.o; - - if isfield(patRecTrained,'thOut') - % Output according to a given threshold - outMov = find(outVector>patRecTrained.thOut'); - else - % The output movement is given by any prediction over 50% - outMov = find(round(outVector)); - - % The output movement is given by the higest prediction - %[maxV, outMov] = max(outVector); - end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Test the ANN MLP. This function was split from OneShotPatRec. +% +% ------------------------- Updates & Contributors ------------------------ +% 2012-02-26 / Max Ortiz / Creation +% 2012-03-04 / Max Ortiz / Modify outMov to be round(outVector) + +function [outMov outVector] = MLPTest(patRecTrained, tSet) + + ANN = EvaluateANN(tSet,patRecTrained.ANN); + outVector = ANN.o; + + if isfield(patRecTrained,'thOut') + % Output according to a given threshold + outMov = find(outVector>patRecTrained.thOut'); + else + % The output movement is given by any prediction over 50% + outMov = find(round(outVector)); + + % The output movement is given by the higest prediction + %[maxV, outMov] = max(outVector); + end + end \ No newline at end of file diff --git a/PatRec/ANN/PSO/InitPSO_MLP.m b/PatRec/ANN/PSO/InitPSO_MLP.m index 6d1eddf..90a4498 100644 --- a/PatRec/ANN/PSO/InitPSO_MLP.m +++ b/PatRec/ANN/PSO/InitPSO_MLP.m @@ -1,69 +1,69 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Use of the ANN MLP struct -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-07-27 / Max Ortiz / Creation -% 2012-06-01 / Max Ortiz / Adapted to new coding standard -% - -function PSO = InitPSO_MLP(ANN) - -PSO.sSize = 20; %Swarm size - -PSO.nVar = ANN.nIn * ANN.nHn(1); % Weight inputs and first layer -PSO.nVar = PSO.nVar + ANN.nHn(1); % Bias first layer -for i = 2 : length(ANN.nHn) - PSO.nVar = PSO.nVar + ANN.nHn(i-1)*ANN.nHn(i); %Weight hidden layer - PSO.nVar = PSO.nVar + ANN.nHn(i); % Bias Hiden Layers -end -PSO.nVar = PSO.nVar + ANN.nHn(end)*ANN.nOn; % Weight output -PSO.nVar = PSO.nVar + ANN.nOn; % Bias outputs - -PSO.xMin = -1; %minumum -PSO.xMax = 1; %maximum - -PSO.dt = 1; %time step lengh -PSO.alpha = 1; %constant -PSO.c1 = 2; %Constant C1 -PSO.c2 = 2; %Constant C2 - -PSO.beta = .6; %Beta for inertia weight -PSO.w = 1.4; %weigh for inertia weight -PSO.pcr = 0.1; %Craziness probability - -PSO.maxSimulations = 100; %max number of simulations - -% Initialization of Vmax -PSO.vMax = (PSO.xMax - PSO.xMin) / PSO.dt; - -PSO.opelite = 1; %input('Do you want to use elite particle? Yes (1) or No (2): '); -PSO.opcrazy = 1; %input('Do you want to use crazy operator? Yes (1) or No (2): '); - - -%Initializations -PSO.positions = PSO.xMin + rand(PSO.sSize,PSO.nVar).*(PSO.xMax - PSO.xMin); -PSO.velocities = InitVelocities(PSO.sSize,PSO.nVar,PSO.xMin,PSO.xMax,PSO.dt,PSO.alpha,PSO.vMax); - -% Initialization of best -PSO.fitness(1:PSO.sSize) = inf; -PSO.pBestFit(1:PSO.sSize) = inf; -PSO.sBestFit = inf; -PSO.pBestPos = zeros(PSO.sSize,PSO.nVar); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Use of the ANN MLP struct +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-07-27 / Max Ortiz / Creation +% 2012-06-01 / Max Ortiz / Adapted to new coding standard +% + +function PSO = InitPSO_MLP(ANN) + +PSO.sSize = 20; %Swarm size + +PSO.nVar = ANN.nIn * ANN.nHn(1); % Weight inputs and first layer +PSO.nVar = PSO.nVar + ANN.nHn(1); % Bias first layer +for i = 2 : length(ANN.nHn) + PSO.nVar = PSO.nVar + ANN.nHn(i-1)*ANN.nHn(i); %Weight hidden layer + PSO.nVar = PSO.nVar + ANN.nHn(i); % Bias Hiden Layers +end +PSO.nVar = PSO.nVar + ANN.nHn(end)*ANN.nOn; % Weight output +PSO.nVar = PSO.nVar + ANN.nOn; % Bias outputs + +PSO.xMin = -1; %minumum +PSO.xMax = 1; %maximum + +PSO.dt = 1; %time step lengh +PSO.alpha = 1; %constant +PSO.c1 = 2; %Constant C1 +PSO.c2 = 2; %Constant C2 + +PSO.beta = .6; %Beta for inertia weight +PSO.w = 1.4; %weigh for inertia weight +PSO.pcr = 0.1; %Craziness probability + +PSO.maxSimulations = 100; %max number of simulations + +% Initialization of Vmax +PSO.vMax = (PSO.xMax - PSO.xMin) / PSO.dt; + +PSO.opelite = 1; %input('Do you want to use elite particle? Yes (1) or No (2): '); +PSO.opcrazy = 1; %input('Do you want to use crazy operator? Yes (1) or No (2): '); + + +%Initializations +PSO.positions = PSO.xMin + rand(PSO.sSize,PSO.nVar).*(PSO.xMax - PSO.xMin); +PSO.velocities = InitVelocities(PSO.sSize,PSO.nVar,PSO.xMin,PSO.xMax,PSO.dt,PSO.alpha,PSO.vMax); + +% Initialization of best +PSO.fitness(1:PSO.sSize) = inf; +PSO.pBestFit(1:PSO.sSize) = inf; +PSO.sBestFit = inf; +PSO.pBestPos = zeros(PSO.sSize,PSO.nVar); diff --git a/PatRec/ANN/PSO/PSO_MLP.m b/PatRec/ANN/PSO/PSO_MLP.m index f875160..1ffd429 100644 --- a/PatRec/ANN/PSO/PSO_MLP.m +++ b/PatRec/ANN/PSO/PSO_MLP.m @@ -1,93 +1,93 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Particle Swarm Optimization to find ... -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-04-23 / Max Ortiz/ Creation -% 2012-06-02 / Max Ortiz/ Adapted for MLP - -function [PSO, ANN] = PSO_MLP(PSO, ANN, trSets, trOuts) - -if PSO.opcrazy ~= 1 - PSO.pcr = 0; -end - -% It's not need it to run over all simulations -%Run until the max number of generations -%for sim = 1:PSO.maxSimulations - - %Fitness evaluation - for i = 1:PSO.sSize - - ANN = Pos2W(ANN,PSO.positions(i,:)); %Rutine borrowed from the EA, in folder Eval - PSO.fitness(i) = FitnessANN(ANN,trSets,trOuts); - - %Particle Best - if (PSO.fitness(i) < PSO.pBestFit(i)) - PSO.pBestFit(i) = PSO.fitness(i); %particle best fitness - PSO.pBestPos(i,:) = PSO.positions(i,:); %particle best position - end - - end - - %Find the best in the swarm - for i=1:PSO.sSize - if PSO.pBestFit(i) < PSO.sBestFit - PSO.sBestFit = PSO.pBestFit(i); %Swarm best fitness - PSO.sBestPos = PSO.positions(i,:); %Swarm best position - end - end - - %Update velocity - for i = 1:PSO.sSize - cr=rand; - if cr > PSO.pcr - tempvel = UpdateVel(i, PSO.velocities, PSO.positions, PSO.pBestPos, PSO.sBestPos, PSO.c1, PSO.c2, PSO.nVar, PSO.dt, PSO.vMax, PSO.w); - else - tempvel = -PSO.vMax + 2 * rand(PSO.nVar,1) * PSO.vMax; - end - PSO.velocities(i,:) = tempvel; - end - - %Reduce inertia weight for velocity - if PSO.w > 0.3 - PSO.w = PSO.beta * PSO.w; - end - - %Update position - PSO.positions = PSO.positions + PSO.velocities * PSO.dt; -% % Limit negative values - negIdx = find(PSO.positions < PSO.xMin); - PSO.positions(negIdx) = -1; -% % Limit values over 1 - negIdx = find(PSO.positions > PSO.xMax); - PSO.positions(negIdx) = 1; - - - %Elite Partic%le - if PSO.opelite == 1 - PSO.positions(1,:) = PSO.sBestPos; %This instruction will always keep the best partical position - end - - %disp(PSO.sBestFit); - -%end -% Return the best position -ANN = Pos2W(ANN,PSO.sBestPos); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Particle Swarm Optimization to find ... +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-04-23 / Max Ortiz/ Creation +% 2012-06-02 / Max Ortiz/ Adapted for MLP + +function [PSO, ANN] = PSO_MLP(PSO, ANN, trSets, trOuts) + +if PSO.opcrazy ~= 1 + PSO.pcr = 0; +end + +% It's not need it to run over all simulations +%Run until the max number of generations +%for sim = 1:PSO.maxSimulations + + %Fitness evaluation + for i = 1:PSO.sSize + + ANN = Pos2W(ANN,PSO.positions(i,:)); %Rutine borrowed from the EA, in folder Eval + PSO.fitness(i) = FitnessANN(ANN,trSets,trOuts); + + %Particle Best + if (PSO.fitness(i) < PSO.pBestFit(i)) + PSO.pBestFit(i) = PSO.fitness(i); %particle best fitness + PSO.pBestPos(i,:) = PSO.positions(i,:); %particle best position + end + + end + + %Find the best in the swarm + for i=1:PSO.sSize + if PSO.pBestFit(i) < PSO.sBestFit + PSO.sBestFit = PSO.pBestFit(i); %Swarm best fitness + PSO.sBestPos = PSO.positions(i,:); %Swarm best position + end + end + + %Update velocity + for i = 1:PSO.sSize + cr=rand; + if cr > PSO.pcr + tempvel = UpdateVel(i, PSO.velocities, PSO.positions, PSO.pBestPos, PSO.sBestPos, PSO.c1, PSO.c2, PSO.nVar, PSO.dt, PSO.vMax, PSO.w); + else + tempvel = -PSO.vMax + 2 * rand(PSO.nVar,1) * PSO.vMax; + end + PSO.velocities(i,:) = tempvel; + end + + %Reduce inertia weight for velocity + if PSO.w > 0.3 + PSO.w = PSO.beta * PSO.w; + end + + %Update position + PSO.positions = PSO.positions + PSO.velocities * PSO.dt; +% % Limit negative values + negIdx = find(PSO.positions < PSO.xMin); + PSO.positions(negIdx) = -1; +% % Limit values over 1 + negIdx = find(PSO.positions > PSO.xMax); + PSO.positions(negIdx) = 1; + + + %Elite Partic%le + if PSO.opelite == 1 + PSO.positions(1,:) = PSO.sBestPos; %This instruction will always keep the best partical position + end + + %disp(PSO.sBestFit); + +%end +% Return the best position +ANN = Pos2W(ANN,PSO.sBestPos); diff --git a/PatRec/ANN/PSO/Pos2W.m b/PatRec/ANN/PSO/Pos2W.m index a7db5e6..b7c5b28 100644 --- a/PatRec/ANN/PSO/Pos2W.m +++ b/PatRec/ANN/PSO/Pos2W.m @@ -1,49 +1,49 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to decode the a vector of weight into the MLP -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-04-23 / Max Ortiz/ Creation - -function ANN = Pos2W(ANN,w) - - % First layer - ANN.w(1:ANN.nIn*ANN.nHn(1)) = w(1 : ANN.nIn*ANN.nHn(1)); - offset = ANN.nIn*ANN.nHn(1); - ANN.b(1:ANN.nHn(1)) = w(offset+1 : offset + ANN.nHn(1)); - offset = offset + ANN.nHn(1); - - % Hidden Layers - for i = 2 : length(ANN.nHn) - tmat = zeros(ANN.nHn(i-1), ANN.nHn(i)); - tmat(1:end) = w(offset + 1 : offset + ANN.nHn(i-1)*ANN.nHn(i)); - ANN.w(1:ANN.nHn(i-1),1:ANN.nHn(i),i) = tmat; - offset = offset + ANN.nHn(i-1)*ANN.nHn(i); - ANN.b(1:ANN.nHn(i), i) = w(offset + 1 : offset + ANN.nHn(i)); - offset = offset + ANN.nHn(i); - end - - % Last layer - tmat = zeros(ANN.nHn(end), ANN.nOn); - tmat(1:end) = w(offset + 1 : offset + ANN.nHn(end)*ANN.nOn); - ANN.w(1:ANN.nHn(end),1:ANN.nOn,end) = tmat; - offset = offset + ANN.nHn(end)*ANN.nOn; - ANN.b(1:ANN.nOn,end) = w(offset+1 : offset + ANN.nOn); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to decode the a vector of weight into the MLP +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-04-23 / Max Ortiz/ Creation + +function ANN = Pos2W(ANN,w) + + % First layer + ANN.w(1:ANN.nIn*ANN.nHn(1)) = w(1 : ANN.nIn*ANN.nHn(1)); + offset = ANN.nIn*ANN.nHn(1); + ANN.b(1:ANN.nHn(1)) = w(offset+1 : offset + ANN.nHn(1)); + offset = offset + ANN.nHn(1); + + % Hidden Layers + for i = 2 : length(ANN.nHn) + tmat = zeros(ANN.nHn(i-1), ANN.nHn(i)); + tmat(1:end) = w(offset + 1 : offset + ANN.nHn(i-1)*ANN.nHn(i)); + ANN.w(1:ANN.nHn(i-1),1:ANN.nHn(i),i) = tmat; + offset = offset + ANN.nHn(i-1)*ANN.nHn(i); + ANN.b(1:ANN.nHn(i), i) = w(offset + 1 : offset + ANN.nHn(i)); + offset = offset + ANN.nHn(i); + end + + % Last layer + tmat = zeros(ANN.nHn(end), ANN.nOn); + tmat(1:end) = w(offset + 1 : offset + ANN.nHn(end)*ANN.nOn); + ANN.w(1:ANN.nHn(end),1:ANN.nOn,end) = tmat; + offset = offset + ANN.nHn(end)*ANN.nOn; + ANN.b(1:ANN.nOn,end) = w(offset+1 : offset + ANN.nOn); + diff --git a/PatRec/Accuracy_patRec.m b/PatRec/Accuracy_patRec.m index 408306b..01549da 100644 --- a/PatRec/Accuracy_patRec.m +++ b/PatRec/Accuracy_patRec.m @@ -1,132 +1,203 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% -% Compute the overal all Accuracy of the patRec algorithm -% As it is, the number of set per class must be the same for each class -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-10-29 / Max Ortiz / Created -% 2012-03-04 / Max Ortiz / Cleaned from old commented code and include a -% validation for outMov == 0 -% 2012-04-01 / Max Ortiz / Cunfusion matrix added to the calculations -% 2012-04-01 / Max Ortiz / tTime added -% 20xx-xx-xx / Author / Comment on update - -function [acc confMat tTime] = Accuracy_patRec(patRec, tSet, tOut, confMatFlag) - -% Init variables -nM = size(patRec.mov,1); % Number of movements (total) -sM = size(tOut,1)/nM; % Sets per movement -good = zeros(size(tSet,1),1); % Keep track of the good prediction -nOut = size(tOut,2); % Number of outputs -confMat = zeros(nM,nOut+1); -tTime = zeros(1,size(tSet,1)); - -for i = 1 : size(tSet,1) - % Start the timer for testing/prediction time - tStart = tic; - %Normalize set - x = NormalizeSet(tSet(i,:), patRec); - - %% Classification - [outMov outVector] = OneShotPatRecClassifier(patRec, x); - - tTime(i) = toc(tStart); - - %% Dirty patch for simultaneous movements - % Dirty patch for classification of simultaneous movements when the - % algorithm has no way to know how many movements are mixed -% if strcmp(patRec.topology,'Single Classifier') -% if strcmp(patRec.patRecTrained(end).algorithm,'DA') ||... -% strcmp(patRec.patRecTrained(end).algorithm,'RFN') -% -% %Quick and dirty routine to evaluate 2 patterns -% % IF it's known that more than two patterns are presented -% nExpMov = length(find(tOut(i,:))); -% if nExpMov ~= 1 % More than one movement -% [Y, I] = sort(outVector,'descend'); -% outMov = I(1:nExpMov)'; -% end -% end -% end - -% % Stop when more than 1 mov is tested -% if length(find(tOut(i,:))) ~= 1 -% 0; -% end - - %% Count the number of correct predictions - if ~isempty(outMov) - if outMov ~= 0 - mask = zeros(1,nOut); - mask(outMov) = 1; - % Are these the right movements? - if tOut(i,:) == mask - good(i) = 1; - else - %stop for debuggin purposes - end - -% %Evaluate only a single movement -% if tOut(i,outMov) == 1 -% good(i) = 1; -% end - - else - %If outMov = 0, then count it for the confusion matrix as no - %prediction in an additional output - outMov = nOut+1; - end - else - %If outMov = empty, then count it for the confusion matrix as no - %prediction in an additional output - outMov = nOut+1; - end - - %Confusion Matrix - if confMatFlag - % This will only work if there is an equal number of sets per class - % in the testing sets - expectedOutIdx = fix((i-1)/sM)+1; - confMat(expectedOutIdx,outMov) = confMat(expectedOutIdx,outMov) + 1; - end -end - -tTime = mean(tTime); - -% Compute the accuracy per movement -% This will only work if there are the same number of movements -acc = zeros(nM+1,1); -for i = 1 : nM - s = 1+((i-1)*sM); - e = sM*i; - acc(i) = sum(good(s:e))/sM; -end -acc(i+1) = sum(good) / size(tSet,1); - -% Print confusion matrix -if confMatFlag - confMat = confMat ./ sM; % This will only work if there is an equal number of sets per class - figure; - imagesc(confMat); - title('Confusion Matrix') - xlabel('Movements'); - ylabel('Movements'); -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% Compute the overal all Accuracy of the patRec algorithm +% As it is, the number of set per class must be the same for each class +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-10-29 / Max Ortiz / Created +% 2012-03-04 / Max Ortiz / Cleaned from old commented code and include a +% validation for outMov == 0 +% 2012-04-01 / Max Ortiz / Cunfusion matrix added to the calculations +% 2012-04-01 / Max Ortiz / tTime added +% 2013-10-22 / Faezeh Rouhani / Additional indicators +% (precision,recall,f1,specificity,npv) +% 2012-10-30 / Max Ortiz / Clean and comment code, rename accnew to accTrue + +function [performance confMat tTime] = Accuracy_patRec(patRec, tSet, tOut, confMatFlag) + +% Init variables +nM = size(patRec.mov,1); % Number of movements (total) +sM = size(tOut,1)/nM; % Sets per movement +good = zeros(size(tSet,1),1); % Keep track of the good prediction +nOut = size(tOut,2); % Number of outputs +confMat = zeros(nM,nOut+1); +tTime = zeros(1,size(tSet,1)); +% prediction metrics +maskMat = zeros(size(tSet,1),nOut); +FN = zeros(size(tSet,1),nOut); +TN = zeros(size(tSet,1),nOut); +TP = zeros(size(tSet,1),nOut); +FP = zeros(size(tSet,1),nOut); + +for i = 1 : size(tSet,1) + % Start the timer for testing/prediction time + tStart = tic; + %Normalize set + x = NormalizeSet(tSet(i,:), patRec); + x = ApplyFeatureReduction(x, patRec); + %% Classification + [outMov outVector] = OneShotPatRecClassifier(patRec, x); + + tTime(i) = toc(tStart); + + %% Count the number of correct predictions + if ~isempty(outMov) + if outMov ~= 0 + % Create a mask to match the correct output + mask = zeros(1,nOut); + mask(outMov) = 1; + % Save the mask for future computation of prediction metrics + maskMat(i,:)=mask; + % Are these the right movements? + if tOut(i,:) == mask + good(i) = 1; + else + %stop for debuggin purposes + end + +% %Evaluate a single movement only / not suitable for simult. +% if tOut(i,outMov) == 1 +% good(i) = 1; +% end + + else + %If outMov = 0, then count it for the confusion matrix as no + %prediction in an additional output + outMov = nOut+1; + end + else + %If outMov = empty, then count it for the confusion matrix as no + %prediction in an additional output + outMov = nOut+1; + end + + %Confusion Matrix + if confMatFlag + expectedOutIdx = fix((i-1)/sM)+1; % This will only work if there is an equal number of sets per class + confMat(expectedOutIdx,outMov) = confMat(expectedOutIdx,outMov) + 1; + end +end +tTime = mean(tTime); + +% Verify that dimension of maskMat and tOut match +if size(tSet,1) ~= size(maskMat,1) + disp('error in maskMat'); +end +if size(tSet,1) ~= size(tOut,1) + disp('error in tOut'); +end +% Compute the FP, FN, TP, TN using the saved maskMat +for m=1:size(tSet,1) + for n=1:nOut + if tOut(m,n) == maskMat(m,n) + if tOut(m,n) == 1 + TP(m,n) = 1; + else + TN(m,n) = 1; + end + else + if tOut(m,n) == 1 + FN(m,n) = 1; + else + FP(m,n) = 1; + end + end + end +end + +% get total +tPs=sum(sum(TP)); +fPs=sum(sum(FP)); +tNs=sum(sum(TN)); +fNs=sum(sum(FN)); + +% Compute metrics per movement/class +% This will only work if there are the same number of movements +acc = zeros(nM+1,1); +tPvec = zeros(nOut,nM); +tNvec = zeros(nOut,nM); +fPvec = zeros(nOut,nM); +fNvec = zeros(nOut,nM); + +for i = 1 : nM + s = 1+((i-1)*sM); + e = sM*i; + acc(i) = sum(good(s:e))/sM; + tPvec(:,i)=sum(TP(s:e,:)); + tNvec(:,i)=sum(TN(s:e,:)); + fPvec(:,i)=sum(FP(s:e,:)); + fNvec(:,i)=sum(FN(s:e,:)); + + if tPvec(:,i) > sM + disp('Error on Ture Possitives'); + end + if tNvec(:,i) > size(tSet,1)-sM + disp('Error on Ture Negatives'); + end + +end +acc(i+1) = sum(good) / size(tSet,1); +tPvec = sum(tPvec)'; +tNvec = sum(tNvec)'; +fPvec = sum(fPvec)'; +fNvec = sum(fNvec)'; + +%Compute the precision per movement +precision = tPvec ./(tPvec+fPvec); +precision(end+1) = tPs/(tPs+fPs); + +%Compute the recall per movement +recall = tPvec ./(tPvec+fNvec); +recall(end+1) = tPs/(tPs+fNs); + +%Compute the specificity per movement +specificity=tNvec ./(tNvec+fPvec); +specificity(end+1)=tNs/(tNs+fPs); + +%Compute the npv per movement +npv=tNvec ./(tNvec+fNvec); +npv(end+1)=tNs/(tNs+fNs); + +%Compute the f1 per movement +f1=(2.*precision.*recall)./(precision+recall); + +% True accuracy / global accuracy +accTrue=(tPvec+tNvec)./(tPvec+fPvec+fNvec+tNvec); +accTrue(end+1)=(tPs+tNs)/(tNs+tPs+fPs+fNs); + +% Save performance metrics +performance.acc = acc*100; +performance.accTrue = accTrue*100; +performance.precision = precision*100; +performance.recall = recall*100; % Sensitivity +performance.f1 = f1; +performance.specificity= specificity*100; +performance.npv = npv*100; + +% Print confusion matrix +if confMatFlag + confMat = confMat ./ sM; % This will only work if there is an equal number of sets per class + figure; + imagesc(confMat); + title('Confusion Matrix') + xlabel('Movements'); + ylabel('Movements'); +end diff --git a/PatRec/ContainsMovementNames.m b/PatRec/ContainsMovementNames.m index 67ea617..721b5a1 100644 --- a/PatRec/ContainsMovementNames.m +++ b/PatRec/ContainsMovementNames.m @@ -1,8 +1,8 @@ -function contains = ContainsMovementNames(movements,movementNames) - contains = 0; - for i = 1:length(movements) - if strfind(movementNames{1},movements(i).name{1}) - contains = 1; - end - end +function contains = ContainsMovementNames(movements,movementNames) + contains = 0; + for i = 1:length(movements) + if strfind(movementNames{1},movements(i).name{1}) + contains = 1; + end + end end \ No newline at end of file diff --git a/PatRec/GUI_PatRec.fig b/PatRec/GUI_PatRec.fig index c8478190148fda825a40ef055b29f4f6542258f1..41cee37ae420d74b1655855e32be35199648aaee 100644 GIT binary patch literal 21703 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

^M(iHqGYjJ|pr!6w(hM&p;9@{Qf%TocICWz1T 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 literal 16064 zcma)jRa6`>*DdZ=q(E^f?(XhTpg@beySo;5hoZ$PR@@zmySoew?lQmt1I+FBfB(8q z_u=LxIcp{RAz9fuXXhLh$!}jIr6_sXI4D&lzp+}`I#{w$eswVQuyS^D6r%hpt*xla z$HPJ??Ph7}VQE3>>?A~~{jZmFaiiqqqvRA2;^7nG;H2c@;N+qF|7?;XF#m0^iYnOu zMjgU1Fg|$~y3_Wg{P>xxB*6!RJdPS)ur0AeN^wFRF%%7BM`P0busyeH&GmF#nwvHZ zO)qWcYz*qu#9HU1$0{>_zQaf4OC<^Zv#>;;=A;>E@!PWIG>bA7Xl%W#x*xCNS7R`P zDEoY!?{|8^X*_?uiT!o;pN~XAe^N+%vC>Dj2#p^5rlGE9I z;8U+)5F%avppO#5B6JQnf;d*i=czAsOXc>u$pPfEqF{KnLTs>{diYw=|Fa@W@V9CO z!`}29+gFx0WLz14%1@5Tg8m%dVu6PlNr3HvCHzJ=9Zhd*!Ha1}|XXrZa`8k{W#H+*l z>K9|mDXa*Bi=WkU#Ft;Sr`vd;Dj)zaR2n4C8~pp(IOp2tJ?W?`M_k-0fd9j>Ds_@| zjEKrkRGH@MzH8YqVAsTjQ-F92BN0hEtZCq_-XpdTSceAHmUja@eIo5H_%0D3o_6;u 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⋘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 CSk0^e diff --git a/PatRec/GUI_PatRec.m b/PatRec/GUI_PatRec.m index 6cd7683..f781ab0 100644 --- a/PatRec/GUI_PatRec.m +++ b/PatRec/GUI_PatRec.m @@ -19,7 +19,10 @@ % Compute the overal all Accuracy of the patRec algorithm % % ------------------------- Updates & Contributors ------------------------ -% 2011-11-25 / Max Ortiz / Created new version from EMG_AQ +% 2011-11-25 / Max Ortiz / Created new version from EMG_AQ +% 2014-11-07 / Diep Khong / Added SVM +% 2014-12-01 / Enzo Mastinu / Added the handling part for the COM port number + % information into the parameters % 20xx-xx-xx / Author / Comment on update function varargout = GUI_PatRec(varargin) @@ -46,7 +49,7 @@ % Edit the above text to modify the response to help GUI_PatRec -% Last Modified by GUIDE v2.5 06-Nov-2012 11:48:34 +% Last Modified by GUIDE v2.5 30-Oct-2013 10:35:30 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; @@ -120,84 +123,146 @@ function pb_GetFeatures_Callback(hObject, eventdata, handles) ss = []; % Dialog box to open a file - [file, path] = uigetfile('*.mat'); + [file, path] = uigetfile({'*.mat';'*.csv'}); % Check that the loaded file is a "ss" struct if ~isequal(file, 0) - load([path,file]); - if (exist('recSession','var')) % Send a recording session for data treatment + [pathstr,name,ext] = fileparts(file); + if(strcmp(ext,'.mat')) + load([path,file]); + if (exist('recSession','var')) % Send a recording session for data treatment - Load_recSession(recSession, handles); - %Enable algorithm selection - set(handles.pm_SelectAlgorithm,'Enable','on'); - set(handles.rb_all,'Enable','on'); - set(handles.rb_top2,'Enable','on'); - set(handles.rb_top3,'Enable','on'); - set(handles.rb_top4,'Enable','on'); - - elseif (exist('sigFeatures','var')) % Get sig_Features - Load_sigFeatures(sigFeatures, handles); - %Enable algorithm selection - set(handles.pm_SelectAlgorithm,'Enable','on'); - set(handles.rb_all,'Enable','on'); - set(handles.rb_top2,'Enable','on'); - set(handles.rb_top3,'Enable','on'); - set(handles.rb_top4,'Enable','on'); - set(handles.pb_RunOfflineTraining,'Enable','on'); - - elseif (exist('treated_data','var')) % Get sig_Features - Load_sigFeatures(treated_data, handles); - %Enable algorithm selection - set(handles.pm_SelectAlgorithm,'Enable','on'); - set(handles.rb_all,'Enable','on'); - set(handles.rb_top2,'Enable','on'); - set(handles.rb_top3,'Enable','on'); - set(handles.rb_top4,'Enable','on'); + Load_recSession(recSession, handles); + %Enable algorithm selection + set(handles.pm_SelectAlgorithm,'Enable','on'); + set(handles.rb_all,'Enable','on'); + set(handles.rb_top2,'Enable','on'); + set(handles.rb_top3,'Enable','on'); + set(handles.rb_top4,'Enable','on'); - elseif (exist('patRec','var')) % Get patRec - Load_patRec(patRec, 'GUI_PatRec',[]); - set(handles.pm_normSets,'Enable','off'); - set(handles.pm_SelectTopology,'Enable','off'); - set(handles.pm_movMix,'Enable','off'); - set(handles.pm_normSets,'Enable','off'); -% set(handles.pb_RealtimePatRec,'Enable','on'); -% set(handles.pb_motionTest,'Enable','on'); - set(handles.pb_RealtimePatRecGUI,'Enable','on'); - %Added to enable Mov2Mov-button after loading PatRec - set(handles.pb_RealtimePatRecMov2Mov,'Enable','on'); - %Load all values from the patRec into the GUI. - set(handles.et_accuracy,'String',num2str(patRec.acc(end))); - set(handles.lb_accuracy,'String',num2str(patRec.acc(1:end-1))); - set(handles.et_trTime,'String',num2str(patRec.trTime)); - set(handles.et_tTime,'String',num2str(patRec.tTime)); - - elseif (exist('ss','var')) % keep compatibility - if ~isempty(ss) - recSession.sF = ss.Fs; - recSession.sT = ss.Ts; - recSession.nM = ss.Ne; - recSession.nR = ss.Nr; - recSession.cT = ss.Tc; - recSession.rT = ss.Tr; - recSession.cTp = ss.Psr; - recSession.mov = ss.msg; - recSession.date = ss.date; - recSession.tdata = ss.tdata; - %recSession.trdata = ss.trdata; - - Load_recSession(recSession, handles); + elseif (exist('sigFeatures','var')) % Get sig_Features + Load_sigFeatures(sigFeatures, handles); + %Enable algorithm selection + set(handles.pm_SelectAlgorithm,'Enable','on'); + set(handles.rb_all,'Enable','on'); + set(handles.rb_top2,'Enable','on'); + set(handles.rb_top3,'Enable','on'); + set(handles.rb_top4,'Enable','on'); + set(handles.pb_RunOfflineTraining,'Enable','on'); + + elseif (exist('treated_data','var')) % Get sig_Features + Load_sigFeatures(treated_data, handles); %Enable algorithm selection set(handles.pm_SelectAlgorithm,'Enable','on'); set(handles.rb_all,'Enable','on'); set(handles.rb_top2,'Enable','on'); set(handles.rb_top3,'Enable','on'); set(handles.rb_top4,'Enable','on'); - else - disp('That was not a valid training matrix'); - errordlg('That was not a valid training matrix','Error'); - return; - end + + elseif (exist('patRec','var')) % Get patRec + Load_patRec(patRec, 'GUI_PatRec',[]); + set(handles.pm_normSets,'Enable','off'); + set(handles.pm_SelectTopology,'Enable','off'); + set(handles.pm_movMix,'Enable','off'); + set(handles.pm_normSets,'Enable','off'); + % set(handles.pb_RealtimePatRec,'Enable','on'); + % set(handles.pb_motionTest,'Enable','on'); + set(handles.pb_RealtimePatRecGUI,'Enable','on'); + %Added to enable Mov2Mov-button after loading PatRec + set(handles.pb_RealtimePatRecMov2Mov,'Enable','on'); + %Load all values from the patRec into the GUI. + set(handles.et_accuracy,'String',num2str(patRec.acc(end))); + set(handles.lb_accuracy,'String',num2str(patRec.acc(1:end-1))); + set(handles.et_trTime,'String',num2str(patRec.trTime)); + set(handles.et_tTime,'String',num2str(patRec.tTime)); + disp('%%%%%%%%%%% patRec loaded %%%%%%%%%%%%%'); + set(handles.t_msg,'String','patRec loaded'); + elseif (exist('ss','var')) % keep compatibility + if ~isempty(ss) + recSession.sF = ss.Fs; + recSession.sT = ss.Ts; + recSession.nM = ss.Ne; + recSession.nR = ss.Nr; + recSession.cT = ss.Tc; + recSession.rT = ss.Tr; + recSession.cTp = ss.Psr; + recSession.mov = ss.msg; + recSession.date = ss.date; + recSession.tdata = ss.tdata; + %recSession.trdata = ss.trdata; + + Load_recSession(recSession, handles); + %Enable algorithm selection + set(handles.pm_SelectAlgorithm,'Enable','on'); + set(handles.rb_all,'Enable','on'); + set(handles.rb_top2,'Enable','on'); + set(handles.rb_top3,'Enable','on'); + set(handles.rb_top4,'Enable','on'); + else + disp('That was not a valid training matrix'); + errordlg('That was not a valid training matrix','Error'); + return; + end + end + else + %CSV / MCARE + + fid = fopen(file); + fullDir = strcat(path,name,ext); % We get the path of the selected file + fileDir = dir(fullDir); % We use this to get the size, which is a field of dir + movText = fgetl(fid); % We read the first line + movText = textscan(movText, '%s', 'Delimiter', ',', 'BufSize', fileDir.bytes); %Scans for objects seperated with commas. We use the filesize as buffer + recSession.mov = movText{1}; %And load them into the recSession + fclose(fid); %We need to close the file, before we can textscan it with other parameters + fid = fopen(file); + C = textscan(fid, '%s', 'Delimiter', '\n', 'BufSize', fileDir.bytes); % Scans for objects seperated by line breaks + recSession.date = C{1}{2}; + recSession.comm = C{1}{3}; + if strcmp(recSession.comm, 'COM') + recSession.comn = C{1}{4}; + end + recSession.sF = csvread(file,3,0,[3, 0, 3, 0]); + recSession.nM = csvread(file,3,1,[3, 1, 3, 1]); + recSession.sT = csvread(file, 3,2,[3,2,3,2]); + recSession.cT = csvread(file, 3,3,[3,3,3,3]); + recSession.rT = csvread(file, 3,4,[3,4,3,4]); + recSession.nR = csvread(file, 3,5,[3,5,3,5]); + recSession.nCh = csvread(file, 3,6,[3,6,3,6]); + %Loading raw data + rawData = csvread(file,4,0)'; + %Preallocate memory + recSession.tdata = zeros(recSession.sF*recSession.cT*recSession.nR*2,recSession.nCh,recSession.nM); + %Sorts the data + for movements = 1 : recSession.nM + for channels = 1 : recSession.nCh + % We iterate over the repititions, as data from MCARE is exported channel-wise (as a subset of each repitition) + % The system is like this (for 3 repititions and 4 + % channels) + %iterator = + % 1 5 9 -> First channel + % 2 6 10 -> Second Channel + % 3 7 11 -> Third Channel + % 4 8 12 -> Fourth Channel + % ^ "lines of data" E.g. "1" = first channel from first + % repition. "5" = first channel from second repitition. + % "2" = second channel from first repitition. + iterator = channels+((movements-1)*(recSession.nCh*recSession.nR)); + for repititions = 1 : recSession.nR + recSession.tdata(1+((repititions-1)*(recSession.sF*(recSession.cT+recSession.rT))):repititions*(recSession.sF*(recSession.cT+recSession.rT)),channels,movements) = rawData(:,iterator); + iterator = iterator + recSession.nCh; + end + end + end + + Load_recSession(recSession, handles); + %Enable algorithm selection + set(handles.pm_SelectAlgorithm,'Enable','on'); + set(handles.rb_all,'Enable','on'); + set(handles.rb_top2,'Enable','on'); + set(handles.rb_top3,'Enable','on'); + set(handles.rb_top4,'Enable','on'); end end + @@ -208,7 +273,7 @@ function pb_RunOfflineTraining_Callback(hObject, eventdata, handles) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % Close real-time testing -% close(GUI_TestPatRec_Mov2Mov); + % close(GUI_TestPatRec_Mov2Mov); set(handles.t_msg,'String','Offline PatRec Started...'); % set(handles.pb_RunOfflineTraining,'Enable','Off'); @@ -225,7 +290,7 @@ function pb_RunOfflineTraining_Callback(hObject, eventdata, handles) set(handles.t_msg,'String','Please select the mix of movements to be used'); return; end - + % Collet sigFeatures sigFeatures = get(handles.t_sigFeatures,'UserData'); if isempty(sigFeatures) @@ -241,6 +306,10 @@ function pb_RunOfflineTraining_Callback(hObject, eventdata, handles) allNormSets = get(handles.pm_normSets,'String'); normSets = char(allNormSets(get(handles.pm_normSets,'Value'))); + % Select feature reduction algorithm + allFeatReducAlg=get(handles.pm_FeatureReduction,'String'); + featReducAlg=char(allFeatReducAlg(get(handles.pm_FeatureReduction,'Value'))); + % Movements mix or individual movements? allMovMixes = get(handles.pm_movMix,'String'); movMix = char(allMovMixes(get(handles.pm_movMix,'Value'))); @@ -272,7 +341,7 @@ function pb_RunOfflineTraining_Callback(hObject, eventdata, handles) topology = char(allTopologies(get(handles.pm_SelectTopology,'Value'))); % Call rutine for offline pat rec - patRec = OfflinePatRec(sigFeatures, selFeatures, randFeatures, normSets, alg, tType, algConf, movMix, topology, confMatFlag); + patRec = OfflinePatRec(sigFeatures, selFeatures, randFeatures, normSets, alg, tType, algConf, movMix, topology, confMatFlag, featReducAlg); % Save and show results handles.patRec = patRec; @@ -280,12 +349,28 @@ function pb_RunOfflineTraining_Callback(hObject, eventdata, handles) % Save in GUI components to be used by statistics rutines % This has to be change - set(handles.lb_accuracy,'UserData',patRec.acc); + set(handles.lb_accuracy,'UserData',patRec.performance.acc); +% set(handles.lb_precision,'UserData',patRec.performance.precision); +% set(handles.lb_recall,'UserData',patRec.performance.recall); +% set(handles.lb_f1,'UserData',patRec.performance.f1); set(handles.et_accuracy,'UserData',patRec); % Update GUI - set(handles.et_accuracy,'String',num2str(patRec.acc(end))); - set(handles.lb_accuracy,'String',num2str(patRec.acc(1:end-1))); + set(handles.et_accuracy,'String',num2str(patRec.performance.acc(end),'%.2f')); + set(handles.lb_accuracy,'String',num2str(patRec.performance.acc(1:end-1),'%.2f')); + set(handles.et_accTrue,'String',num2str(patRec.performance.accTrue(end),'%.2f')); + set(handles.lb_accTrue,'String',num2str(patRec.performance.accTrue(1:end-1),'%.2f')); + set(handles.et_precision,'String',num2str(patRec.performance.precision(end),'%.2f')); + set(handles.lb_precision,'String',num2str(patRec.performance.precision(1:end-1),'%.2f')); + set(handles.et_recall,'String',num2str(patRec.performance.recall(end),'%.2f')); + set(handles.lb_recall,'String',num2str(patRec.performance.recall(1:end-1),'%.2f')); + set(handles.et_f1,'String',num2str(patRec.performance.f1(end),'%.2f')); + set(handles.lb_f1,'String',num2str(patRec.performance.f1(1:end-1),'%.2f')); + set(handles.et_specificity,'String',num2str(patRec.performance.specificity(end),'%.2f')); + set(handles.lb_specificity,'String',num2str(patRec.performance.specificity(1:end-1),'%.2f')); + set(handles.et_npv,'String',num2str(patRec.performance.npv(end),'%.2f')); + set(handles.lb_npv,'String',num2str(patRec.performance.npv(1:end-1),'%.2f')); + set(handles.et_trTime,'String',num2str(patRec.trTime)); set(handles.et_tTime,'String',num2str(patRec.tTime)); set(handles.pb_RealtimePatRecGUI,'Enable','on'); @@ -319,7 +404,7 @@ function pm_SelectAlgorithm_Callback(hObject, eventdata, handles) strcmp(alg,'MLP thOut') set(handles.pm_SelectTraining,'Enable','on'); - tA = {'Select Training A.','Backpropagation', 'PSO'}; + tA = {'Select Training A.','Backpropagation', 'EA', 'PSO'}; set(handles.pm_SelectTraining,'String',tA); set(handles.pb_RunOfflineTraining,'Enable','off'); set(handles.pm_normSets,'Value',4); @@ -331,7 +416,7 @@ function pm_SelectAlgorithm_Callback(hObject, eventdata, handles) set(handles.pb_RunOfflineTraining,'Enable','off'); set(handles.pm_normSets,'Value',1); - elseif strcmp(alg,'RFN') + elseif strcmp(alg,'RFN') % for LDA set(handles.pm_SelectTraining,'Enable','on'); tA = {'Select Training A.','Mean','Mean + PSO','Exclusive Mean'}; set(handles.pm_SelectTraining,'String',tA); @@ -354,8 +439,15 @@ function pm_SelectAlgorithm_Callback(hObject, eventdata, handles) set(handles.pm_SelectTraining,'String',tA); set(handles.pm_SelectTraining,'Value',1); set(handles.pb_RunOfflineTraining,'Enable','off'); - set(handles.pm_normSets,'Value',5); + set(handles.pm_normSets,'Value',5); + elseif strcmp(alg,'SVM'); % for SVM + set(handles.pm_SelectTraining,'Enable','on'); + tA = {'Select Training A.','linear', 'quadratic', 'polynomial', 'rbf', 'mlp'}; + set(handles.pm_SelectTraining,'String',tA); + set(handles.pm_SelectTraining,'Value',1); + set(handles.pb_RunOfflineTraining,'Enable','on'); + set(handles.pm_normSets,'Value',4); end @@ -712,7 +804,7 @@ function m_Stats_CurrentD_Callback(hObject, eventdata, handles) nRep = str2double(inputdlg('Write the number of repetitions','Number of Runs')); nM = size(get(handles.lb_movements,'String'),1); - confMatFlag = get(handles.cb_confMat,'Value') + confMatFlag = get(handles.cb_confMat,'Value'); % safety check if isempty(nRep) @@ -722,41 +814,49 @@ function m_Stats_CurrentD_Callback(hObject, eventdata, handles) errordlg('Invalid number','Error') return; end + + % Init variables + accCS = zeros(nRep,nM+1); + accTrue = zeros(nRep,nM+1); + precision = zeros(nRep,nM+1); + recall = zeros(nRep,nM+1); + f1 = zeros(nRep,nM+1); + specificity = zeros(nRep,nM+1); + npv = zeros(nRep,nM+1); - % run the training the given number of times - acc = zeros(nRep,nM+1); trTime = zeros(1,nRep); - tTime = zeros(1,nRep); - + tTime = zeros(1,nRep); tStd = inf; tAcc = 0; + % run the training the given number of times for i = 1 : nRep pb_RunOfflineTraining_Callback(hObject, eventdata, handles); patRec = get(handles.et_accuracy,'UserData'); - tempAcc = patRec.acc; - %tempAcc = get(handles.lb_accuracy,'UserData'); - %tempAcc = handles.patRec.acc; % Handles cannot be used because - %once the training is done, the new "patRec" is not updated into - %handles since the handles variable was recieved before calling - %the training. + tempPerformance = patRec.performance; if confMatFlag tempConfMat(:,:,i) = patRec.confMat; end - acc(i,:) = tempAcc; - trTime(i) = patRec.trTime; - tTime(i) = patRec.tTime; + accCS(i,:) = tempPerformance.acc; + accTrue(i,:) = tempPerformance.accTrue; + precision(i,:) = tempPerformance.precision; + recall(i,:) = tempPerformance.recall; + f1(i,:) = tempPerformance.f1; + specificity(i,:)= tempPerformance.specificity; + npv(i,:) = tempPerformance.npv; + trTime(i) = patRec.trTime; + tTime(i) = patRec.tTime; set(handles.t_msg,'String',['Runing ' num2str(i)]); drawnow; % Save the best patRec % if std(tempAcc(1:end-1)) <= tStd && tempAcc(end) >= tAcc - if tempAcc(end) >= tAcc + if tempPerformance.acc(end) >= tAcc % tStd = std(tempAcc(1:end-1)); - tAcc = tempAcc(end); + tAcc = tempPerformance.acc(end); tempPatRec = patRec; end @@ -769,22 +869,80 @@ function m_Stats_CurrentD_Callback(hObject, eventdata, handles) %Old way to transfer data between GUIs %set(handles.et_accuracy,'UserData',tempPatRec); - % show results - tAcc = mean(acc); - set(handles.et_accuracy,'String',num2str(tAcc(end))); - set(handles.lb_accuracy,'String',num2str(tAcc(1:end-1)')); + % Display results + tAcc = mean(accCS); + set(handles.et_accuracy,'String',num2str(tAcc(end),'%.2f')); + set(handles.lb_accuracy,'String',num2str(tAcc(1:end-1)','%.2f')); set(handles.et_trTime,'String',num2str(mean(trTime))); set(handles.et_tTime,'String',num2str(mean(tTime))); + + set(handles.et_accTrue,'String',num2str(mean(accTrue(:,end)),'%.2f')); + set(handles.lb_accTrue,'String',num2str(mean(accTrue(:,1:end-1))','%.2f')); + set(handles.et_precision,'String',num2str(mean(precision(:,end)),'%.2f')); + set(handles.lb_precision,'String',num2str(mean(precision(:,1:end-1))','%.2f')); + set(handles.et_recall,'String',num2str(mean(recall(:,end)),'%.2f')); + set(handles.lb_recall,'String',num2str(mean(recall(:,1:end-1))','%.2f')); + set(handles.et_f1,'String',num2str(mean(f1(:,end)),'%.2f')); + set(handles.lb_f1,'String',num2str(mean(f1(:,1:end-1))','%.2f')); + set(handles.et_specificity,'String',num2str(mean(specificity(:,end)),'%.2f')); + set(handles.lb_specificity,'String',num2str(mean(specificity(:,1:end-1))','%.2f')); + set(handles.et_npv,'String',num2str(mean(npv(:,end)),'%.2f')); + set(handles.lb_npv,'String',num2str(mean(npv(:,1:end-1))','%.2f')); + % plot results figure(); - boxplot(acc,'plotstyle','compact') + boxplot(accCS,'plotstyle','compact') + hold on; + plot(mean(accCS),'r*'); + xlabel('Movements'); + ylabel('Accuracy (class-specific)'); + + figure(); + boxplot(accTrue,'plotstyle','compact') + hold on; + plot(mean(accTrue),'r*'); + xlabel('Movements'); + ylabel('Accuracy (Global)'); + + figure(); + boxplot(recall,'plotstyle','compact') + hold on; + plot(mean(recall),'r*'); + xlabel('Movements'); + ylabel('Recall'); + + figure(); + boxplot(precision,'plotstyle','compact') + hold on; + plot(mean(precision),'r*'); + xlabel('Movements'); + ylabel('Precision (PPV)'); + + figure(); + boxplot(f1,'plotstyle','compact') + hold on; + plot(mean(f1),'r*'); + xlabel('Movements'); + ylabel('F1'); + + figure(); + boxplot(specificity,'plotstyle','compact') + hold on; + plot(mean(specificity),'r*'); + xlabel('Movements'); + ylabel('Specificity'); + + figure(); + boxplot(npv,'plotstyle','compact') hold on; - plot(mean(acc),'r*'); - title('Pattern recognition accuracy') + plot(mean(npv),'r*'); xlabel('Movements'); - ylabel('Accuracy'); + ylabel('Negative Predicted Value (NPV)'); + + + % Confution matrix if confMatFlag figure(); confMat = mean(tempConfMat,3); @@ -809,18 +967,42 @@ function m_Stats_CurrentD_Callback(hObject, eventdata, handles) stats.topology = patRec.topology; stats.patRecTrained = patRec.patRecTrained; stats.norm = patRec.normSets; - stats.mean = tAcc; - stats.std = std(acc); - stats.min = min(acc); - stats.max = max(acc); - stats.acc = acc; + + stats.accCS = accCS; + stats.accCSmn = mean(accCS)'; + stats.accCSstd = std(accCS)'; + + stats.accTrue = accTrue; + stats.accTruemn = mean(accTrue)'; + stats.accTruestd = std(accTrue)'; + + stats.recall = recall; + stats.recallmn = mean(recall)'; + stats.recallstd = std(recall)'; + + stats.precision = precision; + stats.precisionmn = mean(precision)'; + stats.precisionstd = std(precision)'; + + stats.f1 = f1; + stats.f1mn = mean(f1)'; + stats.f1std = std(f1)'; + + stats.specificity = specificity; + stats.specificitymn = mean(specificity)'; + stats.specificitystd= std(specificity)'; + + stats.npv = npv; + stats.npvmn = mean(npv)'; + stats.npvstd = std(npv)'; + if confMatFlag - stats.confMat = confMat; + stats.confMat = confMat; end - stats.trTime = trTime; - stats.tTime = tTime; - stats.trTimeMean = mean(trTime); - stats.tTimeMean = mean(tTime); + stats.trTime = trTime; + stats.tTime = tTime; + stats.trTimeMean= mean(trTime); + stats.tTimeMean = mean(tTime); disp(stats); save('stats.mat','stats'); @@ -874,10 +1056,24 @@ function m_Stats_Group_Callback(hObject, eventdata, handles) end nM = size(get(handles.lb_movements,'String'),1); - acc = zeros(nRep,nM+1,nS); + accCS = zeros(nRep,nM+1,nS); + accTrue = zeros(nRep,nM+1,nS); + precision = zeros(nRep,nM+1,nS); + recall = zeros(nRep,nM+1,nS); + f1 = zeros(nRep,nM+1,nS); + specificity = zeros(nRep,nM+1,nS); + npv = zeros(nRep,nM+1,nS); trTime = zeros(nRep,nS); tTime = zeros(nRep,nS); - tAcc = zeros(nS,nM+1); + + % temporal variable with subjects average + tAccCS = zeros(nS,nM+1); + tAccTrue = zeros(nS,nM+1); + tRecall = zeros(nS,nM+1); + tPrecision = zeros(nS,nM+1); + tF1 = zeros(nS,nM+1); + tSpecificity= zeros(nS,nM+1); + tNPV = zeros(nS,nM+1); for i = 1 : nS % load data @@ -897,50 +1093,61 @@ function m_Stats_Group_Callback(hObject, eventdata, handles) % run the training the given number of times for j = 1 : nRep set(handles.t_msg2,'String',['Statistics Rep: ' num2str(j) ' from subject: ' num2str(i)]); - - pb_RunOfflineTraining_Callback(hObject, eventdata, handles) + pb_RunOfflineTraining_Callback(hObject, eventdata, handles); patRec = get(handles.et_accuracy,'UserData'); - acc(j,:,i) = get(handles.lb_accuracy,'UserData'); + tempPerformance = patRec.performance; + + accCS(j,:,i) = tempPerformance.acc; + accTrue(j,:,i) = tempPerformance.accTrue; + precision(j,:,i) = tempPerformance.precision; + recall(j,:,i) = tempPerformance.recall; + f1(j,:,i) = tempPerformance.f1; + specificity(j,:,i)= tempPerformance.specificity; + npv(j,:,i) = tempPerformance.npv; + trTime(j,i) = patRec.trTime; - tTime(j,i) = patRec.tTime; + tTime(j,i) = patRec.tTime; drawnow; end - % show results - tAcc(i,:) = mean(acc(:,:,i)); - set(handles.et_accuracy,'String',num2str(tAcc(i,end))); - set(handles.lb_accuracy,'String',num2str(tAcc(i,1:end-1)')); + % save results + tAccCS(i,:) = mean(accCS(:,:,i)); + tAccTrue(i,:) = mean(accTrue(:,:,i)); + tRecall(i,:) = mean(recall(:,:,i)); + tPrecision(i,:) = mean(precision(:,:,i)); + tF1(i,:) = mean(f1(:,:,i)); + tSpecificity(i,:) = mean(specificity(:,:,i)); + tNPV(i,:) = mean(npv(:,:,i)); % plot figure(); - boxplot(acc(:,:,i),'plotstyle','compact') + boxplot(accCS(:,:,i),'plotstyle','compact') hold on; - plot(mean(acc(:,:,i)),'r*'); - title(['Pattern recognition accuracy: #' num2str(i)]); + plot(mean(accCS(:,:,i)),'r*'); xlabel('Movements'); - ylabel('Accuracy'); + ylabel('Accuracy (class-specific)'); end set(handles.t_msg2,'String',''); % plot per movement figure(); - boxplot(tAcc,'plotstyle','compact') + boxplot(tAccCS,'plotstyle','compact') hold on; - plot(mean(tAcc),'r*'); + plot(mean(tAccCS),'r*'); title('Pattern recognition accuracy, All subjects') xlabel('Movements'); - ylabel('Accuracy'); + ylabel('Accuracy (class-specific)'); % plots per subject figure(); - boxplot(tAcc(:,1:end-1)','plotstyle','compact') + boxplot(tAccCS(:,1:end-1)','plotstyle','compact') hold on; - plot(mean(tAcc(:,1:end-1),2),'r*'); + plot(mean(tAccCS(:,1:end-1),2),'r*'); title('Pattern recognition accuracy, All subjects') xlabel('Subjects'); - ylabel('Accuracy'); + ylabel('Accuracy (class-specific)'); % Signal features fIdx = get(handles.lb_features,'Value'); @@ -948,47 +1155,95 @@ function m_Stats_Group_Callback(hObject, eventdata, handles) features = features(fIdx); % Save stats - patRec = get(handles.et_accuracy,'UserData'); + patRec = get(handles.et_accuracy,'UserData'); stats.nSubjects = nS; stats.nRep = nRep; stats.nM = nM; - stats.features = features; - stats.algorithm = patRec.patRecTrained(end).algorithm; - stats.training = patRec.patRecTrained(end).training; - stats.topology = patRec.topology; - stats.norm = patRec.normSets.type; - stats.meanSub = mean(tAcc(:,end)); - stats.stdSub = std(tAcc(:,end)); - stats.minSub = min(tAcc(:,end)); - stats.maxSub = max(tAcc(:,end)); - stats.meanMov = mean(mean(tAcc(:,1:end-1))); - stats.stdMov = std(std(tAcc(:,1:end-1))); - stats.minMov = min(min(tAcc(:,1:end-1))); - stats.maxMov = max(max(tAcc(:,1:end-1))); - - stats.meanXSub = tAcc(:,end); - stats.stdXSub = std(tAcc(:,1:end-1)')'; - stats.minXSub = min(tAcc(:,1:end-1)')'; - stats.maxXSub = max(tAcc(:,1:end-1)')'; - - stats.meanXMov = mean(tAcc(:,1:end-1)); - stats.stdXMov = std(tAcc(:,1:end-1)); - stats.minXMov = min(tAcc(:,1:end-1)); - stats.maxXMov = max(tAcc(:,1:end-1)); - - stats.accXSub = tAcc(:,1:end-1)'; - stats.accXMov = tAcc; - stats.accAll = acc; + + stats.features.features = features; + stats.features.trSets = str2double(get(handles.et_trSets,'String')); + stats.features.vSets = str2double(get(handles.et_vSets,'String')); + stats.features.tSets = str2double(get(handles.et_tSets,'String')); + + stats.algorithm = patRec.patRecTrained(end).algorithm; + stats.training = patRec.patRecTrained(end).training; + stats.topology = patRec.topology; + stats.norm = patRec.normSets.type; + stats.lastPatRec = patRec; + +% stats.meanSub = mean(tAccCS(:,end)); +% stats.stdSub = std(tAccCS(:,end)); +% stats.minSub = min(tAccCS(:,end)); +% stats.maxSub = max(tAccCS(:,end)); +% stats.meanMov = mean(mean(tAccCS(:,1:end-1))); +% stats.stdMov = std(std(tAccCS(:,1:end-1))); +% stats.minMov = min(min(tAccCS(:,1:end-1))); +% stats.maxMov = max(max(tAccCS(:,1:end-1))); +% +% stats.meanXSub = tAccCS(:,end); +% stats.stdXSub = std(tAccCS(:,1:end-1)')'; +% stats.minXSub = min(tAccCS(:,1:end-1)')'; +% stats.maxXSub = max(tAccCS(:,1:end-1)')'; +% +% stats.meanXMov = mean(tAccCS(:,1:end-1)); +% stats.stdXMov = std(tAccCS(:,1:end-1)); +% stats.minXMov = min(tAccCS(:,1:end-1)); +% stats.maxXMov = max(tAccCS(:,1:end-1)); + + stats.accCS = accCS; + stats.accCSXSub = tAccCS(:,1:end-1)'; + stats.accCSXMov = tAccCS; + + stats.accTrue = accTrue; + stats.accTrueXSub = tAccTrue(:,1:end-1)'; + stats.accTrueXMov = tAccTrue; + + stats.recall = recall; + stats.recallXSub = tRecall(:,1:end-1)'; + stats.recallXMov = tRecall; + + stats.precision = recall; + stats.precisionXSub = tPrecision(:,1:end-1)'; + stats.precisionXMov = tPrecision; + + stats.f1 = f1; + stats.f1XSub = tF1(:,1:end-1)'; + stats.f1XMov = tF1; + + stats.specificity = specificity; + stats.specificityXSub = tSpecificity(:,1:end-1)'; + stats.specificityXMov = tSpecificity; + + stats.npv = npv; + stats.npvXSub = tNPV(:,1:end-1)'; + stats.npvXMov = tNPV; stats.trTime = trTime; - stats.tTime = tTime; + stats.tTime = tTime; disp(stats); save('stats.mat','stats'); - set(handles.et_accuracy,'String',num2str(stats.meanMov)); - set(handles.lb_accuracy,'String',num2str(stats.meanXMov')); - + set(handles.et_accuracy,'String',num2str(mean(tAccCS(:,end)),'%.2f')); + set(handles.lb_accuracy,'String',num2str(mean(tAccCS(:,1:end-1))','%.2f')); + + set(handles.et_accTrue,'String',num2str(mean(tAccTrue(:,end)),'%.2f')); + set(handles.lb_accTrue,'String',num2str(mean(tAccTrue(:,1:end-1))','%.2f')); + set(handles.et_precision,'String',num2str(mean(tPrecision(:,end)),'%.2f')); + set(handles.lb_precision,'String',num2str(mean(tPrecision(:,1:end-1))','%.2f')); + set(handles.et_recall,'String',num2str(mean(tRecall(:,end)),'%.2f')); + set(handles.lb_recall,'String',num2str(mean(tRecall(:,1:end-1))','%.2f')); + set(handles.et_f1,'String',num2str(mean(tF1(:,end)),'%.2f')); + set(handles.lb_f1,'String',num2str(mean(tF1(:,1:end-1))','%.2f')); + set(handles.et_specificity,'String',num2str(mean(tSpecificity(:,end)),'%.2f')); + set(handles.lb_specificity,'String',num2str(mean(tSpecificity(:,1:end-1))','%.2f')); + set(handles.et_npv,'String',num2str(mean(tNPV(:,end)),'%.2f')); + set(handles.lb_npv,'String',num2str(mean(tNPV(:,1:end-1))','%.2f')); + + set(handles.et_trTime,'String',num2str(mean(mean(trTime,2)))); + set(handles.et_tTime,'String',num2str(mean(mean(tTime,2)))); + + set(handles.t_msg,'String','Statistics completed and saved in the "stast.mat" file'); @@ -1362,3 +1617,559 @@ function cb_floorNoise_Callback(hObject, eventdata, handles) % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of cb_floorNoise + + +% --- Executes on selection change in pm_FeatureReduction. +function pm_FeatureReduction_Callback(hObject, eventdata, handles) +% hObject handle to pm_FeatureReduction (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns pm_FeatureReduction contents as cell array +% contents{get(hObject,'Value')} returns selected item from pm_FeatureReduction + allFeatures = cellstr(get(handles.lb_features,'String')); + allFeatReducAlg=get(handles.pm_FeatureReduction,'String'); + selFeatReduc=char(allFeatReducAlg(get(handles.pm_FeatureReduction,'Value'))); + if strcmp(selFeatReduc,'PCA') + set(handles.lb_features,'Value',1:length(allFeatures)); + end + + +% --- Executes during object creation, after setting all properties. +function pm_FeatureReduction_CreateFcn(hObject, eventdata, handles) +% hObject handle to pm_FeatureReduction (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in lb_precision. +function lb_precision_Callback(hObject, eventdata, handles) +% hObject handle to lb_precision (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns lb_precision contents as cell array +% contents{get(hObject,'Value')} returns selected item from lb_precision + + +% --- Executes during object creation, after setting all properties. +function lb_precision_CreateFcn(hObject, eventdata, handles) +% hObject handle to lb_precision (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: listbox controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in listbox5. +function listbox5_Callback(hObject, eventdata, handles) +% hObject handle to listbox5 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns listbox5 contents as cell array +% contents{get(hObject,'Value')} returns selected item from listbox5 + + +% --- Executes during object creation, after setting all properties. +function listbox5_CreateFcn(hObject, eventdata, handles) +% hObject handle to listbox5 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: listbox controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in listbox6. +function listbox6_Callback(hObject, eventdata, handles) +% hObject handle to listbox6 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns listbox6 contents as cell array +% contents{get(hObject,'Value')} returns selected item from listbox6 + + +% --- Executes during object creation, after setting all properties. +function listbox6_CreateFcn(hObject, eventdata, handles) +% hObject handle to listbox6 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: listbox controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function edit12_Callback(hObject, eventdata, handles) +% hObject handle to edit12 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit12 as text +% str2double(get(hObject,'String')) returns contents of edit12 as a double + + +% --- Executes during object creation, after setting all properties. +function edit12_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit12 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function edit13_Callback(hObject, eventdata, handles) +% hObject handle to edit13 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit13 as text +% str2double(get(hObject,'String')) returns contents of edit13 as a double + + +% --- Executes during object creation, after setting all properties. +function edit13_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit13 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function edit14_Callback(hObject, eventdata, handles) +% hObject handle to edit14 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit14 as text +% str2double(get(hObject,'String')) returns contents of edit14 as a double + + +% --- Executes during object creation, after setting all properties. +function edit14_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit14 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in lb_recall. +function lb_recall_Callback(hObject, eventdata, handles) +% hObject handle to lb_recall (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns lb_recall contents as cell array +% contents{get(hObject,'Value')} returns selected item from lb_recall + + +% --- Executes during object creation, after setting all properties. +function lb_recall_CreateFcn(hObject, eventdata, handles) +% hObject handle to lb_recall (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: listbox controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in lb_f1. +function lb_f1_Callback(hObject, eventdata, handles) +% hObject handle to lb_f1 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns lb_f1 contents as cell array +% contents{get(hObject,'Value')} returns selected item from lb_f1 + + +% --- Executes during object creation, after setting all properties. +function lb_f1_CreateFcn(hObject, eventdata, handles) +% hObject handle to lb_f1 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: listbox controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_precision_Callback(hObject, eventdata, handles) +% hObject handle to et_precision (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_precision as text +% str2double(get(hObject,'String')) returns contents of et_precision as a double + + +% --- Executes during object creation, after setting all properties. +function et_precision_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_precision (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function edit16_Callback(hObject, eventdata, handles) +% hObject handle to edit16 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit16 as text +% str2double(get(hObject,'String')) returns contents of edit16 as a double + + +% --- Executes during object creation, after setting all properties. +function edit16_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit16 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function edit17_Callback(hObject, eventdata, handles) +% hObject handle to edit17 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit17 as text +% str2double(get(hObject,'String')) returns contents of edit17 as a double + + +% --- Executes during object creation, after setting all properties. +function edit17_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit17 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +function edit19_Callback(hObject, eventdata, handles) +% hObject handle to edit19 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit19 as text +% str2double(get(hObject,'String')) returns contents of edit19 as a double + + +% --- Executes during object creation, after setting all properties. +function edit19_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit19 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function edit20_Callback(hObject, eventdata, handles) +% hObject handle to edit20 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit20 as text +% str2double(get(hObject,'String')) returns contents of edit20 as a double + + +% --- Executes during object creation, after setting all properties. +function edit20_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit20 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_f1_Callback(hObject, eventdata, handles) +% hObject handle to et_f1 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_f1 as text +% str2double(get(hObject,'String')) returns contents of et_f1 as a double + + +% --- Executes during object creation, after setting all properties. +function et_f1_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_f1 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function edit22_Callback(hObject, eventdata, handles) +% hObject handle to edit22 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit22 as text +% str2double(get(hObject,'String')) returns contents of edit22 as a double + + +% --- Executes during object creation, after setting all properties. +function edit22_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit22 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function edit23_Callback(hObject, eventdata, handles) +% hObject handle to edit23 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit23 as text +% str2double(get(hObject,'String')) returns contents of edit23 as a double + + +% --- Executes during object creation, after setting all properties. +function edit23_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit23 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + +% % % % +function et_recall_Callback(hObject, eventdata, handles) +% hObject handle to et_recall (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_recall as text +% str2double(get(hObject,'String')) returns contents of et_recall as a double + + +% --- Executes during object creation, after setting all properties. +function et_recall_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_recall (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in lb_specificity. +function lb_specificity_Callback(hObject, eventdata, handles) +% hObject handle to lb_specificity (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns lb_specificity contents as cell array +% contents{get(hObject,'Value')} returns selected item from lb_specificity + + +% --- Executes during object creation, after setting all properties. +function lb_specificity_CreateFcn(hObject, eventdata, handles) +% hObject handle to lb_specificity (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: listbox controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_specificity_Callback(hObject, eventdata, handles) +% hObject handle to et_specificity (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_specificity as text +% str2double(get(hObject,'String')) returns contents of et_specificity as a double + + +% --- Executes during object creation, after setting all properties. +function et_specificity_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_specificity (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in lb_npv. +function lb_npv_Callback(hObject, eventdata, handles) +% hObject handle to lb_npv (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns lb_npv contents as cell array +% contents{get(hObject,'Value')} returns selected item from lb_npv + + +% --- Executes during object creation, after setting all properties. +function lb_npv_CreateFcn(hObject, eventdata, handles) +% hObject handle to lb_npv (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: listbox controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_npv_Callback(hObject, eventdata, handles) +% hObject handle to et_npv (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_npv as text +% str2double(get(hObject,'String')) returns contents of et_npv as a double + + +% --- Executes during object creation, after setting all properties. +function et_npv_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_npv (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in lb_accTrue. +function lb_accTrue_Callback(hObject, eventdata, handles) +% hObject handle to lb_accTrue (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns lb_accTrue contents as cell array +% contents{get(hObject,'Value')} returns selected item from lb_accTrue + + +% --- Executes during object creation, after setting all properties. +function lb_accTrue_CreateFcn(hObject, eventdata, handles) +% hObject handle to lb_accTrue (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: listbox controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_accTrue_Callback(hObject, eventdata, handles) +% hObject handle to et_accTrue (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_accTrue as text +% str2double(get(hObject,'String')) returns contents of et_accTrue as a double + + +% --- Executes during object creation, after setting all properties. +function et_accTrue_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_accTrue (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end diff --git a/PatRec/GUI_TacTest.fig b/PatRec/GUI_TacTest.fig index 8def0118474a7dbf744e77aab1a15591798ba270..c6075f33e39b0ff80c8151dd4f249ed3d1cbf588 100644 GIT binary patch delta 5673 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 Pob6XjPQx&>XBJrj&xLu^ delta 5689 zcmV-97RKqJFTgL5H5X`gAVg(jATcu_FgQ9iF*-3YATls9GLcaukzfUXR~7&O00000 z00Bu&4*&oF&lLawc%1B6L2nyH6dv12+N2~TijdMvl>?}W6d|Xo6a+=tB&Ea>X-w=? zAQ`#i@z@@+-d)YihL|%*93xH?@dJ86f=kaFIdI^>0SPK0gd!AB#HCUtF3jxCtamp~ zY_9{MQl7kde$VsX%=>14cHW!G1OQ1 zM(Qrb$L&W>i9^z;(o6ax?;6tnlj+`Y&~Z;Gc}@J;wg_h+M0qHGA`WZUY}2OU7M`LB zoj`GinD{Pi;y{tJIId&59srrMb_I;1^D7GgOgj;*Gg;}V*Ab*JW2qP6@65aya4Aa|?!=S~ABiR= zVI3SNIS&RneI1N{f#^DrGHO5y5yzDE?&nI(kCYL%IBDX*k@T|;PG46U5M5U@;<@J0 z(Q=$if7y|f{TUtn1&yA0`fAzd-zfgA;@>O&gW^9b{;vGb5`VN_D=&Fx&d z2vHF;{<0{q+Wlv{^q-0Jp82QDjPAK`I zctmYXc5%7k|H@&VST$R^%=byce{guuANT%Vo5V?YK@;oXUjWaalvDyT%nCUs`-igJgDN z3GFifvEB0t?S>QDCB-wT9Y|<5-odV){P%C~i)R&Q>KU=q@tt@5)oorc#rl`BzppKx zSsm67&V!Ek;YTO$=hLx%&+;D9=rZon*tn&C>=I;KoZ6<+4{{&*^y8dY7tN0Km(M1Z zybq4wN3M!@yH0J_s`~ekYwCU1zsIlm3Y-QR+Lv3QTrbu&#gOAWNE^hj_u{vEXL+yJ zZ9RtT#k7!8sg!58pK04ocEMA&zn@8lzl^g9xd|)OwpEqDupw>#l=Eryqr#eS2gcAdiJtSqWnX=A1M*2-#W>qd5QvC>;!Nk#moK&b=n zz1SJbZl=$=Os|8v5H8_2x?OgDVACDuK;+}K-(`HI7+z9lwU@HSTw}BHK1O^2?}}-u ziuF4m+Jxt>^GaE~4$x#}{n}h#IA*ecP=3gDDQhc~*YnydS^+00030|Lj;nPZL2Del3&$22u~A zUQ7=fO2VN9jK-L-P=g{62rYVPQm5N#JF?wvcBcxSxR7x0Ks=#8z=LPQnIE9P0R0zw zcV@d|r`wfon^mjX36t&D-S_%`=9~Auc}pAs#@K8S)zGPFXwV#!Y~E#agw0Xs%16w> z5ov%325JiwflCi^oha&MY?6ID4 zX+3cVt5swom15Is`AK!*bx86PWhfGf*mfsFExULZKKb+2q5k;i>#QNFp`vLRw0Ymc zb+!=7m={>5M8_gspxn?^PF|sk^b%v5|Nd4NzHYeq;y80JzhxQ~6V(zcM8j5oV=1@3 zw4C3bOQqArPX4_7M%w&;Mj-FOyB|Bnu}egI<5A>6QsM=G?$@0JEEAE6FAa0osw1_@ z*GZT7kc%<@5pulB{kPpJSEI;umaNN-Te89`39ba!Ac#izcUOv0&mlrnW+?g3!+N zY=_-!r9t>rc$}GWe$P+-kGbOG80m(-Uo#q(|}m_F1wzKFi#tq%`C^) zp(7s)Ev8%k9!dTtTycy*`@V8~t@-h5RJtd$>dtpBT@OqCI5!FI?6h`%TDz}Q%WS+~ zP(E^aKBc^S%g6V{e)4wS2hZ+!;tb&up<$f~>rBzb7sq|o8F9w;P-wzB<6UQjR=K}p z=q*ns9L%p8vgi|kq>+--;P~Nt(!~?U?OPqJ9}$<|_`LEV$bR;9>~Yx7LfFrS=gi>r zvv?1y4T^K5hQodqf;(qo5KKS&`4b;=$`>17?Wv!Qg3MPG(znBYHmoy$r=M-BGhsg) zSe+5i4NyNTZFR7IL|lI3`|bVgVY8n->QSGf&OL+9ezw(95?ix%auDj|mpZX!u{zb%3Ec72Nw>Thf%f<~&Y1*h*8ic8xq$2e223A^YvDNp z7wbcuBleenXLEg>BVPN+8`K1~Ta2Ia{p&iJ56B*1z;qH@w{?>2^S$x_I(4$bqRWz3 zSjJB=sZ^c!PQN<(0RRC1|Lj@KZyQAvAIGtiQY9+iAOy;x$Z-{_qybSrq^#>I)Kb$# z#Np)19q*2vA?uxGcQzy!)Jr8KIE0=G{{mN3aRYIG>H+Z&RFx1Xjv)1j1T(uc@$7n& zwb$DwLVnU{{3i3B-@JM6{oXhL1XQtY6YG#-VqJw4f;#K~m1fz{*eJ3w&BmlyF9<%z z#xr8(bWZU4Hgup)k>mFCTvqfHWAx+{J!i!Fzdl{oxcp**9g`tDCP9ms2AA7N_+C|x zfA1WB8wclWchA@USjqdV?`b~!TuuA!S5C89^gMC)OoOmrIA5<+=jzC%Y0g8@o-cQx z$W0{pvB2bJ1kYy^y7UuhVnbZ?AoqJFat_W@s5k4FnnAwV+N;4d?IY1^+#N z=+7HQYPpb0k!%3{3B#yrV%rX7Szj75+C(N|31O_aK4r7A#I{wpq18dFx&_uvBW7c^ zd6*R6AfiXgrNJc++t5PhrIW~yBzuzlkPpe{0cp2W*ReI@*a=U$M@gMr^6wBh%TfiY&nF8fOV10 z0*?|0NZz8DIbN`!?OA^0H(BHxEbXFxjk}LGK=?j6Uwyx^;gF64yK}49L~diPQomlg z()jq&;?h!Uuz1gf^yWYVq?6Nx+{Tc2F7tL?@e8ZO`?jb?Z7l23=fv4@ZKwx-{(6vL z-!~C|ao%mexv0%=2eoAZ?XqhzSQ#sSv{h@?q0U>#`I1bMW`BVyEp!k)lrM#ARjJ(5 zxX|%1pXZO=UoH##()MG}BG!JFc&?)7xOiQ<f+w9#; zPoJ94GZ8zAARGsre%;FaA04N8&>yE~1^+HkqZ5b>=43m> zMh?ZuWx#_?!$BMYT^s2bnumVT!0`8+ZQmXA4~s+g53=6f2L1g*6`E!X>N}Djwhw36 z=ABeuu1)szA4xv!};UyufZQBEwa}L!mA-(DGg2epuATaele>U z1~RIxor_&z>yO~*yvc!H?tlUPk(FQL^n^|TM#Pgse7~9gegBdctiB-n;*#M1SxNhj zU3%;4`s#rGIvbJayaNq?Oo9;b*Q0rxgf|oHlZ^4F{qC6p;q!3bX*HJ&(#}L)+9Bs8 z|2}a%FWm6)d@m)Q^ZJs6=T-UG7c?xsZ@I`(RgCd{jPFuT17mz2Z~69?2^fGTdF9ylRR+^YTu4se14GrK!@JlS@4T|1?cM_#SJvFF=2 z-^_dS_7wpD3;Z&FluNWCm;xQ~3qs1GFe81b+hL(my`79-Sa zb&+Sg7%cI=J$}u{&zt-2Im73_syx3GZbFT>xN5eH?3l#U~bhYN}28>ZJi0w_|S<-j}4lLTo_Rw0?aShS4wSff@sBU|yN93Xem0+A*PO;z=GRxRZS&80_}j0dEx_%tV;M&O`ll** z-%XisQJb&8>zA(WZf4Z$1#P^*KC%gowm{e~cs>XD{7=M{-x7KainaFr)hkj4f>x{-L@PJs+!x?c1V0xx@RBTXTuy^>`Qrdo&1Tv zg~ae{h@r4AUhhj~yV%QIcJmcsb;fpZKd8GPni6a+x*wgv@gX-_0 zzc0am5Wc(x-wK-nv*_}ori;K0hJ2m~IOS%oE_KN}FNP19e%?&N&(r3Uw9f31wE~9N zKw0NW@#yz7-f8>$FNM8u-OFe5=`Qm`v0l2rZy@VHoZX&Xr!LPH>&SK6yifkF`?&7k zx^LHgj2PaufPk|$U!!V+}Nr|7O6g|cN_jAVES)QJpr(c*_ zo_?G92SFcMI8$ES()1EI=)7KmE8X4p$Jksn#gM6fUPXEc8j|U zy^TBzx1{GRuN@n;<_w6qOTN|BvEvEysCU$(GEAlFFPq9E$9=7gdrAAB!I^gIyA6SV zUjfJVVQZ-HkWbB%7d1QPwe=MELiF`fLHSL6=NaeRe00C}@P6+qbUM772wiVYB!$i!b|{UILF1U*yS}DZtF)OLBaXbAA^2Yf?V0F7c#! z^m`ib)brPkD1W`1(=TOw+Xu(<*Sz_E=}G3JPgCa!JAbakb)G8`27gv{KS;9g92m7v z;F{PR*e+j3o`cJxy>Us}Iqv)4N!NLj>Q};+cP2C*fd7}j-1{{07dWf$Wb(J?u)g!t zm9U=kcQx|!(fUrBJwVe%>hQeObCT-G!#d15m^6=mPvf1oe}DfV^6%2Do-?a|=R85L z+51+$_pOgKy#&tszQsr{Zz(5i(|X_f7XSeN|Ls^oPZL2D9%@@bg_J}SjEUiZp(K#B z=z$m$rk0>25YQGcHd&_IX}hxB-Rw>UZX7vyAfD8-zX3h^1Bics_%HPC%0+M zG*qIQhEhjHMaRmXY417~Qn7^hQott;0-xzq-sP{D^xZ8k8QzwJmki*sHE{`V zjLvBWcG5UIX*`2JPw7}+fJ{3(nS@_Hv+-w{;btV@)a67PElTgXPlw1LnKq8V7{YA~-C z;@<8(AA;6;VtW2*Y0EZiHfl_-YC3jG8@b|o?s@6;VkVm{cNV{3+)EwCJ!l8H-Bhf* zIEva!OK|~!99X=_tMtWxyeq%@-Ta35_cjnOraRR~8JMnbmfvBO%;WboEfqx2+>7ls zrHIrbc1U`blog^iu#&eiB6w9bX6G+h%-5P4lLBjQ&V9}VuZMs6H5TxT>BumRgNE63 z`2Dc>i}Anom)pSaFGrxe`WWXrlR9$98>DNMBB}}hmO|ovGY}_#rt=i{#a{R!Bn-i8 zm;a`wng+2=UBZ$5YS)M5IE?|nEd+TQwo!f>+L@lzBN4j1{>aNhYOh@y0 z>Wu=iHKXRQr#Wh3$v$UUx~3v#vO-w{c>2yqCvg*fLd1>fYMr)Om0;WPaiYE-_5Ggu zzExHnf^4FQYLEJV{#E<_9dNaCrS6_16W+P<^Z{J^I72^6uwG^3F6^X#Cj#+edV%G+ zO}xYUY>ZwFYabg0t?$8fQL_E87k&r{L+}Q@kA023a`SuCWnU5fc?VqF$HMll0y)r! zwQu##k9*L)dAPv&@x;@?@8w?oTiL_9KJ6ZH&i(85cNoEc`(zkANrKk*VLJ0Ao9U0e z@I%P3_zs#U$5zwj$$rF>OL_9m{X-%L-J6srGcHk5p3ZsF?YYMR*Mt|@{@4pYgoGh@ z1LwtI#EY(Y@nbRK1(51`u|M|04D%B@OU72k%8Y`p3HbU=S467 f8&z=4#@M+J9{hORAA8}4EdK(ll#SK1I2LIE2O_dO diff --git a/PatRec/GUI_TestPatRec_Mov2Mov.fig b/PatRec/GUI_TestPatRec_Mov2Mov.fig index acb5527d51f9eaa9d0ff3df9f52f5e8654cd5583..e7598416d6d743832997c4bd30d9d8242cebf610 100644 GIT binary patch literal 54472 zcma&MQ?zJ75+r(fk8RtwjeBg{wr$(CZQHhO+s3=o-D|!1ocyU>Ri8U6A~PeT1(c)& z1o2sD>G7onlxR%Mt&FMh<*f9bOl%#jIq~I$RK;ajnW^!G9E|myjE(SZZ8-6zZEf%+ zoUQN~+3*?IIGLF_nc49f=^0q?{~yC+7T~`DC@u~6@9SX(0KhHlN?qCvn+-W-6>Iao z{;~<;2swrS*DTXU5v{eD;LsQAAopq`MiVv4MTM)b54b#z1=AMS|lR$TV+i8!|BwFL}d&ba}} zNsLwl62wVt+&bSyxG5r8ck0Cy^xWRuPU-nI@}!1cOj_Q*$OtT8uq-ew5&_Hvbdw|` zY|a5J9ev{`40nD9P5Lw5X76w4L4rrJ)=0{zT(|cq>}8yj+Bpf$c0ov!E+0fjM4*mq zZ35H5EOtLF7)yq~K|jGUd@}{q^QQv}rv~mYYes-F4i~+EmJtDKjUl^0iU`0c0af~v zI-njRf-U@t3H0VigDMRnYovBFa1S3-k|CO6!7;bavS<&t=4RKNT&_dd>K~|#!!Q^j zGz<4Wdn+qJ^Mgv$d=!__T|D5cuHL`=PQJxfD)@mVk?6KQIfeD^MOK6d2b3EbVlzyz zF1&Qt_#Qek;Vv7c(@^%br;{#Le7W;_J=FrU+}myNcbXBqDqhm}x+uMsZ{EYJD8EAY zd?>zoZ$iUt$iI^J(!+Rs#$HS(r=nIka0Zu4mpfEEvA#>SDu=a)d4~muMTaGaWrr1q zRfj#Q9=N}LeC2O^90{)@EOn3GCjMvMq{sPiP!EtN!3Yf!P=iZP6$c@Q!Q3>Rs`x^| z0#c@VnL;N9K0Xz4MsaBODD7|5BX7H|-{j;S-_K%>j;wfy+g{?U5A{ z0%kl~cCt_TW!sCo@gSlZ`H_0xEQC+d$lKW~;LV1-nxDdl9h)+VInXbHtg8!Ud26lhu*wOMZgK ziT_&xbFeezj{A7W^%-2K$$_^UbQM$igXtp?IG%srm2~VnYcN-?mzX;&TwrRJi-Y*e z9oTpviXCKyh-gNjqHB8@^ImA#`N5;Wi^Bbr0wnK?m3^pku>Pq#+R~;^eKP8^(yIBa zdJxF7+`Jmt@p3Ixyjr~e#`wlem7n30)=%|w zhpAnZR0uIJkJ<$U^_=LfRr^t-gF+`2__5K9AH8{L#&@|E{#Xjl>@(JV?|m}~cqsb3 zGkwt=8`a}VlFg8?(hRJzTQO?Ywf#F;zm;C{P~YKr+1`|XE4vn(6t}Ul5x{K*);aU% z#}}-naJ2qoZcvTKf@Y<+XZo$J3$@a7nBoUl+7c~csFB*Cr0Dc`bTOB&p5{%y|s7xDzS)6zZI*73#8!JRUsr2mNoesEwA}DZ|0FC zw&>;aNOpnq@9O*YYW{5BqMh&fmpk0+s_A#%{p@LT&dSSmPy*C7%q<e}6xuoTi8BfcaBgZ9^}R{7*#AAcC9 zRD>4FW=n)9#SqnC+znvXmN1aUJwj6Q+Zy2=0_Rlw@ozi;fZyLMRcToy0rVVHf&+g) zhWS9SUx0bxXpaDeKGpyRFJuBzlQr*knXDP=&PEGysVK0+?MN`VA6PnUZuFa-eX=*C zto0ce*U;e>sIar1bEgm9ZPSx&SKeIG2x|mSSbcJ1*)~~_eF~>=C@X`>0ir2!$4e={ zQxE*!PBma|0ct6#B=#y$DKRTj_<%KsKTF#CwDq_GouAkURC|$w44(c}W2C`n+eW?mXe%r0zPyDo9q2yiwy0k|_x4v=a^jhl3k*=2b=a_UG#l#VEHva64iVtM}S6 zX?Ks$lkC95l_YLN7+v`wwn{0M4O_6+n@Q@g5kw6~o=4M~$i6vU3^&d6)2no_AbjCI zrJi?xVR{~pJG>z*M%f8%)eluAniNxdY0Y=+4`&Bk{em}j`P}J>-$B06)^HV1qnPX&SgMEy;(m1c7u; z5A9n|yO) zPOj-)9!0$5j-%-q3u{*@vd*T{-(y&H+i~O4cTP~OdgOmnv38%}9;#HaoLGTPnVxcA z4HC+dBaT}y!4x4$26IpZ7u6|&VQQvqCWcoF{B0FcImBI$cT)(bh5u77EtoN|5=N5& zQ8hOnECbsfEJN$$P_QdVhQ`^x>l~n(e0?p`pG}+6wo0MIy(=$ImnG5;v#`wwi@>us@iS_RxsZZNt zi6G}TC&qy&u^>ahis3I&6&wf^lWo>(TI~y>l$vW(=8xYr@?5x?aB-E`{{jtxmb1Zw z#dvLb-oV&HA%;ruz}ti=zOg&#nRH)nL?(Al^+V(dt_xBR$w-Y~t9}*vEZh$VUC}}EN zQ`FIcb)c+O_4%ln!=|Vm?qvIEitqjDn1o2wX|cyJ&9h%dw;=&djon{S@)U2T>z#Wr z?7t!#Mv(a8X*i*Y;*;D#N7I&SjE?k7$Q^1MhInHEu6?GQljTRmiXGS7_)=%!<9I$_ z2Y79I6wN0o?N^7$3f7SgcU|zT$$#_ULRU{+Jfgsh$~5-%`pr&wc8t||H#@wp_4z5R zyVogogTOI~0L~wOhb5WpnIN-sC)THX{5V4#&8K(Q&bU7r8Tw?lOWbi>cayE&y%vB| z7|AB>?ONtsexD+-<0!H%{e`sI=$c~wRSBBJV0YxBtqjwFcG_m+hnE`nwv&jm4|k@i zbF|-k1(-g0edk~G#@VOB(R83ORH z&W|BmbO9FYBqGvGhH}tdXP}W^FwxdST!si7BFGosw1NNYV$f2l)&wC0!QdXBQoZLD zwTY>KJ|*Eu4>5p(PcShB3$Fb1%bq2(J^F~Nd!#MMP- z_k_MNA_|nG^~$e=Oc%8&Q6s4g+cdhb>eIyB&T3O~w*e~4%PX6s)H*slo>iM|v)!{d zNH!R{IzN85Pwj@4<~ucwL^qRv)%A8W$ye5^3;Fx?{?=IQ%k#B+cc5K!Q*jf3M(1`P zfQFa1{zdC{|5ZKeK9EgJ$9v(k$QXl``7?+~wUH`mHa>C8v~23RhZ8A_2Il4A?A-G0 zOgqQds_^avDYA9sITqJI*aSqDBc0)8ktNvfy%;(W9zPb*_X$Oj>`_?(<{!sUMv|oIXX=~1*$vxvU+hFUx z-mCcZ`8WFD)>iB5yuEt0B*G9IFC&>wBjtGX_YHiDiQ?WjaAaJcp!`=w?B#Rh70fV) z?HS(m1CLFH{1eF+PiE2-%9E%U>j&yT5RkUg!$| zH)2K{(&0?MR=f_`KtV_#5Ck63hVF#U4@?^bfCx$qLXU6wCvH<1q-4sD3^KsVYN~}Q zpK3d#p_=}Pz^*MFhLa?HznTzggv4=W{@!IOAB2{8@!kn zJCdBbi37sxdL79t=VxiFzCi4!pztGy;D_C}hwf+IoMmiuH2Q^+c9|?~3}7JiwwN~U zWDvN8A)APnr#0-yl>9J=J__li0e40Va$JEReDTotyZf0(_%iYok{w2cTz_|}SH>>y z6i?#ci5zED6v3q<#dAOOvp~{^T-6`~<7h3SSh!9IvB5qG`MfIcyw1h>E>*jj@Y1(1 zf9vDgjd=E7kn~Xl{|fjtFDi%x2$C5P`7s1F{|J2&d@)4HAV{^4ZDqnb{wUiG-V>@6 z!EkyZp2Z%;?!oa-_Ii78`T4LoRR&a0J^@8Zul85?Oq z4)HE2|$oOERx!w)|EHvML4OzYc?x@#Q-Cb)>+-a(+ zrJhMIl}0io68i4mzNU$8?vS`fk{%!Ix{7*IA|aH1!e_gc<)~dt>Gdk3QPDMZF+A^$ zGB%%>d!yzG?ib5Zms8oDH_g(zyDYgGX?d{Ddlu=D7;BPQ*s7?hb+q(naadC^745{F zfLfj!r2?F;yvWda9yJWY_|(c3*sb){t)&?g+{STrmr-BL*F+qF7Q^VA}Y(&ca8~V8^u?*3vazpoD-d@qBBhp9iyN~o?Tcr(Cn~-*u z4o^3z- z1(x!0HxB;^_T%DGk)bEge{8HH`4IdXYgI==etDm1mu@spMxJt-WPhQ@Kql4t5O}vtl;ge6LP_I0&5k;2t;c6Dr$quY>`(5gv z>PWP?RHH~-ZDfs+br53p>Wj*G#>O)tJwtCo(?4DqZt=2;T6YAwp^^NVAR;yBzx^)} zfrZw3jl+j_b07}bF#)XbLeY~6VhwpPL*!F1AFs!@WHfyR{H#VB>sX0#9$!`9mBR zfl7EXBPY#caCQHVr}HF)b%-hG>Py1kH9gdpMG6&8I4|2a-VeqRo<0 zB1#(_1IlnTy-$FZ2-;j+XY&B5<7hQ|FjA>pBgmK?8SOOy`4p@ryfR+Q&B4P3 z`&xt#_+`gL%BJY3-2rD?EQ3?(F(-Tyd$f5Ncny^Sm!`kL@l25OYdRoV4WG_aOyQov za|{KL}`FTAoCy2(pab>P@z#MVV~&$M?m1kFPZ5Q=>fCky!qx!DI_fm_S)Z zb;w0O;#v9Cffs3=(weO{c{v@HUn+}vH_PMK2a89ios72OkURvtt`!a5+XvyE4S3TooFQs}o2c&w zn<_{0n7>d6(oyfR3ra2b8+`u-wlvB3A$)j#IJG_?#5zAv>LEemCQxcUK&beC8zYEm zTNe|{gmUS#?TOAJ*>csOA7gF4;b{foqLAZ5*R8EDHfsJbxLbOfE=O2+HWlgy`Vplk zg?qq^#+-SZ%1vtgaUO<^YumB)TNA-SvsM=Hky09>tSTL?>6KViNjFvQ0HKhJtuNkw zvtm*vjYAUF)jjj3F(@hNRrjU|B=6$Fl=}SJ$|YWEVk(Go)W><&*u8#flDuY>BC9x- z0_SrlF1naP0D=zYxuv5P|FmNWfUzvBDKY&L4EP2 ztvtYq;-oub)Y=?!Raj_=N_?TDL=JJ1_HyD0Yi-FR2dRpLDn;BQXi-&Y5|`IpLUK4o zOcS}Rv%(QeCko?yUhlD?r?~9fr~fJHy3{rE2l1i5%Rs5|0ipi=E7Ul6D7Ak(B_Pzg zxlO^{LM#Hi+1(aI*m3cUI`^J|NCLr-%T!o?$?*{EA|(Kuj9<{W7bC+WmHwed{Mysw7id*Kfx}OZ*j73k@m3jV8<#iWG1{3n@qnW zekaG6PSll>PwLejmGEehxch|n)|_*J#B9kM=3lwHCX+3An_F|VcZ-fac+)=pF`is* zt)(I@wYQm^qq5@6;*iib&);l=a9Z!_l}!|oO<6g>o?no5vYD46;M0d6fGvP%9h_qm z3(Oi+@UL9i3@_WV*9pOXW;m=f_QScy$%>6+$<|c5;Q`$axyYv?&B%l6Ud%`eusZpJ zhVwU%Mzj2=5@r_S8#8EZH>jL!eU%T&7dau7az2;DzU z{4YHQAd~>1l+mMv@q<#m-FF_ewowLzzK-xv^TpCL(YMv8@MjHe=uGZ~U@i@RWO&z8 z2yX{XJ6(ej*_E>fxyEZkG=n}}Ol~KEI}zomDC=*uxa$Z)=v(ipunYnE9Gc_m8ot*> z6I;A5Kl!In#L!0!GSC;qyIZ!^$3ME$q1Y5kaqu3~T;{e{Gr0rLsO{86GtCfUNI=N6 z{+58?2qC~m@2`L&a+~_gIH`)dg707qRa_S;Gl#oneW7lH>ENt+so6}nkp84CzWK`9 zp_+CNekfMTH4J8&=UDy3G^~V|u?T4HYe4%_gEB<|kxgNFswv%~MZ1I)i-5vqP1XRohjqgrPS3?3mjj@|1=(65tp+b0-r?>; z=d?0KhtWU(X-ypQ(Gq0iCFVisYFS4+Yfc2G_h}7}#?u0@ql2%$vhL~prAAEXq6*R5 z835n5|1|I^DDpeUqAu%)WzY=`B&srTYQoie?%E}jC$YmP)<{o|F*mjbODUO+*LDQ& zUo+xH`i3h04cd&@up2oF>9p38d8|#I{Indz*zLqQ#%GgM!;{gCCCbFNEUDtmA|JnG ztn6r6vvC)9n%MO5X4j~zpu}6K7%T_UbtX-7S4M`CP;CNbk zS_F}OV`#z_b===diVc@BFb)t#Y6E4s;63vKz zxib+io3gYZdI7>vf@h;1EiXJo6M~o@+ugrYDs7pka3pn1)c6hl_wNnDT5McYr2%l88KnY?|GR9@Pc-}h- zUj@u#{5pkuYV#sfFyAdZ^fgX%G%%CRbQ6hO2RHi&A&D{nN3sfZ$)?vdjGRMWd2B#0wjI0%E z6CQ>r$Qr_e)hCC(YFK-qS(vF^Tcl)D8NxbT#eqo@Gr+$Cq`BNfPm@8kNV2TuC&AxZ zBu}^|Q$J(ue6EF%TCALj2BmAL1w{bhaYHy}*NQ6W&&jgUf+EniHB~_L#oXjYpNDIn zsgcsr5XcsZTJ;3#okvZ_V+M8z1yz;W_w^JNX;u)AyjvGPju z5ARos5RIODafOTYEFl}YnohP^&rfbi6(QIbdc=6;Z+GhLO3Xf+K1{^GOLAR&;{i>; zn1+y6DCUd7!D9ze0m?_HmYcV5H0i5~`>zOca`u@Q5ij%%L#iiFzA% zL}OUTx4?bR3^QXf zc!P6=KgJogaR#K5CFqt8*x5;ARwE`CDq%4%X)w)c{@N0lhaST=7&lsdqM<1 zD7Be{en%*ffzA#oC3{v`IJv@@cG5@J0l&xD4-10MaWd|H!ca2zS-Yz$P!AidzmFys zc7#0arud8HR{mNw_4W^jv?%Q32Y3R;lQ8s?E1PzVmA`v!1Qe5Re^Mq=FwF$!Cr#u- zEJ>m3seQ~sE2?y? zmbxl>Fb#E|tC*Vctk@?E#-BA(GE_-u{6Me{)*ah_>Yh{bK=D9P?7Ue3SW8oynU|6- zB1zZ4=(A^D^2SqXS@dA;$>KFQpVau7HUXyb|LhzK7Ht3tRKFkazY=5)A7~P zmP}9-0HN*tRJv=H5Df3b_Gb##JZ37m0kpMS)d_rDtfKv`?{mNguJ;7XENnx}@o!5o5<|X)CgJMU`jvF#8*#?=;{M~eubzUzTKiD2(6$xk+0 zM=Jw2tJZxxkUWz|Qkid+f*=%a!5Zrj=Ifw2afGWPLOUXW4Q(3e!SyXk;fl zH5CBLHMa<~HK4B^T^uBiL3Z5z;rNwG##!tJ7U(=4mIonJy^qLuEl`)`DZQ5jx9^RE zg#DaB4+BRdSU(&*PhLz6-H?WSM_yjN+tq~_f(0a2(i|Vmg>HxGHEY|DQ`An zX9B?w{9Vjw{9x+PAgq%E7T;w(euh`EQ=1M($VsI&0Z~(xV?79$*L%3R(rm^|r{5sF z5Q2CRk&#yYNt1>bQyd#M&czQg%ZU&eA>_n+HzvWoTtEH=D10uR!pi9McS?zy;(tybiJKDlHm0!>;57`Re<~ZahK5x!h-oa~vkRElmsb6RG|<(ZwGrK7@g6MT zI?csc3ehO-J96j8rybHs6-^HvOgwiDz2L|tfphqwSEfVIraKhwAc4;{W|Q%ii?fk- z4u^Gl<0+sbCkK`*cbb||xOBqLSpS-VXs@g24tggY>)P;_m;u{iOd``<8PrdFsI#(E z92~himaN%IW%I-?|Gf@TxBvOb6gihi*~1-~$gHWTXyTL}fk$i;DUiL(c;H4PC`-H{ z-ZL)GizYf^swNju?`Mc|u73%z_JZ^u`k?p@ew&G@Yd-O4B6;t7*TwRbw5u2%ecw}ge8PWqiG<)!toju{`z zO?M+uicWatD46KV0sH5s-aAYlTp_pxs-razEdSE@$2)!IESvk^ff6%o;_&Xb>bFKx zENy7ghuz&7w77(Kb&LlM_P(gyyU|QB3{}p&)nBmx=mW{pVoLJJqrbP^439+E!wmxk zbMj4Al%OJm;50~D$g~wA7WiRQ;dyd$7V<$*OdomRg=dN2(O>+M0$~F~P9vzOB?{)S zCg){ogY36XHK-&D1p#cdH@1&ETaNqR?7yC~@ZVfB*nDZsY8K7~ALQg2B?hg5P_h6d zMfN_5%6S2It-`x6f+7y5zFs%AXS042oc646^XGa#s&9{FM-!7E{4^Px%-Vo3Cjj_%6Vj>nAKr30CrLvklKp&dMtp@u<_ zqu^Q8)fEMdq)C{SY3z|u-YCOt&^C97fyoIxvL|{d6i3>W)73%9vB+kF>0^*5`dy7= z_g<`;=B1MBw%n&hp1fGGq+Q01IzTqa8V5i2FVMRzDF zC4Z8Hgo+ECajeb`h!H5U+|j!K(N(gN^E0gx4!eJ#&rHX%)P{Ns0MLY% z;26qi5NB3JALlrO{oJw+_cR{cGHU9WphDQ5>kFHu7MoWbcR^PY>Sii>Ockl?6 z^3ars5I~TGd*MRi=gby!tcw#Tguh5$je51+D=%HMUMFf*S7>Sg(d>v=9^rr)2-vQX zi2_Ukj}eLdOaTWL)IR}=0z$Bj2^!>2RIFxGGT7Ob19O^$hK-`k4Tk}H6-NQA@AlDT zn48W5W{MjAD(*3svRrRDP^4-+kSLE?Jg83Tbjp^`c-1pRY?zZ1O3Nl6TXm?D&A8Twdbpn0bYT%D)z+uj#V&k#T-ipfy40(faL}7r(96GyW~&+G z6Kd!UQjs;vH#YXvsp2GinZC_A&un%$o{bGs+N(?9)Zs)qm(H8jf1Guzlfr;PjvCID zzCdQU$!=yphL6g%MQa1B{Y)@67lLS&#r}jXxF;C2nTj+E@^0Q(kiKAn0D6hd_%QtQ+=vexw+TdaM<5Zok>UH?W{z z5~Nu0Wh%1ovE7K{m}Si}DT%}n3BYv6XdJhwy)$o=2omL~JxM9}Y- zrvXttyh_-BC_Z^ig17-q&9$`?=&#ue^t|d#{&*)m?}qoSC^+D^Y;Su0aT5H+BCH62 z*x1NN4uI9aO}osCiZcgp2)%B8VUdwy$zW=(4T@zj{Z zR;n}@{91thB%oneAB}0v&|{d83KpPOIVvPT%cTaR|9#O4fQxe`Uo=FTH&=t9%A5wB zMEPMqBJ}d)NcK@;dBQi|4$0_%pM!acsTI7Z4ICo@>tjN}ixdH?pF+82wc`$+Z>lF* zIAEYF>Qq5PMm?M|vH+5jdZDC^Z4-Nn?d(=#X})hixpnPO$A}V48>9p;b|prV4``K9 zDsp>t=kbMusNB&(6=T&Rn#Fn`{~65-WE!mgC?zU|RA|xo^fgNfy=iJC3Ngpub$Ey))uGwWWJ`zwkXN2Z zfh5hJpEO7|7P=LsHbr?Rb+HFZ&OovGdv zvf|gInC=42*cMR5N(!@Tr9EN&k!kCK6hNZkprsaTV^6m>q?rvhPaZvuH*;qT1hWg2 zaK&`Yg$FJ5X}0FtW~`bUtL9j*qn$X%=JdHE%~iz!y{2(G+;`(k^w#+J z6jnHpARVe@`Rb| z-nP{iLgPTKn7sNK)b5eUM!c?CpGZQOr|qMK&g?FrI@iQVVC$d1Sycb)|EJPuQiK1^ zPydr3GR%nONa3Od@n%kdfWyn4fVFtG>NB{t+%sKFuriBKK|>X~D{s^?v~a65mfz&l zqI-K|XRF&>`$@V?os4jgMj88kG|`$o(~08)!+so-_oeQP>g>1?OW!E6k46o4OM><0 zm}8rqSp^Fq$uAP_;tS?2mz!CxB!$_tBa0+`T$_rVP|6b{NT)ojQ@-gr`^Awmo+Th?0T_o}&!+l#vC~s2t z{Z!a+R5aSjNR{QTei2|e_S{+r(=ymQW;+x+97)9VGV9a*-0}+?tgbsOwK(Xp`F+cd zi5hjz(oKFdJ#mQ)9i4w+#_ukBhz9@8Jv;`JDH&v%vZL@Oo-(BwM~(8&HM1?Hk)<#5 z>=9t@C4{U!hM9>)r(A*yD%uyly^#&Uh6s7Jc zU3N7!Lv8e*@Nk8uYAr5*!z!-5h33VcH;8uWv+C}TR@IqDytO)LNrc!)tCOlju?;yT zFTGR#3bM`JbG9uRLd`;ZSkF$}P507-M)DLK^wkXzWmCH9erYpgl|gO7>g>GDaL@BLFJsxNK-z_BSFvpFCo`F&L!+&1}Fzdu#AV097$ z;1>i1Dr($cdyAzg3Qbg_2R$O-DhwZ{T10fFXO94=vxg~3TP z&Y3A29vFlRg?`tUTs=|9dUk@KI;^0SjNpj%zQWZhW^u5jtgY#p@z&GCoT zJoLe3PSpBU*YA_Oq;wX|^t(ZmgB~93^^m(cmZ|oJKFL+(wl_V{sVY?tB65=u`^Xr~ zAnLOo@<1?QoD^vhFvEJkuY{z1GVDeI`Zqt>ch zWqk{p6siOZ9TtDLf3SlYKX9$PUdCb^s^8@sRq?t&|A76-utb_v>cRVzzPgPetL=_y zIWepjxzqyGKVgRLhZk{ZWdo{ur;N@ar}jWY=q=p%z*`QD;{|K@dyI+~Dh&K&b0{19 z^|#ZA44h3IdCxHN8|IBG-uHj@ivQtXcf1%gVzlt(|0(^M5w`)KDqaudL*^T8pCCV} zAEO_&kjb@w_O(hbIjsSlP|$^^-8^8MS{xUwYZU!Ld8C%Crv^ z+@`U=05_68 zv9?{=NpdC0#Ixw-gU*ogC0cq!=4OJJ)>!&OwV!WNW5mWh#*~B7dcUkMqMukT0GJ|e z>N_(xT83(fgieM_V`s!CUdmMXX}u}u>lN2>dJzbOOsXG!-bgiufS}OM?(u>4*Q-F} z89!^Ub4oay{k8ZwfU>#=_AO(TMkN($RtGmBe~+TbLgs&KXBcHzx_a%Ri0#Jge4eSr`U3TG&a zxhYGu1NKj)MTJT>9*m6%EQ%cBAEL?#JNivUrJRb?!?&kl%19A3pQ%#Y$3n(ABz7a# zThvig#XxY7vT8CGY!R+M#YvS+E*y-(GHTF6G?MhIvF6&#rj!iFWfMnPVfaajI0(Od zVPaW8BvgpOn$6bMt9LM>Kq$4~Za!KXqw4holOud}V}}ym%Tk zN7zXLY5EkOiaYE7C_PXHEcoehcq1u-#uW&lPXH_|$VCq-`A+^3-oOAnrc}U!vu-Bk zCyF4iD?lq>PTv=dP6Lo@^&5sn5wGx!LNg-^>YE-A$-fejy9V|5Nh#02@HNq+Bvceu zBMP-v4p$>PYl|4Gk|#?o4nkQJ$Q}s-%~E=8Oo}~3-}3^XSS7Q$~)_>vFQRX3!CYLizxF&Wi59#ke| zm`)Txv@@Tf6xYW(O4BV4&n-iSMcFE$wysJPs!%c-jg-8I!gA)c3dDY{Jksf-3@i(! zB8{ZB4#s}WveQf{Yj6s$bf1Iepk~53C3SqK7C)DrgXGYF1Vv_NOBXDx*q@?1^Uxbz z0*-WyGy)sQ!m{-I8w^21gj~0`b(AXt>CpCOen}+`S_WUaE|({<1l#XZf7}N`L414Parq@%%%`Db;uuhHokk>LJB zzYoR}wYP9qA91-HloUsu>o}i3TieJ#FZl(P77T;B93|(%}pBg*B{LhpWe|tZx?I z<{i$d+j0^4Pt?wpwX@f(YYVGR_jJ>l_MTPkZBDD(QR!l>E9UHX59fw9UYy(|-x^0( zmvP|ejNa$YzO&_#O+o);A|2cBfBV|>cSR&~%+cTD>WvMJPp{8z$2h+rB20r2swlsK zJO!dj18fu!dHE!QEh4r*9HjAam8a(*+45;uGUleQV7+|7GJNN<-LuHCAtO1;YuP}-7MWXGbpq^68A7En6pD|xzl#HxW;@$)z*H>0WTHA1m!l}P8R7Jg*xa7KW+NAyNQG0v4dEK zA+@f1o);UBzq_E2uCZ!LTwq zL&A4n)|vtlBi(Kr zG+wWJ5Qohe8jdfOKf1DFyxu`#mva8{rgyH@JOLH>Jz!D1&g~H(0TP3>xZPRYjy)|% z$&&ZYtl8<=9m+ykdB`P8@!BIz&s&MHv=x<@D9{C3NKS@RmDxUNqf|D>5lp?kGrvzz z;&9uf$^=1}$lnWS^(rY?YbI|~* zm)i-K^1A;q9j?ULk}rj2t=$IU+HpgNI}ekCeTLQ_T;y7DcX4T>RiAdqf{*dkj%@qd zpKYzx4FSEjuUuw8S z&GbfBB{rpz-AyK{Od6kxh4aXtC1U9O%_fcdv3hSgaEKIz33ldN|6QYm?SN1Ac=mc1 zozOqO^>s99TM5#SA*h+Qi-{?W zE8&D>6`O)+qMPvV?`kmag+uT!I1b$W+##e#I;U^S@73mTqRp0p1g3Q_(Gmuxu}kKk z6Bs;@ncQFJeX5W>~cq$E2x3`}PL^F|^}+Nprh6nXXD#e zxUSRGue%F%^}CKAJxEZCsuzDVp(2l`thRLul-t)9Gi5|xcZ z4+3Fo323@2ETehtYsZr}SYi>6bw2UDfJr(KmYWsjr0tI)2~m7PA~+w)RcJma@lu)=4hY4J|PvtHF@&D-&D@wkN6R z0`DU!tI^{di3L`UBAtSHHBFr^Px`4Hg7_u5pnpoP9cdyO95wz+jhv-Uz5BX~71Q{0 zuhsBjSv)TvkDvpVJ~do)Z#3#raV!*Tzf~|z$gdjtjIWsaCteX5RTO8Dd2BF!IaZD7 zdxz;vKa7-yFzs5j?rJ4vgaO<*+cy*J?1~{>pQx8K6&`+ot_K_TgKi{G=k!i@{Kywk z*K6nP$>PAdT`@$-k(!Rb`g7Vt!dUZ{qwfOe_x@r3fcL*SZ}~zl=o{I&$vPC}T!iOl zV-A%o)l4Y2P%+WK{T-S3`>(~{fO2Qk5EW_9R|h}!YoO=MOzsW#_rw3U1*je;;XU)8 zhEvGcR7CL%X0S(D7tT1C&F-2k;>gjdhRf`&mx&6n;^K!)$0&@_)itp+Ms>e2+P3JK zmWMDPn47@6F8PTWBkH(r%l`qAa}k3*WqZ)7iau9r2ySZKA=FSWI=sE9{M4Q!gH{Om_GQ_a_ObM@O0G zRg36>cG9B?xQ~i{$wuYR$JpX6Y9B=@r7O1IC^g~P-7@}5PX+NxE`O_1O10c;v^435 z(^?0#5svV37h%fV&frth<~Q5IRS_p{=U7(gr~Cx>UtjI=hTMPAXYSeA#~3!>ujU5gp`YacoCL;}OYu z*8H_RijImk%bep7+fIZ&YxFNkt9~%h&Fue8l!=waInz8EP!>q8!YaXgIKC;s(|aYm zt)sm0K5z{oLKShpCU(=IQ@xaRVOLv=5L&_W=}u!{n|TcJiFv*({Dz_Pi9faDQ{~*6bAF zgYFPe`YRVzs~*M8fto*+$7Zpm?z`M*=IPQ$)KWhE%jpVQo+aeAI=rtWifi_q^=7rX zxt%!}+(%fG!;hWvujedI6BNgWIuuf-&`clm9F$R+qg&QAk}dIwL61wBV`O0GA8`~> z{vTzIXX%bpR%vvraIF^jX85lXXl#L--{s()W%Z1?&vd z;<=)x?(I9nHsM(C4}8LBFm#U%%qmx| zK`6zBnvqD!mt#$FYej$@{k{ZKs^AdHcktU`+_BjbVxPM8e#HR}`WxrSQ#qDJfGqte zBN>eBU|^PXP|hKdB3cqDsT!=DkM~dBj(;hZ=IZMQwMtd7>I%}92hq(T01#J@;D3zY zjyk&XhLTiSj;BtFk2*T|cE>TF%1^qQ{QNJ@k)uB3)>;vn(u+2q%UZ;@S*MQ>dsY#} z&UI<(hQ0<$fL|9Xc%j(-(uwb&tlOKGgV%Gq)y|+k;Llr5P+xZh=aRhI# zPI|xQe7YxL`_?a#Bfm^3(eAC(pU})TI6JCmW?oL%A9T;BKlm3^j}ERPmM!?#0k~mp zO2MN5Qs`BB-BJJz(K#)F*2svjzSvt`ay9ya7M<6IPZ+XY73i{7LZh(lkL6<4i!O*b zFroALzWniO@EZk65RoKp#!QBK(WJ?cp=NCPnI>!+ZO@Q!ZjN*(KDzN#JnT9hxd@PY zj|(ZMhrY5FIPAo18mWryl0@=WH+szpW)%v}5tcE{*j()2nt%uOh(A!$RXI8e)*T!XbNPB1R%T7mq=wkJdm6p8w%Cjx?bC%dGo!smH4`^E1^3#cO#g*B zx_CNVBe0ATFiFb}V1yPOB36*H$pQI)RwnM<%X5)*Y4qK|FDBQ#)a=0>Wn?X@?7H0j zAW{>vcU89?pt0}y+wYvEEm!1TVK!79ykNT@1 zTk59EpGxvs`x|w3->|*}UTd3{3*UjQg9sOW9c{VGvL6jTd3fE1YzL>j%NR+@T&)zx z7#sw0dBrtNa~GQs9X5hMN*fc=PY>-Re?gh@y+;BS-6el^!k$tu+*^WVFLwppQADhH zJefjQG$pWr)C}tCHg_g2*uPBXnZ0Z%^jp7o9@+(}wkh&hQhyCEJc+O8hP%ztHXr+;63*$IJ*F`W^2*Szuz> zbF)mf``N`5@JpfDNw|1ivS{W-9qNzvOlaY+h}&E(i`wdgw;y#VXpy{N7&67Cg{c3a zC06@@;r?=c9Pi`N(e-i~;@x=exN>Aibm=tCGD;4hl4#6-B@c$q(2-Qx&w(D{5OXe3 zlum+}yGLz%kX!^Ie#l7BZvBeKKiDr(q)mq`KuQ;PRwR7TmGZZ%M_=sf%NK0*eH=KW z)HArJ?!g+#6!1-`J{-RaU%)Df{r_V+t^Xl6{=Z4^OohdjCdKC9w*8a=Aco1;HOx;p zMmB}OgkAL0akr$i?aBf$p&ei^{7bMTt!3Puy?o#s->Cb>H$tX+cwEHu%Q%MY6TBnx zOtfAdIVniDqV+2)xNP3I?Cby32{?$$mIr^`u*v7$G|bsjN^w>_rnXQ#FhxxYFcl=Y z+P5_(T5i;~P(3so*>#q}ku5FCvl$0Id7C zJUSO8h!v)2t4N(q>Kor+^g`<5X$Yy?Oq<4kXD)s5mA%G1>Kc4eu2pav(Ei3ZKB60D zBFb2W)eGoCzvv)orNJ1#@r~@oZLA!%aI5w}XL(6MaivTd;?X|7a3zOeQ1hjJ`b%!| zO>{CC2=`P`&|7S8lHn{%;z{Kn47MPIr~=lS*qV~8Up==JXI*x z-1s_%u0=%`+nIT9gCrGDNPm7&@h+TbtgQzY`yNeA;EOP|Xv_{>3y=C6wG^^vv$vnv z;rW+%S|7}QRPP){BGBxM!)^?o+;TO*H<(Ri4n|};>zb*_#vP=t{3`9bXbmgEXsoy; zspihExK_1y!lAN4qO!8%CBFJKz2hLQ4^6=PVnGMjP zv!lk_Uq$!|RXpY6tIpPSV$p;>hU*#=?^mPT=-bKk9x(?P!yv?*?MjQ!n~`o#uQo7TomMwJ`L%9K zDB^^fGlSY=)gS%S8F$Oy$Ocl( ze)99kB*izfVf#O11M5G?27LS4lBWMeR{ejF4fy{d8%+h?>Ae+mB6cpDd>l|!l!*J3 zqx2r@CG9RC()qcY3^uw<(I_nxW$VwTLv@&MKWZo{#xH1C00K*Msbv(LQDr1dixg#Y zOZ*#f|HyO)79Y|cZD>KnoOBUuNI73xFbsr$6RfCQqpqugKu%d>aC53%7Z55El@!lge)fo-mgaLm->FAjjq^3(?a0|C} zO*FV|sgiSFF|>7^HY@|ffG%YibhRprH8-03P~el57}G0XNeeVieX#}UT^&I07rP5= zKeA#5YulPH`E5)J+FWxREBh2=fsqpoILpb|sHVPK_+mWP7796?d+#~7EmQBwzqXV2SnV_35F&C1w>(tvcB%t| zu1Sd~cbpkrm%%T}{nOpE^f)#D7cEME(MFTN9{rm8oEKmp2e9%=pu&I>3Ks??j)8-z zqzfiK;GwkRq-I?f!tlfGmUI)KA{7GPT7FClbq|aaD8D7Hs^(D&^l!Srt_0;yCG|m z>AH}feVTp`hk61=^cTLG26JA0+mLCuIx{Oh_gdCrPP5kI1CTHMVz(I#)2XuunjA*D zZcc%$KJ7o|ZE`%#Lfmj?jHU_j^NJT)I>BkD4`*8qmh1SwiD+)|Ti`8wp;lh5HlP zoKxr3vKnjJ>9IF}?jtgHIZ&-3Z^(Hz_Kym0nbacnVUC;8BM*Ocek=Te%N#ZGn>1(5 zPd3<9dU&l8dp~rPDkU)$?4Ko;51Or&KOE)QL5rZ1B`7p|9s2LD zJr_}uTggSDAD3e?EoY@7<53qa8TMA3&}~5;>~G(;$(P`^Ij$+aU0Rjj(5i~vTMwC;6GK7GE|<*rV=j4iJ#2rUxBvnvX`E}Tu< ziYj-6I`*K=o6|CH=O-JeI>e&^KlucnC?6fu2sn=H)yu%(*6d^YC<-$aonoML+R z>k5iwNHHZ*t|m9EHv)l3vh+6C<1y&_=fkL=r^zDFmKi!*tY3jx7Wc4JTzf&$5z4Jm z;mk&0Vp-rRUbNOTdIZ=xnHm0WfD4d6q(A3pB#}ycKjcOiIU3sC|NcA`mmC#@%HrSs z0czA}YA9SwO1!Ef->e=Oz^$Uhx1xUzDJkx~MMJsjmxQ*ltjtTfoi!YDya%{jQb1vc z04pOoM&8Q}DU*_(Q2_y?o_(3;O zS4IC!YPsClj)pW+lILVt*kq#idA}>`qh7CV*Hll~7=e{_v&@FPp_(lQp{_Rgq07)ROY7mX z6D{;NHg4HiTbUi{=bFOZ*02pSuCG6(74fCZCLD5LW%UVYJzaDGz-(^$lXryVe zc)l7d%_<42*e;p;O#dF{A2aTUPKLjX1=hExMS^u6gQUG=3LPi|efUjBT(%7C^oPqHJSr-1%=82O0w} zEoLZPAL6ilgh+O(_3Rg{#y?MR;Z*opuEvb7u}z)>(sQ~~H*M`Sw=Eq3$pVHg_Gl_Q zJYHexXG~h`EHbx3B2Iz~1g5Epc7JbI#in+298W%XG~(sXf1qy0519WV&cFJ4R4uFc z-tsi-ml#R74IT%b(G5IKw&6bz|LbB@yRb(W!v@lZgcF71qJ2=w$h-#t|DsFc^<%?{ z#VDqAws)^(<*enT^#pY_7p!Upbzy-2P#_j6H2Rw#1Q)Ia5rYn=5phr)7PP1ZSNv$2 z-M!J`E@63MS)+g1x|ZeZ$2vP@pXYhFmc%0FqbAZ8KtZ)=Y?36*ZThxk3=G38?PBNH zhd0Et_E$#^8$@ETmUJy<-n&HFINa3mYtf%@o%Le`AhfM|(eQ?{U)4q-yN*jMLxZwj z-Et#>p}g->e>`e`?uICn}Tgy(M!+6llE*!h!S}LI}N_`lV53bwrrBOc~ z64LTTO~x+%Lkm5DA5nNF#}!DOK_7)r>X*`B>6u;#Zke#`ic(p$eQp2tO6H1WC`R&V znzkFq8Wr*=>6yuO8sG55FwSZymsW=O8OhCIEf>}@ljBE+U2c=8E?8erR;IbMh;^Y! zWR_}?c!b#t#q2&saH|r(hVk8>e3OzYYFxsTHs~dmnyT~c_3%l9v}anhnR|NIT0+`S z9&_S2dsIcl>UO<~CMGA$ns-j$FMmgIc-pcbh@}51!#(e7DC!0$$njP^*e*5k_n!P0 z$%LqT?z{2#@Q0@tGwCp}R6=!Q}uZ=xe1^Z-F`gEKCQoG74jSYX?|g*E>I`MJm*>~CGXU^-jIFALbQXXE}1iK ziQc=>%-z^tpA@Jl1wp|tg{6vMc@Xthz@JEM;I_Y5S+E@(5Nj#YSgj=CRtELgKi9OF{Dp;AqAcVT?4gFBj77~LIArjmEK=RcH6&tg( zf(Vffg|+dC7-sCcCI{yS(g(D2;2Fioqr)+@fg7asZ|j&cQjavggC`v-){@NG#vfFd(DF`4&EcqgVxZwhGJ7TbZ&EC(u1_xI!5)RS^kN6 z75JKujJR-kA_c0DJ`0jl;LM*pS#SH-XnTfh6+<-&)p@UYZEj-Jk9^nZ@W8N5J#@z@ zY{x0|Tm7Sk=68VCTXIysE!73355M|Tm=G~Hky1H4=)?TDt07mf6s#Zf1|>=-G-rw{ zbAR8%FXy;dBh@bI-vZ6{6X%! zPrQez=UMDQ_WS>I_B7?K(S*_S{`xb)6-OE{`hg&e2>idexFM%g* ziiqlIJa+T;ygQ3;WF(s~sMk!mu^YYH_g4!a(XBUk_S-eC&ppiOv8D6wIt~THZCgWO zSW%-qaW^Oov0QKN4qGF|r7v;pj*f_>M?2~F$3eA~9MDMp$>%{CM$zzeFyHj$Owip2rn-pv(03C*wI2101q z6+1$fGGD-P8^mc>Ej4UQ&O0vR3wy;QLBYy_+?z2N`<>57OA?$v(?9Lp?579b+u#-K z9~kJVVl#j2@&-p*4jj@lEalqw)-v5?Z=5eF890@Y=+iNeuNb)6z-TxB5VyoH(Xs0* zO%>*^iGV-1WYfua*b;=RqRMKA@|vixGs`L()w3`Krosj*B*YimoDHED2iQXLVjBJ= zZ}kX)m`J6n^z%hncN!p>f-`QE2{7g*_Qz`h;|jtSzA`!2B{5;&LtZUZykffm!JJ!6 znF5F0;y6;Ppe8$)gVracie$A9we@GV}G*(fIUTxF{w{w?EEk(*)W_WRO<(@WNLCqeynMV@JDG z(fvI?=}Yk%>U+KKiuR-dLnNA}{k%2#NiAQK@NBtB#Q8fHzpJO|V`t-VC+ox8{)zD5 zE1b%{@bh~A=UvzE0ioI@@)g1DK|fC~^2=>JV_vfBgWz^-BIB2xzw*3#&wJZQTHb%} z6p`qwvFuNN>`cBfCU@m6ZII{-y!gBz@Zk1rT5tYDA11!p|AC%Eb)_4U5<^sr)XSFw z9dR{_1by?h|MRR!Mn*a1LU?Cf@V|NI=Nhu%_+scaFXOwE9aX|}dvLVj#N>e#S>bZ> zW@zRP>H{;ERS5y*7d08wO3F)a>Z3A3*XetxDF2eep=Wb3cBix2y(WeEqBxNwRkxNF zmP~867I=%+>X5Ch09l_Edc9s(OWutTAR1W4!qMhKWx?Tc)IEhg>d8KuH$+fBg^cu2 z_jHhYQU_K*&YqkdnNQw=g5Fe>+h`2#Px>*MCqVQZ|mY1YH{U9Ssw4>rXJ zLm_~oa!E=fB%{%f4B}@3Pz%-GYBx@RU*NxTkSXNJA6(P5%Ew97hYi{Qb%kst^&f#(K+Y71W?)z?_b%S8=@Xe`w>;sd_MAHB$lXr`DDMiSHHVj;vZt_jX@+Ls|NaGL?%e;jv1e- z;_CPoRlh}3-Ah9rNn_q*ve}3D=j<`BvAxyhX0#S$F08d^-(R(BH-+@b!m+4&^H8K? z$-1!bjtg?u3!#f7WnsQuaj=Jb3ZdbhgE}_7!6QjWOhDltvn~yDMh9*{-fAW7BVwxs zymrzU;Kmm;X|xZC4T0EVMFIe|uaL&Bn+Ah7hCwA5vYyyv-_;r+L12|6beDmL)9}P< zQjMU=Dq8Yv>nKGcpQ9XQu);2f=06woh{9bWm^=<2>tV^tXtIN;)E4fJQ^Mjfc`l73 z=J^nkK~+h@+3975%RjjqHo+5|ynzLkJ#C~<3*Bq6O~JT(hm@GHgVK4hg{OLR(KJAy z`o-db_m62Bqu!e$q&8rM^*;A77ahZ@D7w4C3drIrdJK%S(_U^B?CyFT3{lx)swM=P z;_R97l3JuFp7qR&=(KWa5;*7`$+;gMg;rl=pz?KWxcu6}%G=IG*ra>g_3s3d6D(t8 zosF`qp{BVyRA^PERVRVb;b(hCTfC)J<1P0@zn+aYJAFLF<$L(&EcKc_RA_#fso7gT zHYP?sYH9jvCERf#&L5F@dc*KrM9t62FH$f`UKjl7C)1SYJA$9Xzt_$1OPDra|8{c-aS!wtZJXk6M zIljSB%{MRfLKR&B=XZblHe$Po6~!D9NUsrF8_ zo%b?2#cu{+zKhDxp6W6>L}qp8ll3f;d3QJI^6w@X{u)K3eRW;pdDfXHiXpu!9^Gl! z&1xZl0gs78eUktEo+~Jb=TjxYr_Fsf&Fgoy0)Y8ATuOT82i&|(0?7S}8hz<8dOaV< zZyV{|pvQw41V%kaZ-D=e=ACN_+oMUK=Qhi(qbP<%g6j}V5~EUABP7EW1g*6F7F`O5 zp*j#Ew{FO-f;Mw+=`@yXBXJx{5VsmmAQJ?6ST?>Y8*RaVMdo^(6Fpkv1&n(bUQ`$4 zUnWU3+PeWZZoKV#)@EB2^pW!6c*wn_IgW8+hg#GBJ>t6Ud`dLvCfojQK{l;KIDhTH z(AU0swyA<|pl6U7BgE;>*|A^0hW1M1*~B6-&m{8IRi^f%o1S&yLu%0e9DsToWsUHq z9wgl5!#kTb)qW}uS0I&ka(!J@J>PT`1w3pZ&Ul35rq|1Im(1GFzx_I?? zp#+qD=Nv{Ni-zU^ha@*s#326K`^ZXQEIxODt9Z%$%r${sPqW!5c?F?=}rFDc|NzMn{GwV&(6Qw{d65r-stAP3~cW5#7mCAtgZ4@Svx8XlFW)! zbdZmemeSL_xyy}>@ZNyDl!bVzu!nuhHLJSfCDY572YXti9hU6F6722kb$S1$YeW1s z-*ohAD`@Y$W0dSQyM(^3C;TY)r6$KI>EuUj|YD?auWqdW5k%V>fc+}lWq(Zlzz(o%uK6VMW+H!( zm*SQu{;|AfzB2<8>3v^Dqi&!w@+z|Y`@{Cozm5;EMR~imr90xr$Dc3HePhtaoUqPq z5>CSypUx1Sx@?-8lMi3!lTn+nYh?E|J6hFgz%6DVG)4`-=oiuoT$ji((fy3h{{l(0o zhNcjVM5m&eh_9t*h8#&5+Dih4_=lYY@_@WR@19w|o&}hKCOQez3L4O0^X0_Q6N=klww3_iYIhLqO+U!V;JSKVMQ zEii8Oq0vVgvmQKLjCB^zt)%>M#M6&=d@J{;B%fLFUZr3N?1McX;W+fwdvI{?xR3Z~ zBhDb9^_$p90xVX%Lx5yMWQSm$O$6EcpWqo@If~H1uT zjWuD5hQd3&H z)i-!kwq(2_lJzq)m*BVPKnagnP|oV#icu2riI>eQlPpEbZ~u zCok~jcd$sc%hY>O+jW%|6+8U?M8&_GvCx=RHwJ zru0Gw57<&^`W>%%j4@Z}a{W4UjBMyNSi}+o;73x)3TpKG8t1>@97{!pkEc|yUtwD%#c$M`1+RhvaTL~JG8oiG_`xoT&t}TYTwrv7>^FO;l zhNcjOrry7sAIUYjQ~{fNgubL3%#%!7j{T*+FJR#rtS;+bccG8IF*=|_Q&>V%*`VYz zcz!@ZJ>;*Chj`piFuqeS$zPzna-uUQYROF`ddUB_9~7oz%DQ^~rP8`BI@86eRe*`j z5&;I8_fpukeYD&JyK`FINqjY=9)Vu(MqoL;RPw87t~vj%6QOgKMjD zW~)*rpY;MJ+ZL4WH?#vcon$U;f^^Wc((Y{p>80dr@w~uvKDHe;&}`#eqS!~u{o7R6I@L7=H#O;jJ)(2*pNacg;7;@|;o^IP znYAiorK6R&X+~bclDQo!XTSM1p3j_j_^)-1!d`i#xq%4lpk*4+rl_{G`7ldqUS;kQrS9_?QrK2L}f-(Uw@Xa<@-A<9gq_um{$mn_W2>odG!an7T zXx!RBLhd#*chPc0UQZRoOBb6_&zne$Z`k-4XQ0)o%ME*9=eglNhlTbtUG!PJTk&YN zTb{pquG4y0*=|Sn8ia;`l3_@b=T{+}SM=@gzY=|J62+$4O5e7(abmWoD3U>1j>2V} zUD!u}U9?VZ@NJhvz`52QUqfIsR=iT1>{%+fEE_;+W;digxwok_4 zn{(i{*sEZrO^?=anBbkN`+Rz@*U}TJbW4pjfbkRJix}N!Aab_qMcVblVy!>5jT?c++*(PitP@#^k&Z8m=*ne#GU#u ztDI{%bKBC&hDiloC>CqyHjY(y=%*Nfv$3@q0W^y>_nc|PktHz3%;+65gl;nP5p+QS zgjrNS;O-GrWw}^O?WQHI5gf*H9h&?Jw2!k!fSA5_3daldyxvXX^eyfk(DOpA7jSic zS0(~>4 zll`3RmZ$jQKLbkkc7RI1zR)|^PzD*8;~unw*lvJ%Bc&eIl35ZuN)^T=@662k{@wH4 zxyc!1(-jsn@gJFgVPZgvS7NKC0!fZvK%$sAaZ=Afh-@#nT;;SW7@Tt%%2k}`HJo4B zRnBwXRmTVG$2gII__?IdzT@oxU_Vkdg8`nHpN{0UvBn!eaWJFu!pFZfzcFT5;8rGo z{Mq6y|BR1BebFm>cEG}=1>t(9gc#cW)un9MU4rB%H8mCq8PP zh?qDNRby&}PT3N^l_8TSjx+v@SOJ=KPSpk>x;|%owdz#eFf4qgTSr7Gt}d0_Tu1GC z^; zAWronKRjkZLu7Y_2Ditj518C2d$th64Ba#<&}?;jRw)cZx_Dq9W}vuu!1=qut;`zwJBM`qTJC{vD z-JTvuhDk(aZKPRYL=>&7+oc97mpA&(QBOnRe^GT@WDZli&8{tELl09@J67#u)-^oT zrd!JNd(YSy#&yQbN(*^Sg;(jSz0h5Ye594j4<(BTZax?PaIitop32ho9cP zd1+0=tgCZq4&SVve3tXhkd(9ZIPLspp46f)p>RdAxlh_nPUQ}yQ>_n`a=08U<6h^Z z5A>&0k9Xw!u(VQ7-k&E>M5_3`vrT>8`C7S;jhlC18-6a@tnzKn)EDr(2HqDiVZ}Xj zQ1mV9_E~TCrRk_pRL#Tw$h+U?;bwd?PQGMX!H0u@7ntP0U$z74WH_BWHt3cy7KV42 zSB95;71whw8Y}gD>9dvJkB6+hjA)X3588YiI)mfYEmVtf>{EF6ea@pF&R$d5pogY` z0P`kBT9%^iqW6;ZOkN3Z;_0+Uqnj(k-X$cLMYXaSdBoZ(;}PHUGoXRbvy%_!{a9T$ zKg;=RLHp@W`{~bk?~Io|Q2y7|gRBEtQyfWJJrE5sd2;@b3-q&;!_oFj7 z^#NzIm7YtFhOMv2?jEA#YaS)-h>_n2Yj2|WpVw&rwLQMw65oZza$R$hIet4rNLJIt z7yqmX4lJe zD^Q4k2F-3GW1j6qi{q@U>ISITUMGn+=5=3PWIX|%Msm9YP-rHZsYhvd5LkW~iN><- zAS5P@F(J4J>s{rYoM0H;Bgi6OJ~h2L!#>I9`>aqJ(1nRYP{_`t!}L7S$2pK2RD5*@aH-Hf;&Hu3JN>5bSHlUlrg|FAu`)FWx zrIRJB$Wdy3cD!=Mc$Ygo_s8iH>iz8x|0t(9^SEsDIJ28tWkCWNUk3tS-FGCw8B#w2>on;=Au5$29J>;{Za$^YPXLW_Be1oynH zcjQU^*s-FQ@<#hfeF^Q@H#oGi@dRa1CW29bY7wAAMsJEtgr+~26*+`er$+SCn)e^~ zxSbAxt4&gS5NpMnRAXM?`};K+-|xIXKGmHq3hamtq}>aG;5w~Wl}@7Uha19zFn<}^ zE?T@^UF)T*34fjS3=1anskEJXE^@%8AN%2#yeU9MRG!KchoEHHt^s5X9ed5GEc3FW zS3%F$crhOuUvd)6fM4~U|FCYd;(EK^J)ri0h8ylQ&*O~5{y=PUfm-op$TquH9g(Oo zcK$KH#H+?6iGlSWMfYK~3N0Tb)V*j4Tp*$BbY_3!=AxF6Yd*JK&uWqzi1>u4fXE~7 zg1KM*E-wOM{=vYHFNPgrfBQYg7NJj^@GnPKwPN}a+kb9NT%`wJcZZHTxqWF9N*u2f z$n2#N_9c9NsMl>=hd0u*d)7XW`~u$WsiRV7^G^#R;VWGAx)~YD{XpxrV_Lz*XteVj zE`Q{aw`=;-lxRkCCzKeqGv`;xojE0$;k@#hN<(2x*wsP)syLkH` z<&1N?+;HcJo*h*>er6Nvx@GP%-ciN+C6Lv6xD=7rpZE&fz9+@GMdUI6W#anNd(oh<{+r7ia`5 z3Ar04B{|1CgVf$Dk`ru-(9cw#8RvID$J{*NXe6!K^S*S- zSVpRHC@tT-^ceB#AN+x7+Vu_O)Y1(4 zYIi+4KQ?f2Bu!}qxr`Gx%EV(9GZn$!>w8txBd>FA_`Rx`)KQYtATO*A;7x?nv#!7} zF6e&vSe@1@Mf48TY40jXt=idHe&#bB0%~Mxck;X28oQSGe1cut`fGYS&^P@XzU)3; zO8|IKXA$guu$QkvlCJh(bn@>ENID6g+IPSA~ z{!J~8rO54Q@6pg!b~J^YRNY-n9V)_6rJC)i);ja4-u-c~aEBzwyH&um1@p6s`FIgs zxg2sE=WZC#q`nq`WoUq z1V+KMCht+jW3=vbYu}!FqCfy^@^r5_8ID${uO~FCmi}N2HY5EEBK9L>`vun9-?->y zx&E_5PEc2fv|Iz+Sb^H6HtuLt|BmPBX5p^~Ne*73n0>#8#+mYHUoM6_t5CBq_H#Z9 z57O<2NCU;DDuWRY)!uULV)<}DD>2{euLom}QH$WpDw2PZ*Ou6ViApJi2Ql5w0k1{ySr;igM=svd~c6OZJ^Cdg>UUNS=`zsX7W{|=c8=4eB-T@Qfb3B zj+oCB9#K|kx6o(sb9#g|`c1vJ&z~MH0Rf4%ck&p<{racNSF5XbeJ-;sXm|Y=aIe)F z+Jk>Sj+gwW`^c2;OrF8xI5Is3w_r(V={|k?V3f!jPZ6zeH*q}Nf#c9{gx7C3V!3Zm zciG0P@cVpUrPC(+ATStTr5`l2{f(c0psQLZad@xXeS8S-9rw515=zx0@A7x~EYyki zclmyH>F?L}=Jh_F@mU-Sav{IM{?9JhlKak5$o*6h3IZ*P__8GZ6|R$9s2BkYzXT^P zBHB-DM9?D2+mNH_ZvMjqj1f`YOJbMz8XtfL4CLL*VZHcP1E}24wN}}bDrq$5Im$Z8 zU_F{zA%KiQ<+V*R;BHkyzs1mN#V=%)kt&CiS`#X6U0iRGEhBS(_teAkl)d5UJFD9O z9!*eVZ46@F9*pdzwcT&2>g_S;i4RCqe`5t4zjd@md8HTu!t#0&L|-|BHFR3w>*tVc z4xWf=d|l9}dJW)ud4D)}C(Noz(9>2q=_<&ac)@Uq}9N@E;sa{PA53cTiRM`EvB5Su# z(DKN85;X?&VJM0I2?T4dzc%*NNzhDJ(OOY5@rJUx0NZ55Z)P+n8B@?p13i*m=;J|O z;0r}qe$#QcqTM<_;-2fs6IrgD3*r$?-5R>NYfKIzGeNr|9lX5kq6qK-pb}$7Ej{Uh>__6Lz49KyMA+%X4PT&GJ#`Z%EYRq9Wm}KIQ4y`sTX7#V@7i5R! zL}sS#(CM`54d`=5p&B?-k5+PL8EvY}SQp$4J3n}KMzkD^4Z!~_Ea-H*@TC**3ueZj zgMnKyuHIkE)=J@lr2n3Q=nj48|U({S|%lBK)Gd6#v48iHM)%pI_c}QJvi>(fOU$4L2 zJOUL2M7s0ytc5-~@XmH6!tO`(YlUeLWzm|PH;TE8GkHdrK8il?y?e&0^=a|`Bm!QX zu{B^O=tR7uqi6XzM8IGJzui%+fz>w`|6<@8Eq*g%@P_hdyxn!!ltUZW6He0|bP8`- zm+%1sjlP0s-~_CSK)w@b3Ut-R7*F?mu$x@|ZD2U`yN^%u(dQ>$DeWVw(;Oew=TkcD z7$^t~(aMZhZYQYMDEPqUULHYs*1NQ=!zDShJ4egGEJ?e@hK}l0eZdX{<^~_szM8;7r@d^k^OUttk z@70yqws?~0*k$a4h_A^ae%Q8Sz+ETMP~{>$`&rFKnPN&oAV7-K}yeO=q$Y{$qe5r(5VSj>R`GupuHop z(d6u?w75-!&o9Hl_Zdl?#O<9_z{p-@KEl_7d$KHl=g)biaA&%R|N8~K$$6cM&?h~z zos_jVa_nqpw~@RN!pXmA3UfLD#ItrOknvoKIcDdpevcjoRsgnCP`tIm1FKGnLxa{NjuoqbJk;WwVt2o-3S_8+ZtWpNf(7!lulAUJDI!Lssg-=hyGi& zn_Bg{T{;2ymaf&yj!rHC^aqi`yIjb<{A0f}pUTN91i57`4)?2{rr?hcd2i*e;(qJx z>A=jmu9L`sUcs;YLfGhQeb1-aSR!{om@yuMS17+x+-G`G+gD1Yw7*EvTX|#lbgwHW zM(AWGIZJf_C(mampuah}?tRK~A=62GC5b*7`OP~=0VVzvd8G6T{C3jqZ!#UA?Y1r< zFTtZ2`5pQH9Tt?drxj^4jZFz3T8gLc^f?oJAN=wvte5o1r@LI=tJlJt$ydI3<}Z~T z(EdqK?O@dQd9ki{3~2Wj6TT*gk9n^|vxeGwBI79!Gh|^TojdagL8oX&=Iu22st0PbCOmGe#e}V_Guh&NgdR4cOG-y_G zVHSaXlmK!Nauk~#XC7Cq13PIJSfW`fVmM;58(rt!~Df4qHO zKQ2e&&Fk``ZMstRlFr{c?g3}bc4x$h;GcB<-|?m+T!+S^HJ5&WT=;Fxyi}Z}-zI0x zj<=>OLn}#5>%JS?yKeYUO?_WthL^4Ts6tK#4H80ap) ze*Idz6%#5YF5Gk+J@VM;>X1tHZeV0$(){)`kBBj*m0AuW&!II}FGVN_TQKo7Sm_&! zdtyuF(gz23cX*8gpHn@p=R;lh`$Z$UyGVHNDpg&W`CLtDQLCw+PO_aTi>^6dyW_bmGtO{>AYRW+zW;g^~(*P9P`Ov+C{%USL-ABQssSh3opqG;poD z#q^5d6&VHhZnx6I4^rymLqlucHpoTjDx&}0G~(YhvZAY>%n#a&!`fbRnaO#{ww`#L z*0`MFFrgCwC+6{F_;xa!xqs5P`UtHAdVeBWggUcB+gfE6qxnecIZpT;{L7_#*$n9I z^5uZxyp>wX0^3wS_zu8H{H}j@!69O7U08Cd8at{FvVM6WI3a&TjsO$L=$a@MxE68r zj$MtJB2>C_io3C@m#YL)g8Ri_)$&&e3A*vTDYZe}Nw0+rLGLd)ni||h9D(L^rlIj? z4KkqLs%uyh_axAp3&E0cj1B4@%jT=(%Qz1)>T@mQ326a&PqAr(bfF*3lAiA;zVD`z zznyb8ZEZ~GEbSyRvJ~{=s5H0xlk0Bu?UDaW?DyTpD)$4uE8;@CE>po&^1D+FfRxIc zd<&W539Oah={|O{m0qYT<4gw!Dc5Q+omGY7cck_*$=^;vKN~t7ALDGkaTgCJMp|m9 zl8-ZL-(mG$;9I$am&me&EvxLFa+m02?*1%SD6EKiBw!M7ldIs7FJ61`7zFqdHL-!s zHrSGH>&pX79eaENwvToaTfl;973MbNGZe7@6G*#npsoEM2W9%=fIr!jDnf0 zrF-4!2-y~^lz3samePy}Fm{0vFA7)Yi_=-Xft$~8$U*Zv<51U9={mRWryi7sy)x$Bcf%-Ml@EH4jr9G&PNo%8mTi^bg7n+_c}J`c8+$A$$N^Z$r^`CDW7 zA*o7=5as8Xj7xXcbB&R{BBQ;8na|6ELZxCy4i%$S&{WX8VVhnvp1`xIQ)Iwd)VeGP z);3vprvuz53A`@5_;m03pKJHU7B$^eq{LBcKmn%A?W#2@_kymeQq#9b>Yool#|b4) zKX@@*%_}LKH{<@96t!`(OnF?Dl;$$Y>vy+Om&~N78C4?C{Oz8obNoKVH(#&w$`;_c zYo{a-M82tnsryqxW-etPIs8(pGuz{bxZZiYAua!4(z+wnUeYe1@IX?q2F0|LCHm)V zb14&bVz8F*g^XRsAI%ah^pJytoHRuVZ z%ubHGe`CVL5c8CP1S@7Nj<31p-q#IoX*528I&@w&O51hVoD4qK%^Bi?Ogu4=^wbei zcvJeZP>yRX<_+5XKb37Y7$ffzB?j~uj}he?jtMPpk_v~DzPVhv?(DzWXCQCq`E&3b za{h0F;z!L_??YmlAppB)Ks%x9{XqtHp@4LP6T-oJ>=g6IVN0u*c?q}I-+vLOD*!9p zZXg-RIq$!Dr*g$k?uX`ON!B+t@M^(3Er+M5)UjWAiM>XHtheSJ%cQyPx^4T+*%s z+$Rsmw1*m*g;n2;Q&QsHl6UZ;UzcETR`P)(DQ&xbM zHkSjjtSZzcJ8MH2ZV!X&6c3EEcLB=0lc|2KYXxbQ+} zRaX^MY2u%C9x*7#t7bR39oJs+`pJF>BCeU$C`)M6QKn zQij5Y1^C`JJfQaQi?N{@>`7>7>}lkKXx4qOx&0*8c+XUPOrQmYH($(%{>zU1-3xq` zkm~6p)fLUIl(MmjTj1-`Y1hVvF_SmKYzLQ}#J?KovZ&gYEPoA2-zDC|nN*~}-rAZh z4E1#P$r2(sI|0-nrkBBs~W||j0i=iLMGrnyhhOs=AdgZLx=+Ocn zPl_b}3w zuULEmJb;~~%b>~2$obr&Zw^XihG2Ca$)13AIk%ZJJmMU*uQx$=wE`UA>VXIZ$lgDF z=H?HJ_1B#R&Vsv{MqHzQ;?T~u8{ek3ID`X+_v$v|J9015?@C2LJ9r-;^VNbjql5$U zyco%TzXcNw0fBCt4EcLSctcXRfzeag_?syg3W0#l?`brL@kfQ;oFA$tSYO*vjUZdfC;2o0Cd+{M>|2UT3 z7Y8cxk34j@vmC3WsgnPV%8%9lyZTJ)l=5N8u-=;NW|BGg#}YtJQ*qP97+}^*@^{A5 z#M~F!wU+*DP=w@vMuIVYzTAg1l_lHXszX`d$N~4Dm5YJ4$)DPC4W-$D)aK9qEp1#0 z+P=lS0B%&-UM3SCgZ`askQgMckWt!zTZuzsD3y{tm0DFl5jO@s#8E zdb-S8DH2S}rvBn~hfJ?r&+M%F{!bd~sL{`0bXC#(E@_WefE~!G$~X65-O(ca))}u# z&{4q<+}t6om(dZ5KafqftjeL3X?BSM37I@Mn=&8#_1Fn0iUQL;oAL?l|*Y z(1X*LwQy%nV*l1Om{Gp?A)_H|TIVt^&mVjDIDeseFi8@>K22nGozVp4s#`8eO)UIdDXD=oVZC(!F~U5q+&p*Y5O2cE4qz$AZsbn zJG%+Cxgc3*-F&61(4+S{|JBwDlulMv{$aUSC#QnR{`tJ{{=IWxuWF3xn&cMXwZos}|VJFr^RoN`6oJ<>3QUb`P}n{1oS~q-Oj1Fb^%^gq z+j`39gx#FmQs9ygn@%N`QCLY7c0BLJ>mKxO;&PE9d!{Lz`_U^~zd{UdJ2WAkyJ8%| z{`AY1^guPkTDkZ5fkYd~qMVR&7%-=BCg%yr0I|j*SZ;a<(>EdY4DCh~?LbQhNx1=7 zLySN11+Uxy*BC@V`LH-gFB6RqIjL*EuGywGG0?TkAIsvgdY4`)yiTL}&crY2@xn;y zL<$G-v8QkRv99*N7vAR6u|)d!lq;!W?=JPo{JSt+1?=oq@}Jl5J0>41)~V_g(czxA zjwC@1Lplf$qoCimV!;uYn957clfA8~dcO)}+BwznRbVYn&(JR!;j6#Hfas0+S(d?h z1gSz?NAI=d)!2Jn^Y>enK{JU&>}1&MDsNA1!Fp9k0ZBhpv&#HO=@}i|62-rQR*1jO z`s=_JE+_`e36+h1%1{oLUj>X_2<7CGgKy3`WBkv(PvvfiEh_Fs%E?H@C@2C3ZnEC>7<`Ntu2^MXMX0;#6bQY-iKfdPD&q$jU} z?z+@|c$XY!koH`YgnH(Q{6RDP?}r%qL!li|jq*|7b?8c?IgVe%%%-W97E3Gx42T69 z9?@K#pPO_RM^L7I$P0Wgn^$8M_06AtM(k{6$9(Ac3wQ8aXv~{1F8)`+WZUts-C`;f z)NE?cyxKy&L#e*$j2#r~%uJ-QqAsf{5OZRVt5WmfDw5q-7cv&y3iF0bZEYTSQ>>vm zPWI>h_9zyUBXJW7)@)b*&a*N-5va?CmkIpdbHN8g@Yo&w50ce-7SWMm#O7 z*!Cbj*0bgVEE(jBc0P)9S~XCeSy6?e(g@xIN)Fr&df#9{qP49K3zQ_ z%@k?tezxquBDepVb3LQbwDdw;*gVvByc7BgkXHXvma}lzTf5>3*KivnM1xHwX%CT9 zzG`q^4+1>hIj(YfTaS6&>WFt4miDyihO)>e3x9s`c_5r&E!R(QzX?AQ z)hJzQj*$u;o@=<}t}6jv_*K5`SOv_}!Q_SVF0EK^TA4gEKd*vaahbrTi-}R&y!B9_ zEG$UjrXPZ0n1_@Fb$;EdLL6th&x(&r*UagBu-4l$Sh$=ge^}*IEMJ?MH6U85bvsjp zzxwDQkNn9(XFPE)l=wJCHU3zu|CT@}g*+zE?TanzmrS}zhf_2s2Gc$m?&qTYLVfm7 zyWDFNBP5&kY%ihcs_+VVvjPpH5$dwQ>v6<|++J&5A@2`g=&iOi&e@$jxb#=;J9&Of z;l9B8#4;qHB#|%lfT2ej$QHv=#C7{S{&A4C9P$xT`i0)q_m)=j(HW2axmizBX3pH0 z897V4pB^oHOs;C!?=6c$8NS9h-&>fRtLasV16vj;MrR=Xg)+wnSZ(~y08G_atDPYE z3Sy-rAa#(Jhu_f@lV(lP(;Js^A!M0!)ZRlRPsU|)N&+dgzHMH8k8aPJ?DapAdZ3BvPutrRI9*>Qjk;(P zTte*s&*!#{>U3k((RNqq_}YPgv;}35Iq=QGV%QLL|J_6AYHKsyY6h?J-{;lHHzbD; zy?!glzL>uYV2r4FQj!?v#o70T(e&HG%09Vnsu*g1l}XE=5ct~3KfuIyNAlS8;-L(Y zY;8IB^Kap59`QrROY7K^)imt3B$^`pj-aIUVtQtqbjoJj0kW~pMy(ygLekG23#L8suj+^ zz#~wdr$R@wv#)lc7ufGyg3*#LGhOp{X(pi$ozA-{YF;w<-ogRqcVSb1cP=EOXbXZj zx$xKQSKzD*?5GEYVDo0&4OC*YV+dZsRhk{2V^d$}WR`JHMV#nC`TJZm9iHre?<_KV z%^sClcqq#pMf#%@+QD$9ndlcuxR*V9uA(+*H6|tE&h2^PzP$nJixY|-8#ZIey$lgxh01n``g_05@&CwrLuJ@anT|F-$+AO_VnrwRi2`LQVbeMbN1(YJx9 zzp5DU%`y09CVvl}$^c#u)z69Sh(qQbBJ=)JZgh@9WlWmF^?w)G$(nXdN{> z`L6C}O#W{EEoh&Z=x9M4GGOJA1)Y#XpO|wI1Uyb@B4}{2J%V39#Paza5n@dUg8#VS z@?T#LG-F?vhJDp^pvYeQlz+xNCMD_4r%vCIOy8j?!azHV{QRyrzxJ8>qIJX7EEk6Tq2t2ige z48Qe|x4OJnJ$T9KawOoQ6pB-EUgp7sf65L|eHYi}K5vC-oE;F1dEDG~*gGeCz@cR| z7v+^iTo)L$dcR2ZGLGN&nTS|lZrx~WLZhTaAyOe)P!}PE8EBUL$oSiIHQL!@wnpUR zFLK(6iV3?DSr0PBPofsCS}7pqkn%Hx&l6+4uT=4Rd2IRKk=h zPJ_gGiS;9{t}M?soWlCH#^niGcjoWKeHNbRudhUm1`009{wX2Fl2;af)Oky1V7pjX z2&}(vZQNx_ejuo;+6Z``)JhN_=-&F=*UPL9gf>cp-W;4DT`-)T3roW!C4b%^-cswl zZtWWR*N2z7%GI%%dqVSxLng6TqVvSHxu|j1FV3khs4fG?B{c;Y>J?e|=7YoEElu^2 zo_@XGE>l5dcS8!$=+Xd>Ry_xx9eeBKQs`Jtb;k$7Cseay6aeyr1M*ohhsP)aub`M) z*p*Ty`29qr(r*uckVBU;cs!v1RX!Pf{qR)5aL(*NzPJ#7v*TI?U9JD_B6*-&wi{sXo zxhSZ}+84O$gbfQ^DosHHK{V-6@sX2KbrgTWHLAk@Im<+^X|2zpl{>-~Anf z{rmqVBV}hVey3oPd!t>MM$jHqSe+o9;Timc&<(|O5mV=`HvxX|B3+^5d>SMxt(f2P z4L&gi?Z1<*w{f!4L6YYX(uxz8QZ?6Vny2p&vGRi2;CEdCm@9%&A>hlh{ zWyOQe3lF6m5h6H-Q`&*TgG#hPA3P|341T!wVXK2C!G*%}W-)g@?=Cxmf5>-+Q1ms3 zy<(|aU0SJ}bMAc}2JABN3uUW}ta0VCpKd#)gq0-69ZAM|I-5JFc~`7(xk^~-=?#no z-LryF=LOs!FkYOpq4cGAZ7=(J;%ovx-ukn;{zE4y(d!{upSj=K?)O__p|4(sU&?2( zvnSVzewXh3K3K+g!t2|f9>EZMU(ySlBdAhY67+g^Li)m6IH@U1zzL*$EQl{2?8M(y zH~{L0;02ySlKO8WOdYi@bnb>tS8#rDRKEFq>1wg$|YE-V4?Lqx5)_6FO-IDX%vjXoHy z5SymH-dn#_;+}SWuH^xG5ATZogctQG6I*?Cn;p&PY4TsJ=%*!y*K&KaI*%~ii0q14 zN{Z;Txs_y?C+Pa;TE$!%0J{45pSN)^Jy>&swA^KA^j{a4?pN@`d5|bkSJ?w4WP|-b-g($Q~3=W%G`Y8yVe}|F+iK>ByB+Rc?fExb_5ICLwd6VYxOlB#XE=!9Un6|n#bYAI9Ea=F#d7g@c*KD%)I@C|q9Pd!SWOv$lrpf1+d$~b9^z#5^8IKva^=zE(__XnN z_1zOncDk+i;pWUJ%4=B6^cRpYj1Dr^!v-1?HTrn0yahi7TTgvsnLwXTFyS=x@6t1{-9uc}W z(YSK4ZpOofdA4}W$yL+N&64NKYc=_|vg#3DO1|W4x%%je?lZ;BsmoZKxCwTXiV)Xf ze|NDDr*Ex22q^S_%XY&(UTU9s|J(56JS1+9ym}$n2{>>77n)rwHnN6>pC?k(a8_uX z5yH$QZB8zDe;MFky2Kc=QKYM9E-foFFIxq=sIEYt$ZhLuUK!;&PVu~wRlo?|+6h6f z#%YNUiyySXt8KEGp;?KSg@_XL&MA7@6H5R@oV~nE!tZHx3AOm6r<-5|*P~Z%$SB?> z*W|ICy+-eEV8kjaWA_DH42{nt&7-hwhLvcoYcFGF;9?d0s&eq|3=y^DP{u zpPjc(Mv8ZIkzUGF=%}9SJ1=ufO%O4rL}U29#RNOY5DlVu)t#SP zllH$_cd_CxMl}5(B43)2_ym(IT2H zCA2r)SB`~HQ~;u!>PtfWfcNJj@ZK;nl`A)`=UumA62Qkt=^|29&D!Y^r(j7OJaAiR z4Tr@_$T@oFhv=nfUUhW;I~&j#@l7LL&^U@cJb_N;&MEq`Uam%^GmeZ!pKhzt5!XG0 z4oWs3_7mz`JQD-LS96ZyT{o-pXz}FY5JGI3lf~G=73LqIK%}>iF}cR8{-fo@jD`&f z%#YDSks1aI9+OxHkNyfeI0Iy;F=DSG_4jS|L8p6U(dyXc&*#vqZRQQMmZzavP~)o z`4IJc>c6THI9djhdm)VGY}@Iw`VVTOpC^*^VOnXrevquL7*jPHR%~wxd?)q$yow-B zdA~OZ;8A>O+TJJJPa4VAKl+DY$Zq{%qcC}O;EEJYl;}SxHzuokmwsj|O#y%KzNCWj z;r+_=vb*8UJMR9ry{b>oSHaqRZd zo9R8?H37Jssr-YC8uviP3Yx-x{D@~(?wR|`DrDePI)Vb(g*GN`zHLkSx$p5lRddGG zBS$Xd3gtu@$gWRfVYrJ=Q!P+m=bIUdu}|mA&My7nQ<}A^&rtk6imwUhK#6&}+oW~& z*6xhJDH2-rvxoh3S5@@Y4aB5MBG(6#J>XZNXQp6VKleP~I*K6s$dmANvG8jGjE10W z*<8{B7}tn9S>sYkz3h{SzZ-uK#{cMdG_%7z3uaD2B zFECzR!8Cos< z>ZFU}I#{AVYq|XHi5m$1b|e9R2wO$OcYIR=fAtmhzlqk+%Ps}b!)%@ier|kI_v#Ak z^tv6Q?hOe+T`mUU8a&0-vXXxy3d*W4_HFPBQ`j$uGDQ%y4xFwRc&yPYsqR#&;Y6|~ zlT&K)k%f2u(=Y^Bt!_%a6Q^kza^MGiQ2nTEC)Y>q09wr#X|f7BTy@RiGUQE~A8 zll!xNIvFwYa60;|m|xAVEnap`Qxa(swpQvY1LLbcU+^C5BOMi6%MIE(5EpIt1dl|dk3M%T@2^7wd99o(n@tQVSjO%p{%iPeh zObdCSjQ5?5v19|#c11KxYvi~0u-9Y$@T*2@UO^2wS$tz&qmD<)TOtPG&Tfzb~-N2NbK$zJv?Z`Rw$^o|(C3{UgC7HtL<4seDWKhsar66^icX=X!o zx$gVss`t(GBPw1&I{{q}@dyXrYS9PeKEZ3tD^qTsWqRu|4&=cGUMECF(37AGy}y{B zva;Ma8ylQ`s)B;6#6#Pf(>`2}TA`sM%gI}5Lp!8E6=phE93p&cYshBQ{f}?w<@22} z8m%YWIg1nc;mzoDPSctCA=cTL(?G4%*J(LLK*-@NqF-K`0utul_CyUj|WM%oN(n)nl|vn@KW4G;d?JY7>TznM6LzpNC3b+588 zE`{f=l@uRTb7$uiZTl`E340Tm&mtGy6qDT#e7UNC*!GJLniL^Z^g9(mvnruYNyetE z-eo9j6xJzxALeRR5Amg52YlM-+iJ#3R_NB~mxwtW(*C;&WV{))YmXs?4fv4;`Yp4c z4^AHcxqOyB1Wg%*Hs2G#w(s6K&Af0bMi=tQNmcE*7}$L|$hKrFL22EAZF+^`+}@x< zN7m7vVrRiwxj(4RRHGrhY}eljQB@=%1JJtOwoqYIrFegv%CVr^YcJlpgcmo4>g~-e z3`hJzP~3t3W6Ay|XYdk1&w83FOc9PcyoPH$cb@5MyXSSmTm-pTy!p|W@kp?wCO8p_ zEuYZo*)x1`a|aqI|Kp-aK78Wk->SLu9Bc3@Lq93_?K8(H z>g4-4Ky5W<=#bt^9&z^zQU&^i zcBK1rb2sf@VCerM(Dx1ghkF2!Em?XW(e3$^dh>5-IPF8;N0f=myzURfr;B+2>A8k* z%siXOs)5JAlijDu>MTz;D9=fKHb0g~Uf&(3)wF>;?I zGWmkL@`YDLc`mPrBY}4xZ|Z5>bU=*Q&VY2NOxq>F1j2}tmr_+UR(fbaTmHC>9EgrE z;cas&?3aR@5$duQ;j&Gs@0y_&*cc`(oeeXozP#T)#iQWDjS149SXU{xdS(Hu?v#(4 z&^C1nRnyw*q!dY~C@vz4?^aFqeDZ;?1iNtS8!DAU|B6lynp%70S?)&!YN`Ezc{ReD zu}jx)Q(f2D?rx4-9PfYW?H?D6e^#_3z%ps1D9e|{61`a%xo}xL;nsf~>5Wc(t2&?D z4SOWF5qvH4wx-GgM|z&^UGvjwQ4*Q;L3Mm`X-}W&8Mzg3!KBP#Qc*X2Ia|xYc-iEg z!*;x0)hD&ki~K^an}46PPCu88WY76nS-HU=8zi1kuGWk3f{Y-Z4>3uiSLoV?%51R; zJx9koH}rdv1BUy%`moZRqjC$?AsM~3i620S0FxUfypQngo*i%WzTKU0%etdX?ya6T z%H&{~3MR;DHhSj|@*LaNblvP*77T5vF1+4GVPS+kw(l4Vp8^FI$pT9Oyo)DoV9|G_m^L zimQwt4lCw}auH|L1HKvn0zL8Ie!GcUhilEtoq)&x$|P>_rQ87#1-z! zVp)?l=Ei(qFb$FPb*?PQxQln0NbH%PN7nX0+-+(c4lTIo!uWba806=L3)5h~ZMNOB zs(SFGJ2TxMuG4Da&m*bLkLsU+JyfG;I!6sg4vN#KLlKG+g`wv7bT-zPbXnAI^jJR3 z_e0p*?i2WA?h%Mqac8vs){BUEPD=vE9;1Ava+9C|6NbQd2I%0d4$H@Lq(hkQ`w_?= z@C|=MaB4HKK&o=}Hs=JXT}R*-dgxLa!#Lc@7t?dE-HUPOs4Qr+g=S|*nh@&Ta`}gv zX}@y;pYw=kz}qn$k@}y4GZ5d#UYO=GBU(1)n!-2auyn)pyfzq!VWzYV?&0|kIFz&M z;IF*aq~yuPbyIaGFj=&UHT(3)*#KI%fH}Wi?id+%I9${e`*%E+$4VWoGwDGNJ3^u1 zOPbk0H@NMmdq}!p6nalq5+*iQc```nYpbPLyd6RhT_QT;U`qq*9rB|%$Oh4`D?F|k zntbzL0PF8DCmLzmt_*)v&7FIc4+Qz>NbBEIJz@X8Mj3ki)PX~N>g@effOBG+j!*s0uN< z*nM#It5E2o&G05C=L#=y2ND=vFoS`JNtM~9+fT2D3stNcAvBn@8KZf# z4_=AdBh+TrCwt2nbw+%7nEpefM8?+CE%%LPql)us!$0MUtf6HeNd_Cs1`VYZX1;v2 zZF!?oHma>P?A7%c==s>6gi(6BWhr~iS-N*T^jRxw0rvCMlKH8Ii-zcrw7QoHcV$j$ z|3!F>tcs7V3iePm@FeX+uzV{Me2Ewuu!C2l)}5{yLu=>eK1bw zP#|&(@CXt1&sV~?N7`;X#C!Y#tz0l9!ZgivLcOPF_fHdu+vTK)^ka!3(U>L(aLs+3 z<86hwPNnnFsg>;l^2lmPzJXsc?n33zsCHB&MruUB*L$uZ{D(@i1~`&n{q!2t8V(Og zBXV@SD_KG%#BswXStlRp)-`7BIR1TXYx^o7LDe*Vc{Fr+8lG`i662+ct0AtRXp5CS zkpM&4&PM6`=Ruy)g-=u?{amN-Ww401crPe@Vs2V>dg4gljCFIHkl1*#snDE^Ag|yI zL7U{wEp4oIp35JO1<_-Dwn!^xWM(y*+RNrtqzPi$S3TD;of9qlHac2=283YpKDa}T zO4wB>@4yhVSWlX6y2Qn5^V0G2`KiSdNdwv8D#t4$&s7WVBR2BGsPJXxEbQRKML<^g zv1pyr<#Y?>7HBLiITpWe^$?>qPAHkz(n27C?L6P0(5bDiKilqKEO1fdI*h#9m&N^i znBROt$vrYA1!)ZOfCQJHNIdl03SH=5Sh1X^b4_SgvkwCH4%rM1L4B63rv0yEcQ(KN zesRS#|EDET^RpgT&d?BM)6}>wA)?ycmEY0ibrg)wmFkU@L*nHl^ydEStg5UlpA5)Z zH#VmC`vvSXyMio5kQ){yKABI#@fv(t_hjglyj&^!FvXGbud63$!sa#D z$ghpQ)x3(8bF~jg3oKvWyz2bl$miQ3;x-zJmmB=_Q*3N@vMt3cDb7#I0sh7!CC^E0 ziFDIHf>lKRvodL4H8_ZVy8er6nNzEtnr`5lPyoRurTZyHE$k+H52Og(J{aFX-NG6z zXeiHz^8Q0)x|OLVk0fOU(E$(pjq~$4!P^p(ZU$#^p$m6g!LRNr*{S&QZOI!{^H0_8 zCs@STX1#A_A0hk{H2TcGDNNg5=_=a_QAaB~6~fxZ*7hcY{U20vVJb`WuaNb{UuXd) zOKlhUL{+Tp#b}dqu(GWJg42iXowk;mGoWvtZnQvazv%T`!=LaOdKV=5BM3BI1V-@_ zcSq2#M(50XikB71?`B%aaFWbi7D>vhonUQ3`qYEe&x6#O$YQETeIoVoSd5Dt ztvHmOTSC4R{Dha@51)xY=;HF&tnz&dw`07%!y*o=k7jZAjTp zOhp?MEoesI1?yU#gAc7eYI{R}RZLk>^$!&6+T)fJELJQySA*D5+c7TEKX{*;iE0_< z?DIG&JdVS*3mJ&m3_Sf5~=Yd?e1p(AgGsvej0P+86I9((iK;yg*5f%0#XRq95e<=xs;} zZ8R-$ZKp}yO8-HZjs3kExYErR6H|}JH=!Aubz-t_5Wnci^Xk|e6tM_C1bFU3r;vYA?ocgjpEkdV_-~MDh5_j%rPmpjo&(-avKt)-RA}6n9}uOWc4AlE z=)d-%jU;Ua0G&zc!#bnd45<-vyH?iU9QVDbdpC!RD{lPns~|@I7p|bMc^hT%I5UYt z>CX3F6?xMzB?W3T#mUD)_9aR$sF^yotVQiZJxtq{_O+;_9#gPQ)2!d+=KlP)J;m!q z`W4~orlJbXO)xP9&$zwS&NuxPkEgvMS|awa1;16sOmD=HtPnS4wZx-@uCj)|m6Img zzC;HN;P&VxjzyspUb~9r*$;n1WRw~cux=9r+8rCA3ykf7WC*FwRU$zAaTFNb?;t4_ za)kX2Z8W_$G|j%(?JS(-ZNZY(6m+(JcBO{*&pSI=!;Jqy!j!XCW(ztNz7Rn}BG&T= zX;1#_+oZo{)lIsKgFG;FajD||JRdqB6JfV4;mh=wqwa6lL$()6*u)BpHAnAbGuDj~ z+_~MyOJUha_Yv+!3pM}1US=!Cz#TNzaSLcO|cMpb#|Y4wT~sap1aGH1a|6 zZDNu&;f&d%=25)02~$w*)18BU$H#f zUJm}=Uisqjj?iIyiT6EqQ^~4%0Ud$UisxWqJhu$*zwq4P=n114 zS*H%)J10Jfrb*i9>P`$aNuyOrkq;LM zYnDA?K^dYjp|V?lBHhD^0MMdJ<}BCN8j}j(MQ-C-FGv<7 z4#18NWCada0_)Ki%J!&({f#Aw1G|8WARkA^poAGfsf=f*yI{_7-+q9Mp6hN-soSay z12J!vY&yp8kA~1nGan6Gzzw4F_`i8~4V3=J4sH;UCY8@espk4rhP&aBqw`N24M9Vr zC_80VQX>&svSDlS_q7m4Ci>mt^?&JFTbU zI}_O&IhXp=wVExVJ|iS`EvpoMMyG5#Fe3}W4&IBJhOd($E1VLlueNoxephrh^%>hr z`V7o(5Yz7-ywr#`*P#izP)#w1Rt1aqRd=;Mnl|>d>}oCg#q#PgZ3fF4*_-$r*j*iRZh ze(`3G(5n-V3GF0{E&f23;RRy z&$Eqo_bi3yj-H#2lt%a)W9wZC)~H8^B1)bm%WrfN{H|^%QbNV*p7I9&QZE2^ zgsQI&lK=exCs=jr9~E7AcOTgBqRDy2;wSNh{Kf;-;};@|jnWO_WNsgwmU!${3c?Rk z=XF?*?&Ee@$F$iQ91m!XXPsT1H@y_tI4BoNR4_1HUHF^=Z3zqQDf<|+Lc6Fmbo%q1 zkR;Vaj7YR4E%ZUYv~#y&)d$9tEMdi70*_v93C^tax| z>0X1BtLApyr&KWv!Z2=2iwt*qm5kp!?cIJmRfALV@^eMUxTk#5Dx^;nspN zZEr81*KG!c^vTORwHnNtY|&Z4An-0alDWVCFz;YRRWAyZN${tc+ zTyjS3K6;!=gs;S{0-^))$}S>|3Npnv*V(X#-!iM*z!@-Px-kNox49cCj1ChW=i(aT z`hR_0`#%$I`xmi_att|535obDMRPtRm7+q*X;PY;mcy9Cralg%96}$-VXG(X7Rq6mvt^r&t!6x)=Xrg9`2O--_aAWI_v?DyulsequlM_X8RL#+tDXBo9^Mp> z7`O1hu75&8{%`bHQRD4z^B~x{B+i9{aLxs}NwYa8j|>-J0b3i`1_E@X81bJ}$+hBuY*z#uoJrzqLg*h5SG zXJietl=3M!mm~kFL(nygjmk!;)W%tYFbONPm}i;HU6(a1$lH9er(UVxiAv{~Fe$io zwkJ7g39;R0=^>1_dTMPGWODZK?FTbWUk)3Vcgydxt1fifR9ld7z2bV5*O6AhtK@tQ zxo?;)w|0d{vE8O!6GZvOjeT-opIW9>mm*f)%~t9PF&6X9+b~RP`A%koMvo|-T;16lIk^HvG=lm-yp|(a4bAX z#_+&<+KEf2y-)28AIy8L9Af{pAnkU^+m_BDiGPAyq?`@hYJL58$*v@gpn!V>;>9Qo zxvvpTfOU6#)Pf7EeBMn&U&EYm|*Q)OpfB@p1#ui(Yd!x5AL6L#7Mgu5;V`o z?AO)rKTaOyb&r3PG8o%3+qemo-4q`^o?Tv6xg5k+e*oSqF0plJA0ETpZUC(Fih(Rk0o!0X&ciM`EGic|av}Q} z;n#%l12=dT631u+n!<_?e>Mj>Pke>M6t@k7Qaw51_r2Q9neiRES-H}Q-^(U z?t1nDOWyP%cWUv}k%3_*-lYtvt50*ez7-Wk#%u9a?@Q#8E`D}yiE`^n71tm}xaXKY zzO!(AwKtsFqOmvwUeu9>c$BZKG#ffK7#g9e0b=zlA82^K*+i1G*z(t~DC7gUjU09UAY`osR1b zAA?^kRW>DEn%C{W_<%;d`^2a!+Ke;qA)yyH zpE8~z<#u!O$GQU)uQ)%~=Xo@_Ol0P7?xTgLy@CyMcI;YZV$;Cq-rb13nbGfG=)dtO z*7nOxPKm69xzQ*6jEBlzaK`P&^45V%u|q@OjGw&dxxyt1;B!K|KEZLAA51I~;DP*v z_z0A7QSN@8eKZ;jPuC*fl=P82UqP)6mQ0=E_3JIPzbsL`ztj=;1FC8jCNfqsgxPYJQ;6R5c(Y}1&CS~|5N>SgFWOhTjK zNc&{jsN<=25kS3{{Cps?@=xxKgyN9&;Mx>mmZfL>wVT?zjBdAIs_GLRYtf%yOoLR1 z45?IqKBIidTF1Oy_S%it!?hqP2FSa&vFnpzWQ>ZzQ&2&)bVNm5S#clP{gPkIYJ%}TvJ=x*`xU3`C&vfH#a?2;BDQCKCrBi51pa!;= zec`IUm3t!d@>F@nqF7jJcQNMg@UhMJyba;xSkc{&<{>Ds_hwWRKT?7_xijYC+-B99 zIA0uZ>~b|ec0E@|dbGzR;QJF!llv}9+|`CNi`k&60|)q#j4S40em}t@u>*$0+U;lu z-=Bv^PGNF4&C3)co5^Yp*+Cw;zzU1?ms$i`$%dW$EWwvy00X|D3m1zkI#jJBeOkNK za`5Y0r-H4IukJ8Kdvy!se@tLP32*0fj$+~!EX5y6bp`g}`)@cx=HMUo;ZZXg%ZsfF zf~K7dR>7&kZlW`;48)fOYFESl@24Wiig@^A%{A?3b>F=!lb{Q}EZkLBKUq2vuk*qM zO(=xd*DRiv<1WX($TRcy^eia0E1}i(w0#YhF?C~{?~i{wA^PG@RKOkTu zqy7l_gwaIcoho}@Uv$eg^f_|H0rj%ck*|Ew;`#%!zLhgy6+JF!=FQTIuEs1&@9w?sR%q+R)b5EwN|!%m-`KNd@me z-n1QZbIRxV3+6Z+CF@uf5x@6^0r0=SgEae^y7jIzd-IV^Heff%Je?T%#PHFe(oBYhbLwB3EhepU2ss}cLmXQRN zy49YaE#R8AKsTQcE1hAe@1386L#n8>nzoRT6NVQ0wzV}W%9B*>Nrk`%cQ_e-zHxu4 zCkYLXwTReAs~@fRmy~zGrnT<4yeY($kh*zShaTUCL8Cj4MVWX3pIlg=SdHbI^mok^ z{aU}P9{X|MlH$oC)jNqf-#d$kg5vhn)#||62RcG$O_GYSMTAac!irbh{`M;deN0{9 z_RGtREOvY_Q zTi6j$YjGE+d%l)3H5-1e5r@_vKU#UHsMI6-Te%t7H8nJLMjbXzXy3POc6kxvIQ2HM z`okhMrUlF1DqIJj7bN!4@^2$Ps7Uu@!5_TW<;2<~GBP&9k8KqYv=iOP?<)9ag}!RK zEXR$cu@IJD!k(LKwI338EDxm7hrKqq;Szhdy|k` zA5%1IynK!Vq~ZW-zkNQ^z*EfC#1&naVV9ro^mT_f2{|`S&N7p8ATx7N@CA+1J@_zs zJ5R=Y(liU#OHiGboN*hYoyRMHq<6l=bK)W*h+;;8<25a)nxmK99U1UKapWLaE5iAe ztlgfy>EAN?=T!N7eE%p%?uK;613UV2QX$fX9`6e#4F0_Pv&_b{hi$!od9U%7GQ=)5 zM-h510jh%2cVA6oTHE6;8I+IT4?)PSW-+%hlQl8zk@$K@>&h7PKndI6mxd?rxA~Hl zxEYi0JknUVmz>G`?l+pKVhr)!1mQeJXgWUI8$w^pRIuE_w$X3?!dCfg568KFw+~yG zTVUG0ebF>yYX>?rN)V2FI?dNv-_>v^opvyu2Wj0U0*yu44}nSTF8Gem&_o0O)cwY5 zXg15Mn+I0TL;wdqBS)D+OBG3E90-Y`m&{!%?DedP8Ppyu+hhsiBLU-kb$wWF2o(!; z%r0^=w4-hs%<^th#Q?}?C8FfC1Ir(=w@txU!U*j_)Sq@|1tIpgDf$9%Xj7u<^kvpv zgk;;nNeLJ_hG;(R!wN%4w<%2mm}qGtVA`H_i@zs9!Ab&%b|LCcyRz=_r4kga09dpU zQEA$V6~Nz@aIjqhf`$=Ir@dIAO@MYWS_-p+Hw|Ff^JO=cQKHr<*befv63c1FwMGRc z#_NPl>ueraO`>%PSpbB{)*k6j?9^d|@|IM#GSH~U95Xr?GkAtljdT6#dRD1rJ?6|o zOZQ9mF7iW^A(9kV3M4-f7u{<|maC)Gk<_?qASFwWsM1!F1=j)uw1gPgG-B5GQ6x!6 zxJPQ0@iz=?8#VebQk+O9xhHGY@ZME6I2nD4KIu63c&!TFv&t4H8%zl%nQ_f(HSoS6 zHZU1giYiHmt5bUje>21uChJA&75ImMi0$Th50#d|SqnX>Vec#;*@ zsuqZcsM!Ez02Bb}F!yk{GV+F+El}2x;z$BC zFrydo-RfNRa3!S2xIImtN8ypgxMFM>;S~!zYq@MnHc6f<&sGq+S=d|4lPP49Bv+Cx zC%o2XN07r&a3oc(D&_I&K^O(*AU~nN{K`BFY;kFGBq#|NhaYDExB$=t4H+N>NYaeE zl3>WTGq!8Ib=UFO2sn*aFFokJU)rf$JefhQFEeDmP-kSgXPl+ zo$HBRgtlj{JXKTFst~+TyU_Um*7oz?1kwMfo(DVjcKs!@o0ym8V8tGLU6%Zmad+J5 z&e5dir%z-J7Q62~KYe5aGt6P=fd)@5n9*u@^$U?>iLZtE8{}Bk5v`c-KX~oy->Dwo zu-Y2d8mRvjTZz2H|MnXPJN7@&l-X@_G-z{i?3zJGZs;f9hO?_y+)W+eCiTPO+$qZ{DT8APPqx(0|2ahAmX7Vc z#(Y?YiJ=NhSAp>D3W4K<_43wLUUo|&RTvesEord|{WOEJ+W^VDhrL*j(Og>Ser5gl luG1M>M2WaRom{ORAZU7E-&|541iI~40M--{aXU&x{R_VmjJ5y( literal 50764 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

HQd5f&e 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 diff --git a/PatRec/GUI_TestPatRec_Mov2Mov.m b/PatRec/GUI_TestPatRec_Mov2Mov.m index 43161e2..c022b60 100644 --- a/PatRec/GUI_TestPatRec_Mov2Mov.m +++ b/PatRec/GUI_TestPatRec_Mov2Mov.m @@ -47,6 +47,13 @@ % 2012-11-23 / Joel Falk-Dahlin / Moved all speeds from handles to the % patRec struct. Changed behavior of all % speed text boxes +% 2015-07-16 / Enzo Mastinu / Framework for movements and motors +% implemented, the SPC control does not need +% anymore the MoveMotorWifi or any dedicated +% SPC functions +% 2015-07-21 / Francesco Clemente / Folder select routines for the Control +% Framework. Connection to the robotic +% devices has been improved with WiFi fields % % 20xx-xx-xx / Author / Comment on update @@ -74,7 +81,7 @@ % Edit the above text to modify the response to help GUI_TestPatRec_Mov2Mov -% Last Modified by GUIDE v2.5 21-Mar-2013 10:08:43 +% Last Modified by GUIDE v2.5 21-Jul-2015 13:39:26 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; @@ -109,10 +116,13 @@ function GUI_TestPatRec_Mov2Mov_OpeningFcn(hObject, eventdata, handles, varargin % Choose default command line output for GUI_TestPatRec_Mov2Mov handles.output = hObject; global TAC +global ctrl_dir TAC.running = 0; TAC.ackTimes = 0; + + % Check if any inputs is made when calling the GUI if ~isempty(varargin) % Check if varargin{1} is a patRec @@ -136,6 +146,13 @@ function GUI_TestPatRec_Mov2Mov_OpeningFcn(hObject, eventdata, handles, varargin end end + +% serialportlist = instrhwinfo('serial'); +% serialportlist = serialportlist.SerialPorts; +% for i=1:size(serialportlist) +% set(handles.popmenu16,'String',serialportlist(i)); +% end + end % Logo image @@ -154,7 +171,7 @@ function GUI_TestPatRec_Mov2Mov_OpeningFcn(hObject, eventdata, handles, varargin guidata(hObject, handles); % UIWAIT makes GUI_TestPatRec_Mov2Mov wait for user response (see UIRESUME) -% uiwait(handles.figure1); +% uiwait(handles.Gui1); movegui(hObject,'center'); % --- Outputs from this function are returned to the command line. @@ -291,7 +308,7 @@ function pb_RealtimePatRec_Callback(hObject, eventdata, handles) % hObject handle to pb_RealtimePatRec (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) - +set(hObject, 'Enable', 'off'); %set(hObject,'Enable','off'); % validation of patRec loaded if ~isfield(handles,'patRec') @@ -1114,7 +1131,6 @@ function pb_move1_Callback(hObject, eventdata, handles) % Get the degrees to move movDeg = str2double(get(handles.et_speed1,'String')); - %Check whether to use VRE if isfield(handles,'vre_Com') global TAC @@ -1127,15 +1143,24 @@ function pb_move1_Callback(hObject, eventdata, handles) end end if get(handles.cb_motorCoupling,'Value') - % Get the communication obj - com = handles.com; + % Get the motors obj motors = handles.motors; + % Get the communication obj + Control_obj = handles.Control_obj; + if ~strcmp(Control_obj.status,'open') + fopen(Control_obj); + end + %Activate the motor direction for a short moment. - [motors,movement] = MoveMotor(com, movement, movDeg, motors); + MotorsOn(Control_obj, movement, motors, movDeg); + pause(1); + MotorsOff(Control_obj, movement, motors); + % Save data back handles.mov(mov) = movement; handles.motors = motors; guidata(hObject,handles); + end %Enable the button set(hObject,'Enable','on'); @@ -1169,12 +1194,20 @@ function pb_move2_Callback(hObject, eventdata, handles) end end if get(handles.cb_motorCoupling,'Value') - % Get the communication obj - com = handles.com; + % Get the motors obj motors = handles.motors; + % Get the communication obj + Control_obj = handles.Control_obj; + if ~strcmp(Control_obj.status,'open') + fopen(Control_obj); + end + %Activate the motor direction for a short moment. - [motors,movement] = MoveMotor(com, movement, movDeg, motors); + MotorsOn(Control_obj, movement, motors, movDeg); + pause(1); + MotorsOff(Control_obj, movement, motors); + % Save data back handles.mov(mov) = movement; handles.motors = motors; guidata(hObject,handles); @@ -1210,12 +1243,20 @@ function pb_move3_Callback(hObject, eventdata, handles) end end if get(handles.cb_motorCoupling,'Value') - % Get the communication obj - com = handles.com; + % Get the motors obj motors = handles.motors; + % Get the communication obj + Control_obj = handles.Control_obj; + if ~strcmp(Control_obj.status,'open') + fopen(Control_obj); + end + %Activate the motor direction for a short moment. - [motors,movement] = MoveMotor(com, movement, movDeg, motors); + MotorsOn(Control_obj, movement, motors, movDeg); + pause(1); + MotorsOff(Control_obj, movement, motors); + % Save data back handles.mov(mov) = movement; handles.motors = motors; guidata(hObject,handles); @@ -1251,12 +1292,20 @@ function pb_move4_Callback(hObject, eventdata, handles) end end if get(handles.cb_motorCoupling,'Value') - % Get the communication obj - com = handles.com; + % Get the motors obj motors = handles.motors; + % Get the communication obj + Control_obj = handles.Control_obj; + if ~strcmp(Control_obj.status,'open') + fopen(Control_obj); + end + %Activate the motor direction for a short moment. - [motors,movement] = MoveMotor(com, movement, movDeg, motors); + MotorsOn(Control_obj, movement, motors, movDeg); + pause(1); + MotorsOff(Control_obj, movement, motors); + % Save data back handles.mov(mov) = movement; handles.motors = motors; guidata(hObject,handles); @@ -1293,12 +1342,17 @@ function pb_move5_Callback(hObject, eventdata, handles) end end if get(handles.cb_motorCoupling,'Value') - % Get the communication obj - com = handles.com; + % Get the motors obj motors = handles.motors; + % Get the communication obj + Control_obj = handles.Control_obj; + %Activate the motor direction for a short moment. - [motors,movement] = MoveMotor(com, movement, movDeg, motors); + MotorsOn(Control_obj, movement, motors, movDeg); + pause(1); + MotorsOff(Control_obj, movement, motors); + % Save data back handles.mov(mov) = movement; handles.motors = motors; guidata(hObject,handles); @@ -1335,12 +1389,17 @@ function pb_move6_Callback(hObject, eventdata, handles) end end if get(handles.cb_motorCoupling,'Value') - % Get the communication obj - com = handles.com; + % Get the motors obj motors = handles.motors; + % Get the communication obj + Control_obj = handles.Control_obj; + %Activate the motor direction for a short moment. - [motors,movement] = MoveMotor(com, movement, movDeg, motors); + MotorsOn(Control_obj, movement, motors, movDeg); + pause(1); + MotorsOff(Control_obj, movement, motors); + % Save data back handles.mov(mov) = movement; handles.motors = motors; guidata(hObject,handles); @@ -1382,12 +1441,17 @@ function pb_move10_Callback(hObject, eventdata, handles) end end if get(handles.cb_motorCoupling,'Value') - % Get the communication obj - com = handles.com; + % Get the motors obj motors = handles.motors; + % Get the communication obj + Control_obj = handles.Control_obj; + %Activate the motor direction for a short moment. - [motors,movement] = MoveMotor(com, movement, movDeg, motors); + MotorsOn(Control_obj, movement, motors, movDeg); + pause(1); + MotorsOff(Control_obj, movement, motors); + % Save data back handles.mov(mov) = movement; handles.motors = motors; guidata(hObject,handles); @@ -1424,12 +1488,17 @@ function pb_move9_Callback(hObject, eventdata, handles) end end if get(handles.cb_motorCoupling,'Value') - % Get the communication obj - com = handles.com; + % Get the motors obj motors = handles.motors; + % Get the communication obj + Control_obj = handles.Control_obj; + %Activate the motor direction for a short moment. - [motors,movement] = MoveMotor(com, movement, movDeg, motors); + MotorsOn(Control_obj, movement, motors, movDeg); + pause(1); + MotorsOff(Control_obj, movement, motors); + % Save data back handles.mov(mov) = movement; handles.motors = motors; guidata(hObject,handles); @@ -1466,12 +1535,17 @@ function pb_move8_Callback(hObject, eventdata, handles) end end if get(handles.cb_motorCoupling,'Value') - % Get the communication obj - com = handles.com; + % Get the motors obj motors = handles.motors; + % Get the communication obj + Control_obj = handles.Control_obj; + %Activate the motor direction for a short moment. - [motors,movement] = MoveMotor(com, movement, movDeg, motors); + MotorsOn(Control_obj, movement, motors, movDeg); + pause(1); + MotorsOff(Control_obj, movement, motors); + % Save data back handles.mov(mov) = movement; handles.motors = motors; guidata(hObject,handles); @@ -1508,12 +1582,20 @@ function pb_move7_Callback(hObject, eventdata, handles) end end if get(handles.cb_motorCoupling,'Value') - % Get the communication obj - com = handles.com; + % Get the motors obj motors = handles.motors; + % Get the communication obj + Control_obj = handles.Control_obj; + if ~strcmp(Control_obj.status,'open') + fopen(Control_obj); + end + %Activate the motor direction for a short moment. - [motors,movement] = MoveMotor(com, movement, movDeg, motors); + MotorsOn(Control_obj, movement, motors, movDeg); + pause(1); + MotorsOff(Control_obj, movement, motors); + % Save data back handles.mov(mov) = movement; handles.motors = motors; guidata(hObject,handles); @@ -1526,19 +1608,28 @@ function pb_connect_Callback(hObject, eventdata, handles) % hObject handle to pb_connect (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) -compath = get(handles.et_connect,'String'); -handles.com = Connect_ALC(compath); -%handles.com = MasterModuleComm(compath); -if TestConnectionALC(handles.com)==1; %Write S to stop program - set(handles.t_msg,'String','Connection established'); - guidata(hObject,handles); -else - set(handles.t_msg,'String','Wrong connection'); - fclose(handles.com.io); + +if get(handles.rb_serial, 'Value') + compath_idx = get(handles.pm_serialportipaddress,'Value'); + compath = get(handles.pm_serialportipaddress,'string'); + compath = compath(compath_idx); + BaudRate = str2double(get(handles.et_baudrateport,'String')); + Control_obj = serial(compath, 'BaudRate', BaudRate); +elseif get(handles.rb_wifi, 'Value') + ip = get(handles.pm_serialportipaddress,'String'); + port = str2double(get(handles.et_baudrateport,'String')); + Control_obj = tcpip(ip,port,'NetworkRole','client'); end +fopen(Control_obj); +set(handles.t_msg,'String','Connection established!'); + +handles.Control_obj = Control_obj; +guidata(hObject,handles); set(handles.pb_testConnection,'Enable','on'); set(handles.pb_disconnect,'Enable','on'); +set(handles.pb_sensors,'Enable','on'); + function et_connect_Callback(hObject, eventdata, handles) % hObject handle to et_connect (see GCBO) @@ -1567,10 +1658,12 @@ function pb_disconnect_Callback(hObject, eventdata, handles) % hObject handle to pb_disconnect (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) -fclose(handles.com.io); + +fclose(handles.Control_obj) set(handles.t_msg,'String','Disconnected'); set(handles.pb_testConnection,'Enable','off'); set(handles.pb_disconnect,'Enable','off'); +set(handles.pb_sensors,'Enable','off'); % --- Executes on button press in pb_testConnection. function pb_testConnection_Callback(hObject, eventdata, handles) @@ -1578,13 +1671,47 @@ function pb_testConnection_Callback(hObject, eventdata, handles) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) set(handles.t_msg,'String','Testing connection...'); -if TestConnectionALC(handles.com)==1; %Write S to stop program - set(handles.t_msg,'String','Connection established'); - guidata(hObject,handles); + +if TestConnection(handles.Control_obj) + set(handles.t_msg,'String','Connection OK'); else set(handles.t_msg,'String','Wrong connection'); - fclose(handles.com.io); -end + fclose(handles.Control_obj); +end + +% % obtained the ID of the selected device +% device = get(handles.pm_prosthesis,'Value'); +% % run connecting routine accordingly +% if device == 1 % Multifunctinal prosthesis with DC motors +% if TestConnectionALC(handles.Control_obj)==1; %Write S to stop program +% set(handles.t_msg,'String','Connection established'); +% guidata(hObject,handles); +% else +% set(handles.t_msg,'String','Wrong connection'); +% fclose(handles.Control_obj.io); +% end +% elseif device == 3 % Standard prosthetic units (wireless) +% if isfield(handles,'Control_obj') +% Control_obj = handles.Control_obj; +% disp(Control_obj); +% fclose(Control_obj); +% else +% Control_obj = tcpip('192.168.100.10',65100,'NetworkRole','client'); +% end +% % Open connection +% fopen(Control_obj); +% fwrite(Control_obj,'A','char'); +% fwrite(Control_obj,'C','char') +% replay = char(fread(Control_obj,1,'char')); +% if strcmp(replay,'C'); +% set(handles.t_msg,'String','Connection established!'); +% else +% set(handles.t_msg,'String','Error'); +% end +% fclose(Control_obj); +% handles.Control_obj = Control_obj; +% guidata(hObject,handles); +% end @@ -1838,36 +1965,9 @@ function pb_socketConnect_Callback(hObject, eventdata, handles) % hObject handle to pb_socketConnect (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) - set(handles.pb_socketConnect,'Enable','off'); - % Dialog box to open a file -%[file, path] = uigetfile('*.exe'); -% Check that the loaded file is a "ss" struct -%if ~isequal(file, 0) -% startString = sprintf('start /D%s %s',path,file); -% disp(startString); -% [a,b] = system(startString); -%end - -% This starts the VRE-environment. Add support for different paths? -open('Virtual Reality.exe'); - -%Retrieves specified port. -port = str2double(get(handles.et_port,'String')); - -set(handles.t_msg,'String','Waiting for client connection.'); +handles = ConnectVRE(handles,'Virtual Reality.exe'); guidata(hObject,handles); -obj = tcpip('127.0.0.1',port,'NetworkRole','server'); - -fopen(obj); -set(handles.t_msg,'String',sprintf('Server established on port %d.',port)); -handles.vre_Com = obj; -guidata(hObject,handles); - -set(handles.pb_socketDisconnect,'Enable','on'); -set(handles.pb_Camera,'Enable','on'); -set(handles.pb_ActivateArm,'Enable','on'); - % --- Executes during object creation, after setting all properties. function et_port_CreateFcn(hObject, eventdata, handles) % hObject handle to et_port (see GCBO) @@ -1880,14 +1980,8 @@ function pb_socketDisconnect_Callback(hObject, eventdata, handles) % hObject handle to pb_socketDisconnect (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) -set(handles.pb_socketDisconnect,'Enable','off'); -set(handles.pb_Camera,'Enable','off'); -set(handles.pb_ActivateArm,'Enable','off'); -obj = handles.vre_Com; -fclose(obj); -set(handles.t_msg,'String','Server disconnected.'); -handles.vre_Com = obj; - set(handles.pb_socketConnect,'Enable','on'); +handles = DisconnectVRE(handles); +guidata(hObject,handles); function et_port_Callback(hObject, eventdata, handles) @@ -1927,11 +2021,10 @@ function pb_Camera_Callback(hObject, eventdata, handles) % hObject handle to pb_Camera (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) -%if isfield(handles,'vre_Com') -obj = handles.vre_Com; -fwrite(obj,sprintf('%c%c%c%c','c',char(5),char(0),char(0))); -fread(obj,1); -%end +Control_obj = handles.vre_Com; +fwrite(Control_obj,sprintf('%c%c%c%c','c',char(5),char(0),char(0))); +fread(Control_obj,1); + % --- Executes on selection change in pm_controlAlg. @@ -1979,6 +2072,15 @@ function pm_controlAlg_CreateFcn(hObject, eventdata, handles) set(hObject,'String',names); + +% --- Executes on button press in pb_socketConnect2. +function pb_socketConnect2_Callback(hObject, eventdata, handles) +% hObject handle to pb_socketConnect2 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +handles = ConnectVRE(handles,'Augmented Reality.exe'); +guidata(hObject,handles); + % --- Executes on button press in cb_keys. function cb_keys_Callback(hObject, eventdata, handles) % hObject handle to cb_keys (see GCBO) @@ -2034,7 +2136,77 @@ function et_allSpeed_CreateFcn(hObject, eventdata, handles) end +% --- Executes on button press in cb_proportionalControl. +function cb_proportionalControl_Callback(hObject, eventdata, handles) +% hObject handle to cb_proportionalControl (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of cb_proportionalControl + +% If proportional control checkbox is checked +if get(hObject,'Value') + % Create proportional control variables if patRec structure is loaded + if isfield(handles,'patRec') + % Increase maximum speed to 100 + handles.patRec.control.maxDegPerMov = ones(size(handles.patRec.control.maxDegPerMov)).*100; + set(handles.et_allSpeed,'String',100); + et_allSpeed_Callback(handles.et_allSpeed,eventdata,handles); + + % Initialize proportional control + handles.patRec = InitPropControl(handles.patRec); + + % Save guidata because it is used in GUI_ProportionalControl + guidata(hObject,handles); + + % Open proportional GUI + io.mainGUI = hObject; + setappdata(0,'selFeatures',handles.patRec.selFeatures) + GUI_ProportionalControl(io); + + % If no patRec is loaded in GUI, give message instead + else + set(t_msg,'String','No PatRec Loaded'); + end + +% If proportional control checkbox is unchecked +else + % Decrease maximum speed to 5 + handles.patRec.control.maxDegPerMov = ones(size(handles.patRec.control.maxDegPerMov)).*5; + set(handles.et_allSpeed,'String',5); + et_allSpeed_Callback(handles.et_allSpeed,eventdata,handles); + + % Remove all proportional control fields in patRec + if isfield(handles.patRec.control,'propControl') + handles.patRec.control = rmfield(handles.patRec.control,'propControl'); + end +end + +% Update GUI-handles +guidata(hObject,handles); + +% --- Executes on button press in pb_propGUI. +function pb_propGUI_Callback(hObject, eventdata, handles) +% hObject handle to pb_propGUI (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +% If patRec is loaded, launch proportional Control GUI +if isfield(handles,'patRec') + + % If proportional control checkbox is unchecked, check it + if ~get(handles.cb_proportionalControl,'Value') + set(handles.cb_proportionalControl,'Value',1); + end + + io.mainGUI = hObject; + setappdata(0,'selFeatures',handles.patRec.selFeatures) + GUI_ProportionalControl(io); + +% If no patRec is loaded, give message +else + set(t_msg,'String','No PatRec Loaded'); +end % --- Executes on button press in pb_ActivateArm. @@ -2042,9 +2214,9 @@ function pb_ActivateArm_Callback(hObject, eventdata, handles) % hObject handle to pb_ActivateArm (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) -obj = handles.vre_Com; -fwrite(obj,sprintf('%c%c%c%c%c','c',char(6),char(0),char(0),char(0))); -fread(obj,1); +Control_obj = handles.vre_Com; +fwrite(Control_obj,sprintf('%c%c%c%c%c','c',char(6),char(0),char(0),char(0))); +fread(Control_obj,1); % --- Executes on button press in pb_SetKeys. @@ -2062,3 +2234,255 @@ function cb_motionTestVRE_Callback(hObject, eventdata, handles) % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of cb_motionTestVRE + + +% --- Executes on button press in pb_ARarm. +function pb_ARarm_Callback(hObject, eventdata, handles) +% hObject handle to pb_ARarm (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +handles = ConnectVRE(handles,'Augmented Reality ARM.exe'); +% Flex the elbow with 90 degrees. +VREActivation(handles.vre_Com,90,[],15,1,get(handles.cb_moveTAC,'Value')); +guidata(hObject,handles); + + +% --- Executes on selection change in pm_prosthesis. +function pm_prosthesis_Callback(hObject, eventdata, handles) +% hObject handle to pm_prosthesis (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns pm_prosthesis contents as cell array +% contents{get(hObject,'Value')} returns selected item from pm_prosthesis + + +% --- Executes during object creation, after setting all properties. +function pm_prosthesis_CreateFcn(hObject, eventdata, handles) +% hObject handle to pm_prosthesis (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on button press in cb_controls. +function cb_controls_Callback(hObject, eventdata, handles) +% hObject handle to cb_controls (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of cb_controls + + +% --- Executes on button press in pb_enableRealTimePatRec. +function pb_enableRealTimePatRec_Callback(hObject, eventdata, handles) +% hObject handle to pb_enableRealTimePatRec (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +set(handles.pb_RealtimePatRec, 'Enable', 'on'); + + +% --- If Enable == 'on', executes on mouse press in 5 pixel border. +% --- Otherwise, executes on mouse press in 5 pixel border or over pb_ARarm. +function pb_ARarm_ButtonDownFcn(hObject, eventdata, handles) +% hObject handle to pb_ARarm (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + + +% --- Executes on button press in pb_sensors. +function pb_sensors_Callback(hObject, eventdata, handles) +% hObject handle to pb_sensors (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +GUI_Sensors(hObject, eventdata, handles); + + +function et_COM_Callback(hObject, eventdata, handles) +% hObject handle to et_COM (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_COM as text +% str2double(get(hObject,'String')) returns contents of et_COM as a double + + +% --- Executes during object creation, after setting all properties. +function et_COM_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_COM (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function edit35_Callback(hObject, eventdata, handles) +% hObject handle to edit35 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit35 as text +% str2double(get(hObject,'String')) returns contents of edit35 as a double + + +% --- Executes during object creation, after setting all properties. +function edit35_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit35 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function edit36_Callback(hObject, eventdata, handles) +% hObject handle to edit36 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of edit36 as text +% str2double(get(hObject,'String')) returns contents of edit36 as a double + + +% --- Executes during object creation, after setting all properties. +function edit36_CreateFcn(hObject, eventdata, handles) +% hObject handle to edit36 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_baudrateport_Callback(hObject, eventdata, handles) +% hObject handle to et_baudrateport (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_baudrateport as text +% str2double(get(hObject,'String')) returns contents of et_baudrateport as a double + + +% --- Executes during object creation, after setting all properties. +function et_baudrateport_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_baudrateport (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in pm_serialportipaddress. +function pm_serialportipaddress_Callback(hObject, eventdata, handles) +% hObject handle to pm_serialportipaddress (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns pm_serialportipaddress contents as cell array +% contents{get(hObject,'Value')} returns selected item from pm_serialportipaddress + + +% --- Executes during object creation, after setting all properties. +function pm_serialportipaddress_CreateFcn(hObject, eventdata, handles) +% hObject handle to pm_serialportipaddress (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end +% list all serial ports available +serialportlist = instrhwinfo('serial'); +if ~isempty(serialportlist.AvailableSerialPorts) + serialportlist = serialportlist.SerialPorts; + set(hObject,'String',serialportlist); +else + set(hObject,'String','None Available'); +end + +% --- Executes on button press in rb_serial. +function rb_serial_Callback(hObject, eventdata, handles) +% hObject handle to rb_serial (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of rb_serial +set(handles.rb_wifi,'Value',0); +set(handles.pm_serialportipaddress,'Style','popupmenu'); +% list all serial ports available +serialportlist = instrhwinfo('serial'); +if ~isempty(serialportlist.AvailableSerialPorts) + serialportlist = serialportlist.SerialPorts; + set(handles.pm_serialportipaddress,'String',serialportlist); +else + set(handles.pm_serialportipaddress,'String','None Available'); +end +set(handles.et_baudrateport,'String','BaudRate'); + +% --- Executes on button press in rb_wifi. +function rb_wifi_Callback(hObject, eventdata, handles) +% hObject handle to rb_wifi (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of rb_wifi +set(handles.rb_serial,'Value',0); +set(handles.pm_serialportipaddress,'Style','edit'); +set(handles.pm_serialportipaddress,'String','IP'); +set(handles.et_baudrateport,'String','Port'); + + +% --- Executes on button press in pb_selectctrlfolder. +function pb_selectctrlfolder_Callback(hObject, eventdata, handles) +% hObject handle to pb_selectctrlfolder (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +global ctrl_dir + +% check if another folder was added to the path before and, if yes, remove +% it from the path +if ctrl_dir + rmpath(ctrl_dir); +end + +% add the folder selected by the user to the Matlab path +ctrl_dir = uigetdir; +if ctrl_dir + addpath(ctrl_dir); +end + +% Re-Initialize control variables +% newHandles.motors = InitMotors; +% mov = InitMovements; + +handles.mov = InitMovements(); +handles.motors = InitMotors(); +% Update handles structure +guidata(hObject, handles); diff --git a/PatRec/LDA/DiscriminantAccuracy.m b/PatRec/LDA/DiscriminantAccuracy.m index 367d85d..111a8df 100644 --- a/PatRec/LDA/DiscriminantAccuracy.m +++ b/PatRec/LDA/DiscriminantAccuracy.m @@ -1,117 +1,117 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to compute the accuracy of the DA -% Test of the testing set using the coefficients found before -% currently not the faster or smatertest way to use the coeff -% needs to be improved -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-10-02 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function acc = DiscriminantAccuracy(coeff, tSet, tOut, mov, dType) - -nM = length(mov); -good = zeros(length(tSet),1); -tIdx = 1; %Test index used to compare results with tLables -sM = length(tSet)/nM; % set per movement, NOTE this is done assuming that - % the movements have equal amount of sets (rows) - -% Run the DiscrimnantTest for each testing Set -for i = 1 : size(tSet,1) - - [outMov outVector] = DiscriminantTest(coeff,tSet(i,:),dType); - - %Quick and dirty routine to evaluate 2 patterns - % IF it's known that that more than two patterns are presented - if length(find(tOut(i,:))) ~= 1 % Only one movement - [Y, I] = sort(outVector,'descend'); - outMov = I(1:2)'; - end - - if tOut(i,outMov) == 1 % Evaluate single movement - good(i) = 1; - else - i; - end - - % for old version using labels - %if strcmp(mov(outMov),tLables(tIdx)) - % good(i) = 1; - %end - - -end - -% Old processing rutines for testing the coeff before having DiscriminantTest funtion -% which might be slower since the "if" selecting the type is executed more -% times. This is paid for modularity -% -% Linears -%if strcmp(dType,'linear') || strcmp(dType,'diaglinear') -% for i = 1 : length(tSet) -% -% tempRes = zeros(nM,nM); -% for i = 1 : nM -% for j = 1 : nM -% if i ~= j -% K = coeff(i,j).const; -% L = coeff(i,j).linear; -% tempRes(i,j) = K + tSet(tIdx,:)*L; % k + f1*L1 + f2*L2 ...... -% end -% end -% end -% [maxV, m] = max(sum(tempRes,2)); -% if strcmp(mov(m),tLables(tIdx)) -% good(i) = 1; -% end -% tIdx = tIdx + 1; -% end -% Quadratrics -% elseif strcmp(dType,'quadratic') || strcmp(dType,'diagquadratic') || strcmp(dType,'mahalanobis') -% -% for i = 1 : length(tSet) -% tempRes = zeros(nM,nM); -% for i = 1 : nM -% for j = 1 : nM -% if i ~= j -% K = coeff(i,j).const; -% L = coeff(i,j).linear; -% Q = coeff(i,j).quadratic; -% tempRes(i,j) = K + tSet(tIdx,:)*L + sum((tSet(tIdx,:)*Q) .* tSet(tIdx,:), 2); -% end -% end -% end -% [maxV, m] = max(sum(tempRes,2)); -% if strcmp(mov(m),tLables(tIdx)) -% good(i) = 1; -% end -% tIdx = tIdx + 1; -% end -% end - -acc = zeros(nM+1,1); -for i = 1 : nM - s = 1+((i-1)*sM); - e = sM*i; - acc(i) = sum(good(s:e))/sM; -end -acc(i+1) = sum(good) / length(tSet); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to compute the accuracy of the DA +% Test of the testing set using the coefficients found before +% currently not the faster or smatertest way to use the coeff +% needs to be improved +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-10-02 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function acc = DiscriminantAccuracy(coeff, tSet, tOut, mov, dType) + +nM = length(mov); +good = zeros(length(tSet),1); +tIdx = 1; %Test index used to compare results with tLables +sM = length(tSet)/nM; % set per movement, NOTE this is done assuming that + % the movements have equal amount of sets (rows) + +% Run the DiscrimnantTest for each testing Set +for i = 1 : size(tSet,1) + + [outMov outVector] = DiscriminantTest(coeff,tSet(i,:),dType); + + %Quick and dirty routine to evaluate 2 patterns + % IF it's known that that more than two patterns are presented + if length(find(tOut(i,:))) ~= 1 % Only one movement + [Y, I] = sort(outVector,'descend'); + outMov = I(1:2)'; + end + + if tOut(i,outMov) == 1 % Evaluate single movement + good(i) = 1; + else + i; + end + + % for old version using labels + %if strcmp(mov(outMov),tLables(tIdx)) + % good(i) = 1; + %end + + +end + +% Old processing rutines for testing the coeff before having DiscriminantTest funtion +% which might be slower since the "if" selecting the type is executed more +% times. This is paid for modularity +% +% Linears +%if strcmp(dType,'linear') || strcmp(dType,'diaglinear') +% for i = 1 : length(tSet) +% +% tempRes = zeros(nM,nM); +% for i = 1 : nM +% for j = 1 : nM +% if i ~= j +% K = coeff(i,j).const; +% L = coeff(i,j).linear; +% tempRes(i,j) = K + tSet(tIdx,:)*L; % k + f1*L1 + f2*L2 ...... +% end +% end +% end +% [maxV, m] = max(sum(tempRes,2)); +% if strcmp(mov(m),tLables(tIdx)) +% good(i) = 1; +% end +% tIdx = tIdx + 1; +% end +% Quadratrics +% elseif strcmp(dType,'quadratic') || strcmp(dType,'diagquadratic') || strcmp(dType,'mahalanobis') +% +% for i = 1 : length(tSet) +% tempRes = zeros(nM,nM); +% for i = 1 : nM +% for j = 1 : nM +% if i ~= j +% K = coeff(i,j).const; +% L = coeff(i,j).linear; +% Q = coeff(i,j).quadratic; +% tempRes(i,j) = K + tSet(tIdx,:)*L + sum((tSet(tIdx,:)*Q) .* tSet(tIdx,:), 2); +% end +% end +% end +% [maxV, m] = max(sum(tempRes,2)); +% if strcmp(mov(m),tLables(tIdx)) +% good(i) = 1; +% end +% tIdx = tIdx + 1; +% end +% end + +acc = zeros(nM+1,1); +for i = 1 : nM + s = 1+((i-1)*sM); + e = sM*i; + acc(i) = sum(good(s:e))/sM; +end +acc(i+1) = sum(good) / length(tSet); \ No newline at end of file diff --git a/PatRec/LDA/DiscriminantAnalysis.m b/PatRec/LDA/DiscriminantAnalysis.m index c1bc842..1d938e6 100644 --- a/PatRec/LDA/DiscriminantAnalysis.m +++ b/PatRec/LDA/DiscriminantAnalysis.m @@ -1,72 +1,72 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to execute the discrimant analysis provided by matlab -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-06-24 / Max Ortiz / Created -% 2011-08-01 / Max Ortiz / Creation of the function DiscriminantTest -% 2011-10-02 / Max Ortiz / Creation of the function DiscriminantAccuracy -% 2012-05-20 / Max Ortiz / Move the Get_SetsLabels funtion inside this -% routne and deleted old commented code -% 20xx-xx-xx / Author / Comment on update - -%function [coeff accVset] = DiscriminantAnalysis(trSet, trLables, vSet, vLables, mov, dType) -function [coeff accVset] = DiscriminantAnalysis(dType, trSets, trOuts, vSets, vOuts, mov, movIdx) - -% Get labels required for LDA (only LDA so far) - [trLables, vLables] = GetSetsLables_Stack(mov, trOuts, vOuts, movIdx); - mov = mov(movIdx); - if size(trLables,1) ~= size(trOuts,1) - disp('Error obtaining the lables!!!!') - errordlg('Error obtaining the lables!','Discriminant A.') - return; - end - -%Apply discriminat to vset -[class,err,POSTERIOR,logp,coeff] = classify(vSets,trSets,trLables,dType); - -nM = size(mov,1); -good = zeros(size(vSets,1),1); -sM = size(vSets,1)/nM; % set per movement, NOTE this is done assuming that - % the movements have equal amount of sets (rows) - -% Run the DiscrimnantTest for each testing Set -for i = 1 : size(vSets,1) - if strcmp(class(i),vLables(i)) - good(i) = 1; - end -end - -accVset = zeros(nM,1); -for i = 1 : nM - s = 1+((i-1)*sM); - e = sM*i; - accVset(i) = sum(good(s:e))/sM; -end -accVset(i+1) = sum(good) / size(vSets,1); - -%Compute General level of accuracy for the validation set -% good = 0; -% for i = 1 : size(vSets,1) -% if strcmp(class(i),vLables(i)) -% good = good + 1; -% end -% end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to execute the discrimant analysis provided by matlab +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-06-24 / Max Ortiz / Created +% 2011-08-01 / Max Ortiz / Creation of the function DiscriminantTest +% 2011-10-02 / Max Ortiz / Creation of the function DiscriminantAccuracy +% 2012-05-20 / Max Ortiz / Move the Get_SetsLabels funtion inside this +% routne and deleted old commented code +% 20xx-xx-xx / Author / Comment on update + +%function [coeff accVset] = DiscriminantAnalysis(trSet, trLables, vSet, vLables, mov, dType) +function [coeff accVset] = DiscriminantAnalysis(dType, trSets, trOuts, vSets, vOuts, mov, movIdx) + +% Get labels required for LDA (only LDA so far) + [trLables, vLables] = GetSetsLables_Stack(mov, trOuts, vOuts, movIdx); + mov = mov(movIdx); + if size(trLables,1) ~= size(trOuts,1) + disp('Error obtaining the lables!!!!') + errordlg('Error obtaining the lables!','Discriminant A.') + return; + end + +%Apply discriminat to vset +[class,err,POSTERIOR,logp,coeff] = classify(vSets,trSets,trLables,dType); + +nM = size(mov,1); +good = zeros(size(vSets,1),1); +sM = size(vSets,1)/nM; % set per movement, NOTE this is done assuming that + % the movements have equal amount of sets (rows) + +% Run the DiscrimnantTest for each testing Set +for i = 1 : size(vSets,1) + if strcmp(class(i),vLables(i)) + good(i) = 1; + end +end + +accVset = zeros(nM,1); +for i = 1 : nM + s = 1+((i-1)*sM); + e = sM*i; + accVset(i) = sum(good(s:e))/sM; +end +accVset(i+1) = sum(good) / size(vSets,1); + +%Compute General level of accuracy for the validation set +% good = 0; +% for i = 1 : size(vSets,1) +% if strcmp(class(i),vLables(i)) +% good = good + 1; +% end +% end % accvSets = good / length(vSets); % Accuracy on the validation set \ No newline at end of file diff --git a/PatRec/LDA/DiscriminantTest.m b/PatRec/LDA/DiscriminantTest.m index 3567532..a168a85 100644 --- a/PatRec/LDA/DiscriminantTest.m +++ b/PatRec/LDA/DiscriminantTest.m @@ -1,61 +1,61 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to execute the discrimant analysis use the coeficient previusly -% calculated -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-08-01 / Max Ortiz / Created -% 2011-10-02 / Max Ortiz / Modified to return outVector and outMov - -function [outMov outVector] = DiscriminantTest(coeff, tSet, dType) - - nM = size(coeff,1); - tempRes = zeros(nM,nM); - - % Linears - if strcmp(dType,'linear') || strcmp(dType,'diaglinear') - for i = 1 : nM - for j = 1 : nM - if i ~= j - K = coeff(i,j).const; - L = coeff(i,j).linear; - tempRes(i,j) = K + tSet * L; % k + f1*L1 + f2*L2 ...... - end - end - end - - % Quadratrics - elseif strcmp(dType,'quadratic') || strcmp(dType,'diagquadratic') || strcmp(dType,'mahalanobis') - for i = 1 : nM - for j = 1 : nM - if i ~= j - K = coeff(i,j).const; - L = coeff(i,j).linear; - Q = coeff(i,j).quadratic; - tempRes(i,j) = K + tSet*L + sum((tSet * Q) .* tSet, 2); - end - end - end - end - - outVector = sum(tempRes,2); - [maxV, outMov] = max(outVector); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to execute the discrimant analysis use the coeficient previusly +% calculated +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-08-01 / Max Ortiz / Created +% 2011-10-02 / Max Ortiz / Modified to return outVector and outMov + +function [outMov outVector] = DiscriminantTest(coeff, tSet, dType) + + nM = size(coeff,1); + tempRes = zeros(nM,nM); + + % Linears + if strcmp(dType,'linear') || strcmp(dType,'diaglinear') + for i = 1 : nM + for j = 1 : nM + if i ~= j + K = coeff(i,j).const; + L = coeff(i,j).linear; + tempRes(i,j) = K + tSet * L; % k + f1*L1 + f2*L2 ...... + end + end + end + + % Quadratrics + elseif strcmp(dType,'quadratic') || strcmp(dType,'diagquadratic') || strcmp(dType,'mahalanobis') + for i = 1 : nM + for j = 1 : nM + if i ~= j + K = coeff(i,j).const; + L = coeff(i,j).linear; + Q = coeff(i,j).quadratic; + tempRes(i,j) = K + tSet*L + sum((tSet * Q) .* tSet, 2); + end + end + end + end + + outVector = sum(tempRes,2); + [maxV, outMov] = max(outVector); + end \ No newline at end of file diff --git a/PatRec/Load_patRec.m b/PatRec/Load_patRec.m index c19b0a8..c63c63a 100644 --- a/PatRec/Load_patRec.m +++ b/PatRec/Load_patRec.m @@ -1,143 +1,143 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Funtion to load "patRec" into a selected GUI -% -% --------------------------Updates-------------------------- -% 2011-11-16 / Max Ortiz / Creation -% 2012-06-01 / Nichlas Sander/ Loading movements into parameters of new patRec -% 2012-10-11 / Joel Falk-Dahlin/ Changed so the GUI now selects output -% movements same as trained movement if it -% exists -% 2012-10-12 / Joel Falk-Dahlin/ Moved loading of movList and speeds -% from GUI_TestPatRec_Mov2Mov here, now -% these lists are updated as they are -% changed in the GUI compared to before -% where these had to be loaded in -% TacTest and RealTimePatRec - -function Load_patRec(patRec, newGUI, loadMovements) - - % To make sure old patRecs still can be used, initialize max / current - % speeds - if ~isfield(patRec,'control') - patRec.control.maxDegPerMov = ones(1,patRec.nOuts); - elseif isfield(patRec,'control') - if ~isfield(patRec.control,'maxDegPerMov') - patRec.control.maxDegPerMov(1:patRec.nOuts) = 1; - end - end - - % Open Fig and load information - nG = eval([newGUI,'(patRec)']); - newHandles = guidata(nG); - - % Fill the GUI - set(newHandles.lb_features,'Value',1:length(patRec.selFeatures)); - set(newHandles.lb_features,'String',patRec.selFeatures); - set(newHandles.lb_movements,'String',patRec.mov(patRec.indMovIdx)); - set(newHandles.pm_SelectAlgorithm,'String',patRec.patRecTrained(1).algorithm); - set(newHandles.pm_SelectTraining,'String',patRec.patRecTrained(1).training); - set(newHandles.pm_normSets,'String',patRec.normSets.type); - - if exist('loadMovements','var') - if(loadMovements) - - % Fill List of Movements - % Setup variables - newHandles.motors = InitMotors; - mov = InitMovements; - newHandles.mov = mov; - movements = []; - patRecMovements = []; - - % Save Valid movements, that can be used with VRE/ARE and - % motors - i = 1; - while(size(movements,2) < size(mov,2)) - movements = [movements,mov(i).name]; - i = i+1; - end - - % Read movements trained in patRec structure - i = 1; - while length( patRec.movOutIdx{i} ) == 1 && i < size( patRec.movOutIdx,2 ) - patMov = patRec.mov{i}; - patMov = strrep(patMov,'Extend', 'Ext'); - patMov = strrep(patMov,'Pointer', 'Point'); - patRecMovements = [patRecMovements; {patMov}]; - i = i+1; - end - - % Set selectable options to valid movements for pop-menus in - % GUI_TestPatRec_Mov2Mov - for i=1:20 - s = sprintf('pm_m%d',i); - if isfield(newHandles,s) - set(newHandles.(s),'String',movements); - % If patRec has trained movement - if i <= length(patRecMovements) - % Find menu-value that correspond to trained - % movement - for j = 1:size(movements,2) - if strcmp(patRecMovements{i},movements{j}) - % Set menu to trained movement - set(newHandles.(s),'Value',j); - break; - end - end - end - - % if(size(get(newHandles.(s),'String'),1) > i) - % set(newHandles.(s),'Value',i); - % else - % set(newHandles.(s),'Value',1); - % end - - end - end - end - - % If we load the GUI_TestPatRec_Mov2Mov, then store movList and - % speeds to the handles by reading from the pop-menus and et-boxes - if strcmp(newGUI,'GUI_TestPatRec_Mov2Mov') - clear movements; - i = 1; - dropdown = sprintf('pm_m%d',i); - movementSpeed = sprintf('et_speed%d',i); - while(isfield(newHandles,dropdown) && isfield(newHandles,movementSpeed)) - movIndex = get(newHandles.(dropdown),'Value'); - movements(i) = newHandles.mov(movIndex); - speeds(i) = str2double(get(newHandles.(movementSpeed),'String')); - i = i+1; - dropdown = sprintf('pm_m%d',i); - movementSpeed = sprintf('et_speed%d',i); - end - %Save all values of speeds in the handles - newHandles.movList = movements; - %newHandles.maxDegPerMov = speeds; - end - - end - - - % Save the patRec in the handles - newHandles.patRec = patRec; - guidata(nG,newHandles); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Funtion to load "patRec" into a selected GUI +% +% --------------------------Updates-------------------------- +% 2011-11-16 / Max Ortiz / Creation +% 2012-06-01 / Nichlas Sander/ Loading movements into parameters of new patRec +% 2012-10-11 / Joel Falk-Dahlin/ Changed so the GUI now selects output +% movements same as trained movement if it +% exists +% 2012-10-12 / Joel Falk-Dahlin/ Moved loading of movList and speeds +% from GUI_TestPatRec_Mov2Mov here, now +% these lists are updated as they are +% changed in the GUI compared to before +% where these had to be loaded in +% TacTest and RealTimePatRec + +function Load_patRec(patRec, newGUI, loadMovements) + + % To make sure old patRecs still can be used, initialize max / current + % speeds + if ~isfield(patRec,'control') + patRec.control.maxDegPerMov = ones(1,patRec.nOuts); + elseif isfield(patRec,'control') + if ~isfield(patRec.control,'maxDegPerMov') + patRec.control.maxDegPerMov(1:patRec.nOuts) = 1; + end + end + + % Open Fig and load information + nG = eval([newGUI,'(patRec)']); + newHandles = guidata(nG); + + % Fill the GUI + set(newHandles.lb_features,'Value',1:length(patRec.selFeatures)); + set(newHandles.lb_features,'String',patRec.selFeatures); + set(newHandles.lb_movements,'String',patRec.mov(patRec.indMovIdx)); + set(newHandles.pm_SelectAlgorithm,'String',patRec.patRecTrained(1).algorithm); + set(newHandles.pm_SelectTraining,'String',patRec.patRecTrained(1).training); + set(newHandles.pm_normSets,'String',patRec.normSets.type); + + if exist('loadMovements','var') + if(loadMovements) + + % Fill List of Movements + % Setup variables + newHandles.motors = InitMotors; + mov = InitMovements; + newHandles.mov = mov; + movements = []; + patRecMovements = []; + + % Save Valid movements, that can be used with VRE/ARE and + % motors + i = 1; + while(size(movements,2) < size(mov,2)) + movements = [movements,mov(i).name]; + i = i+1; + end + + % Read movements trained in patRec structure + i = 1; + while length( patRec.movOutIdx{i} ) == 1 && i < size( patRec.movOutIdx,2 ) + patMov = patRec.mov{i}; + patMov = strrep(patMov,'Extend', 'Ext'); + patMov = strrep(patMov,'Pointer', 'Point'); + patRecMovements = [patRecMovements; {patMov}]; + i = i+1; + end + + % Set selectable options to valid movements for pop-menus in + % GUI_TestPatRec_Mov2Mov + for i=1:20 + s = sprintf('pm_m%d',i); + if isfield(newHandles,s) + set(newHandles.(s),'String',movements); + % If patRec has trained movement + if i <= length(patRecMovements) + % Find menu-value that correspond to trained + % movement + for j = 1:size(movements,2) + if strcmp(patRecMovements{i},movements{j}) + % Set menu to trained movement + set(newHandles.(s),'Value',j); + break; + end + end + end + + % if(size(get(newHandles.(s),'String'),1) > i) + % set(newHandles.(s),'Value',i); + % else + % set(newHandles.(s),'Value',1); + % end + + end + end + end + + % If we load the GUI_TestPatRec_Mov2Mov, then store movList and + % speeds to the handles by reading from the pop-menus and et-boxes + if strcmp(newGUI,'GUI_TestPatRec_Mov2Mov') + clear movements; + i = 1; + dropdown = sprintf('pm_m%d',i); + movementSpeed = sprintf('et_speed%d',i); + while(isfield(newHandles,dropdown) && isfield(newHandles,movementSpeed)) + movIndex = get(newHandles.(dropdown),'Value'); + movements(i) = newHandles.mov(movIndex); + speeds(i) = str2double(get(newHandles.(movementSpeed),'String')); + i = i+1; + dropdown = sprintf('pm_m%d',i); + movementSpeed = sprintf('et_speed%d',i); + end + %Save all values of speeds in the handles + newHandles.movList = movements; + %newHandles.maxDegPerMov = speeds; + end + + end + + + % Save the patRec in the handles + newHandles.patRec = patRec; + guidata(nG,newHandles); + \ No newline at end of file diff --git a/PatRec/Load_recSession.m b/PatRec/Load_recSession.m index ed496e2..9bd3e08 100644 --- a/PatRec/Load_recSession.m +++ b/PatRec/Load_recSession.m @@ -1,83 +1,84 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Funtion to load "recSession" in GUI_PatRec -% -% --------------------------Updates-------------------------- -% 2011-06-30 / Max Ortiz / Creation -% 2011-07-19 / Max Ortiz / Modified to only load the recSession, -% the signal processing has been dived inside -% the GUI_SigTreatment - -function Load_recSession(recSession, handles) - - % Removed useless fields from previus versions (EMG_AQ) - if isfield(recSession,'trdata') - recSession = rmfield(recSession,'trdata'); - end - if isfield(recSession,'cTp') - recSession = rmfield(recSession,'cTp'); - end - - % Open Fig and load information - - %st = GUI_GetSigFeatures(); - st = GUI_SigTreatment(); - stdata = guidata(st); - set(stdata.et_sF,'String',num2str(recSession.sF)); - set(stdata.et_nM,'String',num2str(recSession.nM)); - set(stdata.et_nR,'String',num2str(recSession.nR)); - set(stdata.et_cT,'String',num2str(recSession.cT)); - set(stdata.et_rT,'String',num2str(recSession.rT)); - set(stdata.lb_movements,'String',recSession.mov); - set(stdata.lb_movements,'Value',1:recSession.nM); - - if isfield(recSession,'dev') - set(stdata.t_dev,'String',recSession.dev); - else - set(stdata.t_dev,'String','Unknown'); - end - - if isfield(recSession,'nCh') - nCh = recSession.nCh; - if length(recSession.nCh) == 1 - recSession.nCh = 1:recSession.nCh; - nCh = recSession.nCh; - end - if length(recSession.nCh) ~= length(recSession.tdata(1,:,1)) - set(stdata.t_msg,'String','Error in the number of channels'); - set(handles.t_msg,'String','Error in the number of channels'); - end - else - nCh = 1:length(recSession.tdata(1,:,1)); - recSession.nCh = nCh; - end - set(stdata.lb_nCh,'String',num2str(nCh')); - set(stdata.lb_nCh,'Value',nCh); - - - %set(stdata.t_path,'UserData',path); - - %Load the whole recSession - set(stdata.t_recSession,'UserData',recSession); - % Save this GUI handles - set(stdata.t_mhandles,'UserData',handles); - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Funtion to load "recSession" in GUI_PatRec +% +% --------------------------Updates-------------------------- +% 2011-06-30 / Max Ortiz / Creation +% 2011-07-19 / Max Ortiz / Modified to only load the recSession, +% the signal processing has been dived inside +% the GUI_SigTreatment + +function Load_recSession(recSession, handles) + + % Removed useless fields from previus versions (EMG_AQ) + if isfield(recSession,'trdata') + recSession = rmfield(recSession,'trdata'); + end + if isfield(recSession,'cTp') + recSession = rmfield(recSession,'cTp'); + end + + % Open Fig and load information + + %st = GUI_GetSigFeatures(); + st = GUI_SigTreatment(); + stdata = guidata(st); + set(stdata.et_sF,'String',num2str(recSession.sF)); + set(stdata.et_downsample,'String',num2str(recSession.sF)); + set(stdata.et_nM,'String',num2str(recSession.nM)); + set(stdata.et_nR,'String',num2str(recSession.nR)); + set(stdata.et_cT,'String',num2str(recSession.cT)); + set(stdata.et_rT,'String',num2str(recSession.rT)); + set(stdata.lb_movements,'String',recSession.mov); + set(stdata.lb_movements,'Value',1:recSession.nM); + + if isfield(recSession,'dev') + set(stdata.t_dev,'String',recSession.dev); + else + set(stdata.t_dev,'String','Unknown'); + end + + if isfield(recSession,'nCh') + nCh = recSession.nCh; + if length(recSession.nCh) == 1 + recSession.nCh = 1:recSession.nCh; + nCh = recSession.nCh; + end + if length(recSession.nCh) ~= length(recSession.tdata(1,:,1)) + set(stdata.t_msg,'String','Error in the number of channels'); + set(handles.t_msg,'String','Error in the number of channels'); + end + else + nCh = 1:length(recSession.tdata(1,:,1)); + recSession.nCh = nCh; + end + set(stdata.lb_nCh,'String',num2str(nCh')); + set(stdata.lb_nCh,'Value',nCh); + + + %set(stdata.t_path,'UserData',path); + + %Load the whole recSession + set(stdata.t_recSession,'UserData',recSession); + % Save this GUI handles + set(stdata.t_mhandles,'UserData',handles); + + + + diff --git a/PatRec/Load_sigFeatures.m b/PatRec/Load_sigFeatures.m index ca7e2c1..5b27529 100644 --- a/PatRec/Load_sigFeatures.m +++ b/PatRec/Load_sigFeatures.m @@ -1,50 +1,50 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% % Funtion to load "trated_data" in GUI_PatRec -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-06-25 Max Ortiz, Created -% 20xx-xx-xx / Author / Comment on update - -function Load_sigFeatures(sigFeatures, handles) - - % Keep compatibility with previous files - sigFeatures = Compatibility_treated_data(sigFeatures); - - % Load treated data - set(handles.t_sigFeatures,'UserData',sigFeatures); - % Load information from treated data - set(handles.et_trSets,'String',num2str(sigFeatures.trSets)); - set(handles.et_vSets,'String',num2str(sigFeatures.vSets)); - set(handles.et_tSets,'String',num2str(sigFeatures.tSets)); - set(handles.lb_movements,'String',sigFeatures.mov); - - allFeatures = fieldnames(sigFeatures.trFeatures); - set(handles.lb_features,'String',allFeatures); - set(handles.lb_features,'Value',1:length(allFeatures)); - - % Enable controls - set(handles.pm_SelectAlgorithm,'Enable','on'); - set(handles.rb_top2,'Value',1); - set(handles.lb_features,'Value',[3 5]); % tstd, twl -% set(handles.lb_features,'Value',[2 7 9 13]); % tmabs, twl, tzc, tslpch EH03, HZ09, SL09 - - % User message - set(handles.t_msg,'String','Treated data loaded'); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% % Funtion to load "trated_data" in GUI_PatRec +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-06-25 Max Ortiz, Created +% 20xx-xx-xx / Author / Comment on update + +function Load_sigFeatures(sigFeatures, handles) + + % Keep compatibility with previous files + sigFeatures = Compatibility_treated_data(sigFeatures); + + % Load treated data + set(handles.t_sigFeatures,'UserData',sigFeatures); + % Load information from treated data + set(handles.et_trSets,'String',num2str(sigFeatures.trSets)); + set(handles.et_vSets,'String',num2str(sigFeatures.vSets)); + set(handles.et_tSets,'String',num2str(sigFeatures.tSets)); + set(handles.lb_movements,'String',sigFeatures.mov); + + allFeatures = fieldnames(sigFeatures.trFeatures); + set(handles.lb_features,'String',allFeatures); + set(handles.lb_features,'Value',1:length(allFeatures)); + + % Enable controls + set(handles.pm_SelectAlgorithm,'Enable','on'); + set(handles.rb_top2,'Value',1); + set(handles.lb_features,'Value',[3 5]); % tstd, twl +% set(handles.lb_features,'Value',[2 7 9 13]); % tmabs, twl, tzc, tslpch EH03, HZ09, SL09 + + % User message + set(handles.t_msg,'String','Treated data loaded'); diff --git a/PatRec/Load_treated_data.m b/PatRec/Load_treated_data.m index e9c2b77..2e534cd 100644 --- a/PatRec/Load_treated_data.m +++ b/PatRec/Load_treated_data.m @@ -1,53 +1,53 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to load "trated_data" in GUI_PatRec -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-06-25 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update% - - -function Load_treated_data(treated_data, handles) - - % Keep compatibility with previous files - treated_data = Compatibility_treated_data(treated_data); - - % Load treated data - set(handles.t_treated_data,'UserData',treated_data); - % Load information from treated data - set(handles.et_trSets,'String',num2str(treated_data.trSets)); - set(handles.et_vSets,'String',num2str(treated_data.vSets)); - set(handles.et_tSets,'String',num2str(treated_data.tSets)); - set(handles.lb_movements,'String',treated_data.mov); - - allFeatures = fieldnames(treated_data.trdata); - set(handles.lb_features,'String',allFeatures); - set(handles.lb_features,'Value',1:length(allFeatures)); - - % Enable controls - set(handles.pm_SelectAlgorithm,'Enable','on'); - set(handles.rb_all,'Enable','on'); - set(handles.rb_top3,'Enable','on'); - set(handles.rb_top4,'Enable','on'); - set(handles.rb_top4,'Value',1); - set(handles.lb_features,'Value',[2 7 9 13]); % tmabs, twl, tzc, tslpch EH03, HZ09, SL09 - - % User message - set(handles.t_msg,'String','Treated data loaded'); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to load "trated_data" in GUI_PatRec +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-06-25 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update% + + +function Load_treated_data(treated_data, handles) + + % Keep compatibility with previous files + treated_data = Compatibility_treated_data(treated_data); + + % Load treated data + set(handles.t_treated_data,'UserData',treated_data); + % Load information from treated data + set(handles.et_trSets,'String',num2str(treated_data.trSets)); + set(handles.et_vSets,'String',num2str(treated_data.vSets)); + set(handles.et_tSets,'String',num2str(treated_data.tSets)); + set(handles.lb_movements,'String',treated_data.mov); + + allFeatures = fieldnames(treated_data.trdata); + set(handles.lb_features,'String',allFeatures); + set(handles.lb_features,'Value',1:length(allFeatures)); + + % Enable controls + set(handles.pm_SelectAlgorithm,'Enable','on'); + set(handles.rb_all,'Enable','on'); + set(handles.rb_top3,'Enable','on'); + set(handles.rb_top4,'Enable','on'); + set(handles.rb_top4,'Value',1); + set(handles.lb_features,'Value',[2 7 9 13]); % tmabs, twl, tzc, tslpch EH03, HZ09, SL09 + + % User message + set(handles.t_msg,'String','Treated data loaded'); diff --git a/PatRec/MotionTest.m b/PatRec/MotionTest.m index 8214307..1944ed9 100644 --- a/PatRec/MotionTest.m +++ b/PatRec/MotionTest.m @@ -1,449 +1,489 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Motion Test for to evaluate real-time performance of the patRec -% This test is implemented in such a way that the "rest" movement is -% required -% -% --------------------------Updates-------------------------- -% [Contributors are welcome to add their email] -% 2012-01-xx / Max Ortiz / Creation from MotionTestLegacy -% 2012-05-29 / Max Ortiz / Addition of "clear" commands -% 2012-07-24 / Max Ortiz / Use fpTW to compute the predition time of the -% first prediction in order to calculate the selection time -% 2012-10-05 / Joel Falk-Dahlin / Added ApplyControl after OneShotPatRec -% to allow for the use of control algorithms in the motion test -% 2012-10-26 / Joel Falk-Dahlin / Added ApplyProportionalControl -% 2011-11-06 / Max Ortiz / Create RealimePatRec_OneShot to concentrate -% critical routines of realtime patrec -% 20xx-xx-xx / Author / Comment on update - -function motionTest = MotionTest(patRecX, handlesX) - -clear global; - -global patRec; -global handles; -global nTW; % Number of time windows evaluated -global fpTW; % Time window of the first prediction -global dataTW; % Raw data from each time windows -global tempData; % Reset tempData if it is the first call -global motorCoupling; -global vreCoupling; -global thresholdGUIData; - -% Time markers -global selTimeTic; - -% Key performance indicators -global nCorrMov; -global selTime; % Selection time (selected = expected movement) -global selTimeTW; -global compTime; % Completion time (selected = expected movement) -global compTimeTW; -global nwCompTime; % Number of time windows to consider completion time -global predictions; -global procTime; % Processing time - -% Key performance indicators for at least 1 correct prediction -global nCorrMov1; % Number of at least 1 correct number -global selTime1; % Selection time (selected = at leas 1 expected mov) -global selTime1TW; -global compTime1; % Completion time (selected = at leas 1 expected mov) -global compTime1TW; -% Variables to keep track of testing movement -global mIdx; -global m; - -%% Init variable -patRec = patRecX; -handles = handlesX; -pDiv = 2; % Peeking devider -trials = str2double(get(handles.et_trials,'String')); -nR = str2double(get(handles.et_nR,'String')); -timeOut = str2double(get(handles.et_timeOut,'String')); -nwCompTime = 20; % Number of windows to consider completion time -pause on; % Enable pauses - -%% Is threshold (thOut) used? -if(isfield(patRec.patRecTrained,'thOut')); - %Threshold GUI init - thresholdGUI = GUI_Threshold; - thresholdGUIData = guidata(thresholdGUI); - set(GUI_Threshold,'CloseRequestFcn', 'set(GUI_Threshold, ''Visible'', ''off'')'); - xpatch = [1 1 0 0]; - ypatch = [0 0 0 0]; - for i=0:patRec.nOuts-1 - s = sprintf('movementSelector%d',i); - s0 = sprintf('thPatch%d',i); - s1 = sprintf('meter%d',i); - axes(thresholdGUIData.(s1)); - handles.(s0) = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','normal','visible','on'); - ylim(thresholdGUIData.(s1), [0 1]); - xlim('auto'); - set(thresholdGUIData.(s),'String',patRec.mov(patRec.indMovIdx)); - if (size(patRec.mov(patRec.indMovIdx),1) < i+1); - set(thresholdGUIData.(s),'Value',size(patRec.indMovIdx,2)); - else - set(thresholdGUIData.(s),'Value',i+1); - end - end -end - -%% Is the VRE selected? -if isfield(handles,'cb_motionTestVRE') - trainUsingVre = get(handles.cb_motionTestVRE,'Value'); -else - trainUsingVre = 0; -end - -%% Is there an option for coupling with the motors? -if isfield(handles,'cb_motorCoupling') %&& ~isfield(handles,'com') - motorCoupling = get(handles.cb_motorCoupling,'Value'); -else - motorCoupling = 0; -end - -%% Init motionTest structure -motionTest.patRec = patRec; -motionTest.sF = patRec.sF; -motionTest.tW = patRec.tW; -motionTest.trails = trials; -motionTest.nR = nR; -motionTest.timeOut = timeOut; - -%% Initialize DAQ card -% Note: A function for DAQ selection will be required when more cards are -% added - -if strcmp(patRec.dev,'ADH') - -else % at this poin everything else is SBI (e.g.NI-USB6009) - chAI = zeros(1,8); - chAI(patRec.nCh) = 1; - % create the SBI - s = InitSBI_NI(patRec.sF,timeOut,chAI); - % Change the peek time - s.NotifyWhenDataAvailableExceeds = (patRec.sF*patRec.tW)/pDiv; % Max 0.05, or 20 times per second - %Add listener - lh = s.addlistener('DataAvailable', @MotionTest_OneShot); - %Test the DAQ by ploting the data - %lh = s.addlistener('DataAvailable', @plotDataTest); -end - -%% Motion Test -% Note: Probabily this way of testing only works for the NI -% specific funtions per card might be required. - -if trainUsingVre - if ~isfield(handles,'vre_Com') - handles = ConnectVRE(handles,'Virtual Reality.exe'); - end -end - -% run over all the trails -for t = 1 : trials - - for i = 1 : 5; - msg = ['Trial: "' num2str(t) '" in: ' num2str(6-i) ' seconds']; - set(handles.t_msg,'String',msg); - pause(1); - end - - % repite the movement the chosen times - for r = 1 : nR - % Randomize the aperance of the requested movement - mIdx = randperm(patRec.nM - 1); % It doesn't include "rest" - % Run over all movements but "rest" - for m = 1 : patRec.nM - 1 - - %Select movement - movementObjects = handles.movList(patRec.movOutIdx{mIdx(m)}); - mov = patRec.mov{mIdx(m)}; - %Prepare - for i = 1 : 3; - % Warn the user - %msg = ['Relax and prepare to "' mov '" in: ' num2str(4-i) ' seconds (trial:' num2str(t) ', rep:' num2str(r) ')']; - % No warning - msg = 'Relax'; - set(handles.t_msg,'String',msg); - pause(1); - end - - % Warning to the user - fileName = ['Img/' char(mov) '.jpg']; - if ~exist(fileName,'file') - fileName = 'Img/relax.jpg'; - end - % Display image - movI = importdata(fileName); % Import Image - set(handles.a_pic,'Visible','on'); % Turn on visibility - pic = image(movI,'Parent',handles.a_pic); % set image - axis(handles.a_pic,'off'); % Remove axis tick marks - - % Init required for patRec of the selected movement - nTW = 1; % Number of time windows - fpTW = 0; % Time window of the first prediction - nCorrMov = 0; % Number of corect movements - predictions = {}; % Vector with the classifier prediction per tw - selTime = NaN; % Selection time - selTimeTW = NaN; % nTW when the selection time happen - compTime = NaN; % Completion time - compTimeTW = NaN; % nTW when the completion time happen - - nCorrMov1 = 0; % Number of corect movements of at least 1 mov - selTime1 = NaN; % Selection time of at least 1 expected mov - selTime1TW = NaN; % nTW when the selection time happen - compTime1 = NaN; % Completion time - compTime1TW = NaN; % nTW when the completion time happen - - selTimeTic = []; % Resets the time of first movement - procTime = []; % Processing time (vector) - dataTW = []; % Reset the dataTW (matrix) - tempData = []; % Reset tempData if it is the first call - - % Ask the user to execute movement - set(handles.t_msg,'String',mov); - drawnow; - %% Start test - s.startBackground(); % Start the data acquision for timeOut seconds - - % Move the VRE into place. - for i = 1:length(movementObjects) - movementObject = movementObjects(i); - if trainUsingVre - % Send values to VRE to move hand into position. - for j = 1:15 %Move it 15 times, distance of 5 each time. - VREActivation(handles.vre_Com,5,[],movementObject.idVRE, movementObject.vreDir, 0); - end - end - end - - %selTimeTic = tic; % Start counting after user instruction, - % which might not be as preciase as start counting just after a - % movement is predicted. - - % Wait until it has finished done - %s.IsDone % will report 0 - s.wait(); % rather than while - %s.IsDone % will report 1 - - %Reset the VRE. - if trainUsingVre - ResetVRE(handles.vre_Com,1,1); - end - - %% Save data - test.mov = mov; - test.nTW = nTW-1; - test.fpTW = fpTW; - test.nCorrMov = nCorrMov; - test.predictions = predictions; - test.selTime = selTime; - test.selTimeTW = selTimeTW; - test.compTime = compTime; - test.compTimeTW = compTimeTW; - % Performance for at least 1 movement - test.nCorrMov1 = nCorrMov1; - test.selTime1 = selTime1; - test.selTime1TW = selTime1TW; - test.compTime1 = compTime1; - test.compTime1TW = compTime1TW; - % Data - test.dataTW = dataTW; - test.procTime = mean(procTime); - - % Test fail? - if isnan(compTime) - test.fail = 1; - % Test failed even for 1 movement simul? - if isnan(compTime1) - test.fail1 = 1; - else - test.fail1 = 0; - end - else - test.fail = 0; - test.fail1 = 0; - end - - % Compute accuracy between fpTw to achivement of compTime - if test.fail - % Consider the accuracy during fail trials - %corrP = sum(predictions == mIdx(m)); - %test.acc = corrP / size(predictions,2); - - %Don't acount for acc in failed trials - test.acc = NaN; - - else - corrP = 0; - for i = fpTW : compTimeTW+fpTW-1 - if isequal(predictions{i},patRec.movOutIdx{mIdx(m)}') - corrP = corrP +1; - end - end - %corrP = sum(predictions{fpTW:fpTW+compTimeTW-1} == mIdx(m)); %It doesn't work for simultaneous prediction - test.acc = corrP / compTimeTW; - end - - disp(test); - motionTest.test(r,mIdx(m),t) = test; - delete(pic); - set(handles.t_msgMT,'Visible','off'); - drawnow; - end - end -end - -if trainUsingVre - handles = DisconnectVRE(handles); -end - -% Saved any modification to patRec -motionTest.patRec = patRec; - -%Delete listener SBI -delete (lh) -pause off; - -% show results -motionTest = MotionTestResults(motionTest); - -% Save test -disp(motionTest); -[filename, pathname] = uiputfile({'*.mat','MAT-files (*.mat)'},'Save as', 'Untitled.mat'); - if isequal(filename,0) || isequal(pathname,0) - disp('User pressed cancel') - else - save([pathname,filename],'motionTest'); - end - -% Clear key variables -clear mIdx; -clear m; - -end - -function MotionTest_OneShot(src,event) - - % General - global patRec; - global handles; - global nTW; % Number of time windows evaluated - global fpTW; % Time window of the first prediction - global dataTW; % Raw data from each time windows - global tempData; - global motorCoupling; - global vreCoupling; - global thresholdGUIData; - - % Time markers - global selTimeTic; - % Key performance indicators - global procTime; - global nCorrMov; - global selTime; % Selection time - global selTimeTW; - global compTime; % Completion time - global compTimeTW; - global nwCompTime; % Number of time windows to consider completion time - global predictions; - % Key performance indicators for at least one movement - global nCorrMov1; % Number of at least 1 correct number - global selTime1; % Selection time (selected = at leas 1 expected mov) - global selTime1TW; - global compTime1; % Completion time (selected = at leas 1 expected mov) - global compTime1TW; - % Variables to keep track of testing movement - global mIdx; - global m; - - - tempData = [tempData; event.Data]; % Record all signal - - % Is there enough data for a TW? - if size(tempData,1) >= (patRec.sF*patRec.tW) - - % Save the TW data, maybe useful for further analysis - tData = tempData(end-patRec.sF*patRec.tW+1:end,:); %Copy the temporal data to the test data - dataTW(:,:,nTW) = tData; % Save data for future analisys - - % Start of processing time - procTimeTic = tic; - - % General routine for RealtimePatRec - [outMov outVector patRec handles] = OneShotRealtimePatRec(tData, patRec, handles, thresholdGUIData); - - % Finish of processing time - procTime(nTW) = toc(procTimeTic); - - % Save the classifier prediction for statistics - predictions{nTW} = outMov; - - % Is there a prediction different than "rest"? - % This conditional considers that there is always a rest movement, - % and this has the last index, therefore rest Indx = patRec.nOuts - if isempty(selTimeTic) - % Compare each outMov to rest -% maskRest(1:size(outMov)) = patRec.nM; - maskRest(1:size(outMov)) = patRec.nOuts; - if sum(outMov ~= maskRest') - selTimeTic = tic; % Starts counting time for selection and completion - fpTW = nTW; % Consider the TW of the first moment - end - end - - % Is the output the expected classes? - if isequal(patRec.movOutIdx{mIdx(m)}, outMov') - nCorrMov = nCorrMov + 1; - if nCorrMov == 1; - selTime = toc(selTimeTic)+patRec.tW+procTime(fpTW); - selTimeTW = nTW-fpTW+1; - elseif nCorrMov == nwCompTime; - compTime = toc(selTimeTic)+patRec.tW+procTime(fpTW); - compTimeTW = nTW-fpTW+1; - set(handles.t_msgMT,'Visible','on'); - drawnow; - end - else % At least one movement is correct? - for i = 1 : size(outMov,1) - if find(patRec.movOutIdx{mIdx(m)} == outMov(i)) - nCorrMov1 = nCorrMov1 + 1; - if nCorrMov1 == 1; - selTime1 = toc(selTimeTic)+patRec.tW+procTime(fpTW); - selTime1TW = nTW-fpTW+1; - elseif nCorrMov1 == nwCompTime; - compTime1 = toc(selTimeTic)+patRec.tW+procTime(fpTW); - compTime1TW = nTW-fpTW+1; - end - break; % Break the for - end - end - end - - if ~isnan(compTime) - src.stop(); - pause(1); - end - % Next cycle - nTW = nTW + 1; - end - - end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Motion Test for to evaluate real-time performance of the patRec +% This test is implemented in such a way that the "rest" movement is +% required +% +% --------------------------Updates-------------------------- +% [Contributors are welcome to add their email] +% 2012-01-xx / Max Ortiz / Creation from MotionTestLegacy +% 2012-05-29 / Max Ortiz / Addition of "clear" commands +% 2012-07-24 / Max Ortiz / Use fpTW to compute the predition time of the +% first prediction in order to calculate the selection time +% 2012-10-05 / Joel Falk-Dahlin / Added ApplyControl after OneShotPatRec +% to allow for the use of control algorithms in the motion test +% 2012-10-26 / Joel Falk-Dahlin / Added ApplyProportionalControl +% 2012-11-06 / Max Ortiz / Create RealimePatRec_OneShot to concentrate +% critical routines of realtime patrec +% 2013-01-29 / Nichlas Sander / Added possibility to use VRE as guidance +% during the motion test. +% 2015-02-02 / Enzo Mastinu / All this function has been re-organizated +% to be compatible with the functions +% placed into COMM/AFE folder. For more +% details read RecordingSession.m log. + +% 20xx-xx-xx / Author / Comment on update + + + +function motionTest = MotionTest(patRecX, handlesX) + +clear global; + +global patRec; +global handles; +global nTW; % Number of time windows evaluated +global fpTW; % Time window of the first prediction +global dataTW; % Raw data from each time windows +global tempData; % Reset tempData if it is the first call +global motorCoupling; +global vreCoupling; +global thresholdGUIData; + +% Time markers +global selTimeTic; + +% Key performance indicators +global nCorrMov; +global selTime; % Selection time (selected = expected movement) +global selTimeTW; +global compTime; % Completion time (selected = expected movement) +global compTimeTW; +global nwCompTime; % Number of time windows to consider completion time +global predictions; +global procTime; % Processing time + +% Key performance indicators for at least 1 correct prediction +global nCorrMov1; % Number of at least 1 correct number +global selTime1; % Selection time (selected = at leas 1 expected mov) +global selTime1TW; +global compTime1; % Completion time (selected = at leas 1 expected mov) +global compTime1TW; +% Variables to keep track of testing movement +global mIdx; +global m; + +%% Init variable +patRec = patRecX; +handles = handlesX; +pDiv = 4; % Peeking devider +trials = str2double(get(handles.et_trials,'String')); +nR = str2double(get(handles.et_nR,'String')); +timeOut = str2double(get(handles.et_timeOut,'String')); +nwCompTime = 20; % Number of windows to consider completion time +pause on; % Enable pauses + +%% Is threshold (thOut) used? +if(isfield(patRec.patRecTrained,'thOut')); + %Threshold GUI init + thresholdGUI = GUI_Threshold; + thresholdGUIData = guidata(thresholdGUI); + set(GUI_Threshold,'CloseRequestFcn', 'set(GUI_Threshold, ''Visible'', ''off'')'); + xpatch = [1 1 0 0]; + ypatch = [0 0 0 0]; + for i=0:patRec.nOuts-1 + s = sprintf('movementSelector%d',i); + s0 = sprintf('thPatch%d',i); + s1 = sprintf('meter%d',i); + axes(thresholdGUIData.(s1)); + handles.(s0) = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','normal','visible','on'); + ylim(thresholdGUIData.(s1), [0 1]); + xlim('auto'); + set(thresholdGUIData.(s),'String',patRec.mov(patRec.indMovIdx)); + if (size(patRec.mov(patRec.indMovIdx),1) < i+1); + set(thresholdGUIData.(s),'Value',size(patRec.indMovIdx,2)); + else + set(thresholdGUIData.(s),'Value',i+1); + end + end +end + +%% Is the VRE selected? +if isfield(handles,'cb_motionTestVRE') + trainUsingVre = get(handles.cb_motionTestVRE,'Value'); +else + trainUsingVre = 0; +end + +%% Is there an option for coupling with the motors? +if isfield(handles,'cb_motorCoupling') + motorCoupling = get(handles.cb_motorCoupling,'Value'); +else + motorCoupling = 0; +end + +%% Init motionTest structure +motionTest.patRec = patRec; +motionTest.sF = patRec.sF; +% motionTest.tW = patRec.tW; +motionTest.trails = trials; +motionTest.nR = nR; +motionTest.timeOut = timeOut; + +sF = motionTest.sF; +nCh = length(patRec.nCh); +ComPortType = patRec.comm; +deviceName = patRec.dev; + +% Get sampling time +sT = motionTest.timeOut; +tW = sT/100; % Time window size +tWs = tW*sF; % Time window samples + + +%% Motion Test +% Note: Probabily this way of testing only works for the NI +% specific funtions per card might be required. + +if trainUsingVre + if ~isfield(handles,'vre_Com') + handles = ConnectVRE(handles,'Virtual Reality.exe'); + end +end + + +% run over all the trails +for t = 1 : trials + + for i = 1 : 5; + msg = ['Trial: "' num2str(t) '" in: ' num2str(6-i) ' seconds']; + set(handles.t_msg,'String',msg); + pause(1); + end + + % repeat the movement the chosen times + for r = 1 : nR + % Randomize the aperance of the requested movement + mIdx = randperm(patRec.nM - 1); % It doesn't include "rest" + % Run over all movements but "rest" + for m = 1 : patRec.nM - 1 + + %Select movement + movementObjects = handles.movList(patRec.movOutIdx{mIdx(m)}); + mov = patRec.mov{mIdx(m)}; + %Prepare + for i = 1 : 3; + % Warn the user + %msg = ['Relax and prepare to "' mov '" in: ' num2str(4-i) ' seconds (trial:' num2str(t) ', rep:' num2str(r) ')']; + % No warning + msg = 'Relax'; + set(handles.t_msg,'String',msg); + pause(1); + end + + % Warning to the user + fileName = ['Img/' char(mov) '.jpg']; + if ~exist(fileName,'file') + fileName = 'Img/relax.jpg'; + end + % Display image + movI = importdata(fileName); % Import Image + set(handles.a_pic,'Visible','on'); % Turn on visibility + pic = image(movI,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + + % Init required for patRec of the selected movement + nTW = 1; % Number of time windows + fpTW = 0; % Time window of the first prediction + nCorrMov = 0; % Number of corect movements + predictions = {}; % Vector with the classifier prediction per tw + selTime = NaN; % Selection time + selTimeTW = NaN; % nTW when the selection time happen + compTime = NaN; % Completion time + compTimeTW = NaN; % nTW when the completion time happen + + nCorrMov1 = 0; % Number of corect movements of at least 1 mov + selTime1 = NaN; % Selection time of at least 1 expected mov + selTime1TW = NaN; % nTW when the selection time happen + compTime1 = NaN; % Completion time + compTime1TW = NaN; % nTW when the completion time happen + + selTimeTic = []; % Resets the time of first movement + procTime = []; % Processing time (vector) + dataTW = []; % Reset the dataTW (matrix) + tempData = []; % Reset tempData if it is the first call + + % Ask the user to execute movement + set(handles.t_msg,'String',mov); + drawnow; + + + % Move the VRE into place. + for i = 1:length(movementObjects) + movementObject = movementObjects(i); + if trainUsingVre + % Send values to VRE to move hand into position. + for j = 1:15 %Move it 15 times, distance of 5 each time. + VREActivation(handles.vre_Com,5,[],movementObject.idVRE, movementObject.vreDir, 0); + end + end + end + + + cData = zeros(tWs,nCh); + if strcmp (ComPortType, 'NI') + + % Init SBI + sCh = 1:nCh; + s = InitSBI_NI(sF,sT,sCh); + s.NotifyWhenDataAvailableExceeds = tWs; % PEEK time + lh = s.addlistener('DataAvailable', @MotionTest_OneShot); + + % Start DAQ + s.startBackground(); % Run in the backgroud + + if ~s.IsDone % check if is done + s.wait(); + end + delete(lh); + + %%%%% Motion Test with other custom device %%%%% + else + + % Prepare handles for next function calls + handles.deviceName = deviceName; + handles.ComPortType = ComPortType; + if strcmp (ComPortType, 'COM') + handles.ComPortName = patRec.comn; + end + handles.sF = sF; + handles.sT = sT; + handles.nCh = nCh; + handles.sTall = sT; + + % Connect the chosen device, it returns the connection object + obj = ConnectDevice(handles); + + % Set the selected device and Start the acquisition + SetDeviceStartAcquisition(handles, obj); + + for timeWindowNr = 1:sT/tW + + cData = Acquire_tWs(deviceName, obj, nCh, tWs); % acquire a new time window of samples + acquireEvent.Data = cData; + MotionTest_OneShot(0, acquireEvent); + end + + % Stop acquisition + StopAcquisition(deviceName, obj); + end + + + %Reset the VRE. + if trainUsingVre + ResetVRE(handles.vre_Com,1,1); + end + + %% Save data + test.mov = mov; + test.nTW = nTW-1; + test.fpTW = fpTW; + test.nCorrMov = nCorrMov; + test.predictions = predictions; + test.selTime = selTime; + test.selTimeTW = selTimeTW; + test.compTime = compTime; + test.compTimeTW = compTimeTW; + % Performance for at least 1 movement + test.nCorrMov1 = nCorrMov1; + test.selTime1 = selTime1; + test.selTime1TW = selTime1TW; + test.compTime1 = compTime1; + test.compTime1TW = compTime1TW; + % Data + test.dataTW = dataTW; + test.procTime = mean(procTime); + + % Test fail? + if isnan(compTime) + test.fail = 1; + % Test failed even for 1 movement simul? + if isnan(compTime1) + test.fail1 = 1; + else + test.fail1 = 0; + end + else + test.fail = 0; + test.fail1 = 0; + end + + % Compute accuracy between fpTw to achivement of compTime + if test.fail + % Consider the accuracy during fail trials + %corrP = sum(predictions == mIdx(m)); + %test.acc = corrP / size(predictions,2); + + %Don't acount for acc in failed trials + test.acc = NaN; + + else + corrP = 0; + for i = fpTW : compTimeTW+fpTW-1 + if isequal(predictions{i},patRec.movOutIdx{mIdx(m)}') + corrP = corrP +1; + end + end + %corrP = sum(predictions{fpTW:fpTW+compTimeTW-1} == mIdx(m)); %It doesn't work for simultaneous prediction + test.acc = corrP / compTimeTW; + end + + disp(test); + motionTest.test(r,mIdx(m),t) = test; + delete(pic); + set(handles.t_msgMT,'Visible','off'); + drawnow; + end + end +end + +if trainUsingVre + handles = DisconnectVRE(handles); +end + +% Saved any modification to patRec +motionTest.patRec = patRec; + +pause off; + +% show results +motionTest = MotionTestResults(motionTest); + +% Save test +disp(motionTest); +[filename, pathname] = uiputfile({'*.mat','MAT-files (*.mat)'},'Save as', 'Untitled.mat'); + if isequal(filename,0) || isequal(pathname,0) + disp('User pressed cancel') + else + save([pathname,filename],'motionTest'); + end + +% Clear key variables +clear mIdx; +clear m; +end + + +function MotionTest_OneShot(src,event) + + % General + global patRec; + global handles; + global nTW; % Number of time windows evaluated + global fpTW; % Time window of the first prediction + global dataTW; % Raw data from each time windows + global tempData; + global motorCoupling; + global vreCoupling; + global thresholdGUIData; + + % Time markers + global selTimeTic; + % Key performance indicators + global procTime; + global nCorrMov; + global selTime; % Selection time + global selTimeTW; + global compTime; % Completion time + global compTimeTW; + global nwCompTime; % Number of time windows to consider completion time + global predictions; + % Key performance indicators for at least one movement + global nCorrMov1; % Number of at least 1 correct number + global selTime1; % Selection time (selected = at leas 1 expected mov) + global selTime1TW; + global compTime1; % Completion time (selected = at leas 1 expected mov) + global compTime1TW; + % Variables to keep track of testing movement + global mIdx; + global m; + + + tempData = [tempData; event.Data]; % Record all signal + + % Is there enough data for a TW? + if size(tempData,1) >= (patRec.sF*patRec.tW) + + % Save the TW data, maybe useful for further analysis + tData = tempData(end-patRec.sF*patRec.tW+1:end,:); %Copy the temporal data to the test data + dataTW(:,:,nTW) = tData; % Save data for future analisys + + % Start of processing time + procTimeTic = tic; + + % General routine for RealtimePatRec + [outMov outVector patRec handles] = OneShotRealtimePatRec(tData, patRec, handles, thresholdGUIData); + + % Finish of processing time + procTime(nTW) = toc(procTimeTic); + + % Save the classifier prediction for statistics + predictions{nTW} = outMov; + + % Is there a prediction different than "rest"? + % This conditional considers that there is always a rest movement, + % and this has the last index, therefore rest Indx = patRec.nOuts + if isempty(selTimeTic) + % Compare each outMov to rest +% maskRest(1:size(outMov)) = patRec.nM; + maskRest(1:size(outMov)) = patRec.nOuts; + if sum(outMov ~= maskRest') + selTimeTic = tic; % Starts counting time for selection and completion + fpTW = nTW; % Consider the TW of the first moment + end + end + + % Is the output the expected classes? + if isequal(patRec.movOutIdx{mIdx(m)}, outMov') + nCorrMov = nCorrMov + 1; + if nCorrMov == 1; + selTime = toc(selTimeTic)+patRec.tW+procTime(fpTW); + selTimeTW = nTW-fpTW+1; + elseif nCorrMov == nwCompTime; + compTime = toc(selTimeTic)+patRec.tW+procTime(fpTW); + compTimeTW = nTW-fpTW+1; + set(handles.t_msgMT,'Visible','on'); + drawnow; + end + else % At least one movement is correct? + for i = 1 : size(outMov,1) + if find(patRec.movOutIdx{mIdx(m)} == outMov(i)) + nCorrMov1 = nCorrMov1 + 1; + if nCorrMov1 == 1; + selTime1 = toc(selTimeTic)+patRec.tW+procTime(fpTW); + selTime1TW = nTW-fpTW+1; + elseif nCorrMov1 == nwCompTime; + compTime1 = toc(selTimeTic)+patRec.tW+procTime(fpTW); + compTime1TW = nTW-fpTW+1; + end + break; % Break the for + end + end + end + +% if ~isnan(compTime) +% src.stop(); +% pause(1); +% end + % Next cycle + nTW = nTW + 1; + end + + end + diff --git a/PatRec/MotionTestResults.m b/PatRec/MotionTestResults.m index bc7cecb..82ec8a7 100644 --- a/PatRec/MotionTestResults.m +++ b/PatRec/MotionTestResults.m @@ -1,118 +1,118 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to print the results of the motion test -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-04-02 / Max Ortiz / Creation -% 2012-05-30 / Max Ortiz / Modified to return motionTest with values -% 20xx-xx-xx / Author / Comment on update - -function motionTest = MotionTestResults(motionTest) - -nSucct = zeros(motionTest.patRec.nM-1,1); - -% General matrix -for t = 1 : motionTest.trails - for m = 1 : motionTest.patRec.nM - 1 - for r = 1 : motionTest.nR - selTime(r+(motionTest.nR*(t-1)),m) = motionTest.test(r,m,t).selTime; - selTimeTW(r+(motionTest.nR*(t-1)),m) = motionTest.test(r,m,t).selTimeTW; - compTime(r+(motionTest.nR*(t-1)),m) = motionTest.test(r,m,t).compTime; - compTimeTW(r+(motionTest.nR*(t-1)),m) = motionTest.test(r,m,t).compTimeTW; - acc(r+(motionTest.nR*(t-1)),m) = motionTest.test(r,m,t).acc; - - % Check the numer of succesful trails - if ~motionTest.test(r,m,t).fail - nSucct(m) = nSucct(m) + 1; % Number of succesful trials - end - end - end -end - -compRate = nSucct ./ (motionTest.nR*motionTest.trails); - - -%% Add a mean -compRate(end+1) = mean(compRate); -selTime(:,end+1) = nanmean(selTime,2); -compTime(:,end+1) = nanmean(compTime,2); -acc(:,end+1) = nanmean(acc,2); - -%% Save results - -motionTest.compRate = compRate; -motionTest.selTime = selTime; -motionTest.selTimeTW = selTimeTW; -motionTest.compTime = compTime; -motionTest.compTimeTW = compTimeTW; -motionTest.acc = acc; - -%% plot and save results -% Selection Time -figure(); -boxplot(selTime,'plotstyle','compact') -hold on; -plot(nanmean(selTime),'r*'); -title('Selection Time'); -xlabel('Movements (mean @ end)'); -ylabel('Seconds'); - -% Selection Time - Time Window -figure(); -boxplot(selTimeTW,'plotstyle','compact') -hold on; -plot(nanmean(selTimeTW),'r*'); -title('Selection Time - TW'); -xlabel('Movements'); -ylabel('Number of Time Windows'); - -% Completion Time -figure(); -boxplot(compTime,'plotstyle','compact') -hold on; -plot(nanmean(compTime),'r*'); -title('Completion Time'); -xlabel('Movements (mean @ end)'); -ylabel('Seconds'); - -% Completion Time - Time Window -figure(); -boxplot(compTimeTW,'plotstyle','compact') -hold on; -plot(nanmean(compTimeTW),'r*'); -title('Completion Time - TW'); -xlabel('Movements'); -ylabel('Number of Time Windows'); - -% Completion rate -figure(); -plot(compRate,'r-*'); -title('Completion rate'); -xlabel('Movements (mean @ end)'); -ylabel('% of completion'); - -% Accuracy -figure(); -boxplot(acc,'plotstyle','compact') -hold on; -plot(nanmean(acc),'r*'); -title('Prediction accuracy on completed motions'); -xlabel('Movements'); -ylabel('Accuracy'); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to print the results of the motion test +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-04-02 / Max Ortiz / Creation +% 2012-05-30 / Max Ortiz / Modified to return motionTest with values +% 20xx-xx-xx / Author / Comment on update + +function motionTest = MotionTestResults(motionTest) + +nSucct = zeros(motionTest.patRec.nM-1,1); + +% General matrix +for t = 1 : motionTest.trails + for m = 1 : motionTest.patRec.nM - 1 + for r = 1 : motionTest.nR + selTime(r+(motionTest.nR*(t-1)),m) = motionTest.test(r,m,t).selTime; + selTimeTW(r+(motionTest.nR*(t-1)),m) = motionTest.test(r,m,t).selTimeTW; + compTime(r+(motionTest.nR*(t-1)),m) = motionTest.test(r,m,t).compTime; + compTimeTW(r+(motionTest.nR*(t-1)),m) = motionTest.test(r,m,t).compTimeTW; + acc(r+(motionTest.nR*(t-1)),m) = motionTest.test(r,m,t).acc; + + % Check the numer of succesful trails + if ~motionTest.test(r,m,t).fail + nSucct(m) = nSucct(m) + 1; % Number of succesful trials + end + end + end +end + +compRate = nSucct ./ (motionTest.nR*motionTest.trails); + + +%% Add a mean +compRate(end+1) = mean(compRate); +selTime(:,end+1) = nanmean(selTime,2); +compTime(:,end+1) = nanmean(compTime,2); +acc(:,end+1) = nanmean(acc,2); + +%% Save results + +motionTest.compRate = compRate; +motionTest.selTime = selTime; +motionTest.selTimeTW = selTimeTW; +motionTest.compTime = compTime; +motionTest.compTimeTW = compTimeTW; +motionTest.acc = acc; + +%% plot and save results +% Selection Time +figure(); +boxplot(selTime,'plotstyle','compact') +hold on; +plot(nanmean(selTime),'r*'); +title('Selection Time'); +xlabel('Movements (mean @ end)'); +ylabel('Seconds'); + +% Selection Time - Time Window +figure(); +boxplot(selTimeTW,'plotstyle','compact') +hold on; +plot(nanmean(selTimeTW),'r*'); +title('Selection Time - TW'); +xlabel('Movements'); +ylabel('Number of Time Windows'); + +% Completion Time +figure(); +boxplot(compTime,'plotstyle','compact') +hold on; +plot(nanmean(compTime),'r*'); +title('Completion Time'); +xlabel('Movements (mean @ end)'); +ylabel('Seconds'); + +% Completion Time - Time Window +figure(); +boxplot(compTimeTW,'plotstyle','compact') +hold on; +plot(nanmean(compTimeTW),'r*'); +title('Completion Time - TW'); +xlabel('Movements'); +ylabel('Number of Time Windows'); + +% Completion rate +figure(); +plot(compRate,'r-*'); +title('Completion rate'); +xlabel('Movements (mean @ end)'); +ylabel('% of completion'); + +% Accuracy +figure(); +boxplot(acc,'plotstyle','compact') +hold on; +plot(nanmean(acc),'r*'); +title('Prediction accuracy on completed motions'); +xlabel('Movements'); +ylabel('Accuracy'); + diff --git a/PatRec/MotionTest_Legacy.m b/PatRec/MotionTest_Legacy.m index 1053983..3d0b915 100644 --- a/PatRec/MotionTest_Legacy.m +++ b/PatRec/MotionTest_Legacy.m @@ -1,164 +1,164 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Motion Test for to evaluate real-time performance of the patRec -% -% --------------------------Updates-------------------------- -% 2011-08-02 / Max Ortiz / Creation - - -function motionTest = MotionTest_Legacy(patRec, handles) - -trials = str2double(get(handles.et_trials,'String')); -nR = str2double(get(handles.et_nR,'String')); -timeOut = str2double(get(handles.et_timeOut,'String')); -pause on; - -%% Init motionTest structure -motionTest.sF = patRec.sF; -motionTest.tW = patRec.tW; -motionTest.trails = trials; -motionTest.nR = nR; -motionTest.timeOut = timeOut; - - -%% Initialize DAQ card -% Note: A function for DAQ selection will be required when more cards are -% added - -if strcmp(patRec.dev,'ADH') - -else % at this poin everything else is the NI - USB6009 - chAI = zeros(1,8); - chAI(patRec.nCh) = 1; - ai = InitNI(patRec.sF,timeOut,chAI); -end - -%% Motion Test -% Note: Probabily this way of testing only works for the NI -% specific funtions per card might be required. - -% run over all the trails -for t = 1 : trials - - % Randomize the aperance of the requested movement - mIdx = randperm(patRec.nM); - - % Run over all movements - for m = 1 : patRec.nM - - %Select movement - mov = patRec.mov{mIdx(m)}; - %Prepare - for i = 1 : 3; - msg = ['Relax and prepare to "' mov '" in: ' num2str(4-i) ' seconds (trial:' num2str(t) ')' ]; - set(handles.t_msg,'String',msg); - pause(1); - end - - % Init required for patRec of the selected movement - nTW = 1; % Number of time windows - nCorrMov = 0; % Number of corect movements - selTime = inf; % Selection time - selTimeTW = inf; % nTW when the selection time happen - compTime = inf; % Completion time - compTimeTW = inf; % nTW when the completion time happen - clear 'dataTW'; - - - % Execute movement - set(handles.t_msg,'String',mov); - drawnow; - start(ai); % Start the data acquision for timeOut seconds - selTimeS = tic; - - % Wait until the first samples are aquired - while ai.SamplesAcquired < patRec.sF*patRec.tW - end - % start DAQ - while ai.SamplesAcquired < patRec.sF*timeOut - - %Data Peek - data = peekdata(ai,patRec.sF*patRec.tW); - dataTW(:,:,nTW) = data; % Save data for future analisys - - %Signal processing - tSet = SignalProcessing_RealtimePatRec(data, patRec); - - % One shoot PatRec - [selMov outVector] = OneShotPatRecClassifier(patRec, tSet); - -% % Algorithm selection -% if strcmp(patRec.algorithm,'ANN'); -% -% elseif strcmp(patRec.algorithm,'DA') -% selMov = DiscriminantTest(patRec.coeff, tSet, patRec.training); -% end - - % Check the selected movement - if mIdx(m) == selMov - nCorrMov = nCorrMov + 1; - if nCorrMov == 1; - selTime = toc(selTimeS); - selTimeTW = nTW; - elseif nCorrMov == 10; - compTime = toc(selTimeS); - compTimeTW = nTW; - end - end - - % Show results - set(handles.lb_movements,'Value',selMov); - drawnow; - - % Next cycle - nTW = nTW + 1; - - end - % Finish current movement - test.mov = mov; - test.nTW = nTW-1; - test.nCorrMov = nCorrMov; - test.selTime = selTime; - test.selTimeTW = selTimeTW; - test.compTime = compTime; - test.compTimeTW = compTimeTW; - test.dataTW = dataTW; - if compTime == inf - test.fail = 1; - else - test.fail = 0; - end - disp(test); - motionTest.test(t,mIdx(m)) = test; - stop(ai); - end -end - -delete(ai); -pause off; -disp(motionTest); - -[filename, pathname] = uiputfile({'*.mat','MAT-files (*.mat)'},'Save as', 'Untitled.mat'); - if isequal(filename,0) || isequal(pathname,0) - disp('User pressed cancel') - else - save([pathname,filename],'motionTest'); - end - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Motion Test for to evaluate real-time performance of the patRec +% +% --------------------------Updates-------------------------- +% 2011-08-02 / Max Ortiz / Creation + + +function motionTest = MotionTest_Legacy(patRec, handles) + +trials = str2double(get(handles.et_trials,'String')); +nR = str2double(get(handles.et_nR,'String')); +timeOut = str2double(get(handles.et_timeOut,'String')); +pause on; + +%% Init motionTest structure +motionTest.sF = patRec.sF; +motionTest.tW = patRec.tW; +motionTest.trails = trials; +motionTest.nR = nR; +motionTest.timeOut = timeOut; + + +%% Initialize DAQ card +% Note: A function for DAQ selection will be required when more cards are +% added + +if strcmp(patRec.dev,'ADH') + +else % at this poin everything else is the NI - USB6009 + chAI = zeros(1,8); + chAI(patRec.nCh) = 1; + ai = InitNI(patRec.sF,timeOut,chAI); +end + +%% Motion Test +% Note: Probabily this way of testing only works for the NI +% specific funtions per card might be required. + +% run over all the trails +for t = 1 : trials + + % Randomize the aperance of the requested movement + mIdx = randperm(patRec.nM); + + % Run over all movements + for m = 1 : patRec.nM + + %Select movement + mov = patRec.mov{mIdx(m)}; + %Prepare + for i = 1 : 3; + msg = ['Relax and prepare to "' mov '" in: ' num2str(4-i) ' seconds (trial:' num2str(t) ')' ]; + set(handles.t_msg,'String',msg); + pause(1); + end + + % Init required for patRec of the selected movement + nTW = 1; % Number of time windows + nCorrMov = 0; % Number of corect movements + selTime = inf; % Selection time + selTimeTW = inf; % nTW when the selection time happen + compTime = inf; % Completion time + compTimeTW = inf; % nTW when the completion time happen + clear 'dataTW'; + + + % Execute movement + set(handles.t_msg,'String',mov); + drawnow; + start(ai); % Start the data acquision for timeOut seconds + selTimeS = tic; + + % Wait until the first samples are aquired + while ai.SamplesAcquired < patRec.sF*patRec.tW + end + % start DAQ + while ai.SamplesAcquired < patRec.sF*timeOut + + %Data Peek + data = peekdata(ai,patRec.sF*patRec.tW); + dataTW(:,:,nTW) = data; % Save data for future analisys + + %Signal processing + tSet = SignalProcessing_RealtimePatRec(data, patRec); + + % One shoot PatRec + [selMov outVector] = OneShotPatRecClassifier(patRec, tSet); + +% % Algorithm selection +% if strcmp(patRec.algorithm,'ANN'); +% +% elseif strcmp(patRec.algorithm,'DA') +% selMov = DiscriminantTest(patRec.coeff, tSet, patRec.training); +% end + + % Check the selected movement + if mIdx(m) == selMov + nCorrMov = nCorrMov + 1; + if nCorrMov == 1; + selTime = toc(selTimeS); + selTimeTW = nTW; + elseif nCorrMov == 10; + compTime = toc(selTimeS); + compTimeTW = nTW; + end + end + + % Show results + set(handles.lb_movements,'Value',selMov); + drawnow; + + % Next cycle + nTW = nTW + 1; + + end + % Finish current movement + test.mov = mov; + test.nTW = nTW-1; + test.nCorrMov = nCorrMov; + test.selTime = selTime; + test.selTimeTW = selTimeTW; + test.compTime = compTime; + test.compTimeTW = compTimeTW; + test.dataTW = dataTW; + if compTime == inf + test.fail = 1; + else + test.fail = 0; + end + disp(test); + motionTest.test(t,mIdx(m)) = test; + stop(ai); + end +end + +delete(ai); +pause off; +disp(motionTest); + +[filename, pathname] = uiputfile({'*.mat','MAT-files (*.mat)'},'Save as', 'Untitled.mat'); + if isequal(filename,0) || isequal(pathname,0) + disp('User pressed cancel') + else + save([pathname,filename],'motionTest'); + end + + diff --git a/PatRec/Normalization/NormalizeSet.m b/PatRec/Normalization/NormalizeSet.m index fc5cf22..5f520ab 100644 --- a/PatRec/Normalization/NormalizeSet.m +++ b/PatRec/Normalization/NormalizeSet.m @@ -1,57 +1,57 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to normalize the testing set according to how the previously -% saved normalization parameters -% -% ------------------------- Updates & Contributors ------------------------ -% 2011-10-09 / Max Ortiz / Created -% 2012-10-10 / Max Ortiz / Addition of Mean0Std and NormLog -% 20xx-xx-xx / Author / Comment on update - -function tSet = NormalizeSet(tSet, patRec) - - if strcmp(patRec.normSets.type,'None') - - return; - - elseif strcmp(patRec.normSets.type,'Mean0Var1') - - tSet = (tSet - patRec.normSets.nMean) ./ patRec.normSets.nVar; - - elseif strcmp(patRec.normSets.type,'Mean0Std1') - - tSet = (tSet - patRec.normSets.nMean) ./ patRec.normSets.nStd; - - elseif strcmp(patRec.normSets.type,'UnitaryRange') - - tSet = (tSet - patRec.normSets.min) ./ patRec.normSets.range; - - elseif strcmp(patRec.normSets.type,'Midrange0Range2') - - tSet = (tSet - patRec.normSets.midrange) ./ (patRec.normSets.range/2); - - elseif strcmp(patRec.normSets.type,'NormLog') - - tSet = log(abs(tSet - patRec.normSets.min) +1) ; - - else - disp('Error: No normalization method found'); - end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to normalize the testing set according to how the previously +% saved normalization parameters +% +% ------------------------- Updates & Contributors ------------------------ +% 2011-10-09 / Max Ortiz / Created +% 2012-10-10 / Max Ortiz / Addition of Mean0Std and NormLog +% 20xx-xx-xx / Author / Comment on update + +function tSet = NormalizeSet(tSet, patRec) + + if strcmp(patRec.normSets.type,'None') + + return; + + elseif strcmp(patRec.normSets.type,'Mean0Var1') + + tSet = (tSet - patRec.normSets.nMean) ./ patRec.normSets.nVar; + + elseif strcmp(patRec.normSets.type,'Mean0Std1') + + tSet = (tSet - patRec.normSets.nMean) ./ patRec.normSets.nStd; + + elseif strcmp(patRec.normSets.type,'UnitaryRange') + + tSet = (tSet - patRec.normSets.min) ./ patRec.normSets.range; + + elseif strcmp(patRec.normSets.type,'Midrange0Range2') + + tSet = (tSet - patRec.normSets.midrange) ./ (patRec.normSets.range/2); + + elseif strcmp(patRec.normSets.type,'NormLog') + + tSet = log(abs(tSet - patRec.normSets.min) +1) ; + + else + disp('Error: No normalization method found'); + end + end \ No newline at end of file diff --git a/PatRec/Normalization/NormalizeSets_Mean0Std1.m b/PatRec/Normalization/NormalizeSets_Mean0Std1.m index e3db5d3..9f2952b 100644 --- a/PatRec/Normalization/NormalizeSets_Mean0Std1.m +++ b/PatRec/Normalization/NormalizeSets_Mean0Std1.m @@ -1,37 +1,37 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------- Function Description ------------- -% Normalization to zero mean and unit variance -% Requires test and validation set -% -% ------------- Updates ------------- -% 2012-10-10 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function [trSet vSet mn st] = NormalizeSets_Mean0Std1(trSet, vSet) - - %Get mean - mn = mean(trSet,1); - %Ger variance - st = std(trSet); - % Normalize - trSet = (trSet - mn(ones(size(trSet,1), 1), :)) ./ st(ones(size(trSet,1), 1), :); - vSet = (vSet - mn(ones(size(vSet, 1), 1), :)) ./ st(ones(size(vSet, 1), 1), :); - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------- Function Description ------------- +% Normalization to zero mean and unit standard deviation +% Requires test and validation set +% +% ------------- Updates ------------- +% 2012-10-10 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function [trSet vSet mn st] = NormalizeSets_Mean0Std1(trSet, vSet) + + %Get mean + mn = mean(trSet,1); + %Ger variance + st = std(trSet); + % Normalize + trSet = (trSet - mn(ones(size(trSet,1), 1), :)) ./ st(ones(size(trSet,1), 1), :); + vSet = (vSet - mn(ones(size(vSet, 1), 1), :)) ./ st(ones(size(vSet, 1), 1), :); + + \ No newline at end of file diff --git a/PatRec/Normalization/NormalizeSets_Mean0Var1.m b/PatRec/Normalization/NormalizeSets_Mean0Var1.m index 8e857b9..7b9f118 100644 --- a/PatRec/Normalization/NormalizeSets_Mean0Var1.m +++ b/PatRec/Normalization/NormalizeSets_Mean0Var1.m @@ -1,50 +1,50 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------- Function Description ------------- -% Normalization to zero mean and unit variance -% Requires test and validation set -% -% ------------- Updates ------------- -% 2011-10-06 / Max Ortiz / Creation (maxo@chalmers.se) -% 2011-10-10 / Max Ortiz / Normalization paramenters only taken from trSet - -function [trSet vSet mn vr] = NormalizeSets_Mean0Var1(trSet, vSet) - - %Get mean - mn = mean(trSet,1); - %Ger variance - vr = var(trSet); - % Normalize - trSet = (trSet - mn(ones(size(trSet,1), 1), :)) ./ vr(ones(size(trSet,1), 1), :); - vSet = (vSet - mn(ones(size(vSet, 1), 1), :)) ./ vr(ones(size(vSet, 1), 1), :); - - -%% Old implementation where Tr and V sets where normalized together -% %put together all the sets -% allsets = trset; -% allsets(length(trset(:,1))+1 : length(trset(:,1))+length(vset(:,1)),:) = vset; -% % get mean -% mn = mean(allsets,1); -% %get variance -% vr = var(allsets); -% %normalize each set -% trset = (trset - mn(ones(size(trset,1), 1), :)) ./ vr(ones(size(trset,1), 1), :); -% vset = (vset - mn(ones(size(vset, 1), 1), :)) ./ vr(ones(size(vset, 1), 1), :); - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------- Function Description ------------- +% Normalization to zero mean and unit variance +% Requires test and validation set +% +% ------------- Updates ------------- +% 2011-10-06 / Max Ortiz / Creation (maxo@chalmers.se) +% 2011-10-10 / Max Ortiz / Normalization paramenters only taken from trSet + +function [trSet vSet mn vr] = NormalizeSets_Mean0Var1(trSet, vSet) + + %Get mean + mn = mean(trSet,1); + %Ger variance + vr = var(trSet); + % Normalize + trSet = (trSet - mn(ones(size(trSet,1), 1), :)) ./ vr(ones(size(trSet,1), 1), :); + vSet = (vSet - mn(ones(size(vSet, 1), 1), :)) ./ vr(ones(size(vSet, 1), 1), :); + + +%% Old implementation where Tr and V sets where normalized together +% %put together all the sets +% allsets = trset; +% allsets(length(trset(:,1))+1 : length(trset(:,1))+length(vset(:,1)),:) = vset; +% % get mean +% mn = mean(allsets,1); +% %get variance +% vr = var(allsets); +% %normalize each set +% trset = (trset - mn(ones(size(trset,1), 1), :)) ./ vr(ones(size(trset,1), 1), :); +% vset = (vset - mn(ones(size(vset, 1), 1), :)) ./ vr(ones(size(vset, 1), 1), :); + + \ No newline at end of file diff --git a/PatRec/Normalization/NormalizeSets_Midrange0Range2.m b/PatRec/Normalization/NormalizeSets_Midrange0Range2.m index a07638b..35f98f2 100644 --- a/PatRec/Normalization/NormalizeSets_Midrange0Range2.m +++ b/PatRec/Normalization/NormalizeSets_Midrange0Range2.m @@ -1,46 +1,46 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------- Function Description ------------- -% Normalization to zero mean and unit variance -% Requires all available sets -% -% ------------- Updates ------------- -% 2011-10-10 / Max Ortiz / Creation (maxo@chalmers.se) -% 20xx-xx-xx / Author / Comment on update - - -function [trSet vSet mRange mMidrange] = NormalizeSets_Midrange0Range2(trSet, vSet) - - mMin = min(trSet); % matrix min - mMax = max(trSet); %matrix max - mRange = mMax - mMin; - mMidrange = (mMax + mMin)/2; - - % Normalize by using the range which will create a distribution between - % 0 and 1 - for i = 1 : size(trSet,2) - trSet(:,i) = (trSet(:,i) - mMidrange(i)) ./ (mRange(i)/2); - end - - % Normalize the validation set from the information of training set - for i = 1 : size(vSet,2) - vSet(:,i) = (vSet(:,i) - mMidrange(i)) ./ (mRange(i)/2); - end - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------- Function Description ------------- +% Normalization to zero mean and unit variance +% Requires all available sets +% +% ------------- Updates ------------- +% 2011-10-10 / Max Ortiz / Creation (maxo@chalmers.se) +% 20xx-xx-xx / Author / Comment on update + + +function [trSet vSet mRange mMidrange] = NormalizeSets_Midrange0Range2(trSet, vSet) + + mMin = min(trSet); % matrix min + mMax = max(trSet); %matrix max + mRange = mMax - mMin; + mMidrange = (mMax + mMin)/2; + + % Normalize by using the range which will create a distribution between + % 0 and 1 + for i = 1 : size(trSet,2) + trSet(:,i) = (trSet(:,i) - mMidrange(i)) ./ (mRange(i)/2); + end + + % Normalize the validation set from the information of training set + for i = 1 : size(vSet,2) + vSet(:,i) = (vSet(:,i) - mMidrange(i)) ./ (mRange(i)/2); + end + + \ No newline at end of file diff --git a/PatRec/Normalization/NormalizeSets_TrV.m b/PatRec/Normalization/NormalizeSets_TrV.m index e73f2b4..be6ac05 100644 --- a/PatRec/Normalization/NormalizeSets_TrV.m +++ b/PatRec/Normalization/NormalizeSets_TrV.m @@ -1,68 +1,68 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------- Function Description ------------- -% Function to normalize the Trainig and Validation Sets, the testing set -% is normalize according to how the the two latter where normalized -% -% ------------- Updates ------------- -% 2011-10-09 / Max Ortiz / -% 20xx-xx-xx / Author / Comment on update - -function [trSet vSet patRec] = NormalizeSets_TrV(normSetsType, trSet, vSet, patRec) - - if strcmp(normSetsType,'Select Normalization') - normSets.type = 'None'; - - elseif strcmp(normSetsType,'Mean0Var1') - %[trSet vSet tSet mn vr] = NormalizeSets_mean0_var1(trSet, vSet, tSet); - [trSet vSet mn vr] = NormalizeSets_Mean0Var1(trSet, vSet); - normSets.type = normSetsType; - normSets.nMean = mn; - normSets.nVar = vr; - - elseif strcmp(normSetsType,'Mean0Std1') - [trSet vSet mn st] = NormalizeSets_Mean0Std1(trSet, vSet); - normSets.type = normSetsType; - normSets.nMean = mn; - normSets.nStd = st; - - elseif strcmp(normSetsType,'UnitaryRange') - [trSet vSet mRange mMin] = NormalizeSets_UnitaryRange(trSet, vSet); - normSets.type = normSetsType; - normSets.min = mMin; - normSets.range = mRange; - - elseif strcmp(normSetsType,'Midrange0Range2') - [trSet vSet mRange mMidrange] = NormalizeSets_Midrange0Range2(trSet, vSet); - normSets.type = normSetsType; - normSets.midrange = mMidrange; - normSets.range = mRange; - - elseif strcmp(normSetsType,'NormLog') - [trSet vSet mMin] = NormalizeSets_normLog(trSet, vSet); - normSets.type = normSetsType; - normSets.min = mMin; - - else - - disp('No normalization method found'); - - end - patRec.normSets = normSets; - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------- Function Description ------------- +% Function to normalize the Trainig and Validation Sets, the testing set +% is normalize according to how the the two latter where normalized +% +% ------------- Updates ------------- +% 2011-10-09 / Max Ortiz / +% 20xx-xx-xx / Author / Comment on update + +function [trSet vSet patRec] = NormalizeSets_TrV(normSetsType, trSet, vSet, patRec) + + if strcmp(normSetsType,'Select Normalization') + normSets.type = 'None'; + + elseif strcmp(normSetsType,'Mean0Var1') + %[trSet vSet tSet mn vr] = NormalizeSets_mean0_var1(trSet, vSet, tSet); + [trSet vSet mn vr] = NormalizeSets_Mean0Var1(trSet, vSet); + normSets.type = normSetsType; + normSets.nMean = mn; + normSets.nVar = vr; + + elseif strcmp(normSetsType,'Mean0Std1') + [trSet vSet mn st] = NormalizeSets_Mean0Std1(trSet, vSet); + normSets.type = normSetsType; + normSets.nMean = mn; + normSets.nStd = st; + + elseif strcmp(normSetsType,'UnitaryRange') + [trSet vSet mRange mMin] = NormalizeSets_UnitaryRange(trSet, vSet); + normSets.type = normSetsType; + normSets.min = mMin; + normSets.range = mRange; + + elseif strcmp(normSetsType,'Midrange0Range2') + [trSet vSet mRange mMidrange] = NormalizeSets_Midrange0Range2(trSet, vSet); + normSets.type = normSetsType; + normSets.midrange = mMidrange; + normSets.range = mRange; + + elseif strcmp(normSetsType,'NormLog') + [trSet vSet mMin] = NormalizeSets_normLog(trSet, vSet); + normSets.type = normSetsType; + normSets.min = mMin; + + else + + disp('No normalization method found'); + + end + patRec.normSets = normSets; + end \ No newline at end of file diff --git a/PatRec/Normalization/NormalizeSets_UnitaryRange.m b/PatRec/Normalization/NormalizeSets_UnitaryRange.m index cb083ae..d90565b 100644 --- a/PatRec/Normalization/NormalizeSets_UnitaryRange.m +++ b/PatRec/Normalization/NormalizeSets_UnitaryRange.m @@ -1,73 +1,73 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------- Function Description ------------- -% Normalization to zero mean and unit variance -% Requires all available sets -% -% ------------- Updates ------------- -% 2011-09-29 / Max Ortiz / Creation -% 2011-10-10 / Max Ortiz / Normalization paramenters only taken from trSet -% 20xx-xx-xx / Author / Comment on update - -function [trSet vSet mRange mMin] = NormalizeSets_UnitaryRange(trSet, vSet) - - tolerance = 0.0000001; - tempSet = trSet; - mMin = min(tempSet); - mMin = mMin - tolerance; %Quick patch for connMat - mMax = max(tempSet) + tolerance; - mRange = mMax - mMin; - - % Normalize by using the range which will create a distribution between - % 0 and 1 - for i = 1 : size(trSet,2) - trSet(:,i) = (tempSet(:,i) - mMin(i)) ./ mRange(i); - end - - % Normalize the validation set from the information of training set - for i = 1 : size(vSet,2) - vSet(:,i) = (vSet(:,i) - mMin(i)) ./ mRange(i); - vSet(find(vSet(:,i) < 0),i) = tolerance; - end - - - -%% Old implementation where Tr and V sets where normalized together -% %put together all the sets -% allSets = trSet; -% allSets(length(trSet(:,1))+1 : length(trSet(:,1))+length(vSet(:,1)),:) = vSet; -% %allSets(length(trSet(:,1))+length(vSet(:,1))+1 : -% %length(trSet(:,1))+length(vSet(:,1))+length(tSet(:,1)),:) = tSet; -% -% mMin = min(allSets); -% mMin = mMin + 0.0000001; %Quick patch for connMat -% mMax = max(allSets); -% mRange = mMax - mMin; -% -% % Normalize by using the range which will create a distribution between -% % 0 and 1 -% for i = 1 : size(allSets,2) -% allSets(:,i) = (allSets(:,i) - mMin(i)) ./ mRange(i); -% end -% -% trSet = allSets(1:size(trSet,1),:); -% vSet = allSets(length(trSet(:,1))+1 : length(trSet(:,1))+length(vSet(:,1)),:); -% %tSet = allSets(length(trSet(:,1))+length(vSet(:,1))+1 : length(trSet(:,1))+length(vSet(:,1))+length(tSet(:,1)),:); -% -% +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------- Function Description ------------- +% Normalization between 0 and 1 +% Requires all available sets +% +% ------------- Updates ------------- +% 2011-09-29 / Max Ortiz / Creation +% 2011-10-10 / Max Ortiz / Normalization paramenters only taken from trSet +% 20xx-xx-xx / Author / Comment on update + +function [trSet vSet mRange mMin] = NormalizeSets_UnitaryRange(trSet, vSet) + + tolerance = 0.0000001; + tempSet = trSet; + mMin = min(tempSet); + mMin = mMin - tolerance; %Quick patch for connMat + mMax = max(tempSet) + tolerance; + mRange = mMax - mMin; + + % Normalize by using the range which will create a distribution between + % 0 and 1 + for i = 1 : size(trSet,2) + trSet(:,i) = (tempSet(:,i) - mMin(i)) ./ mRange(i); + end + + % Normalize the validation set from the information of training set + for i = 1 : size(vSet,2) + vSet(:,i) = (vSet(:,i) - mMin(i)) ./ mRange(i); + vSet(find(vSet(:,i) < 0),i) = tolerance; + end + + + +%% Old implementation where Tr and V sets where normalized together +% %put together all the sets +% allSets = trSet; +% allSets(length(trSet(:,1))+1 : length(trSet(:,1))+length(vSet(:,1)),:) = vSet; +% %allSets(length(trSet(:,1))+length(vSet(:,1))+1 : +% %length(trSet(:,1))+length(vSet(:,1))+length(tSet(:,1)),:) = tSet; +% +% mMin = min(allSets); +% mMin = mMin + 0.0000001; %Quick patch for connMat +% mMax = max(allSets); +% mRange = mMax - mMin; +% +% % Normalize by using the range which will create a distribution between +% % 0 and 1 +% for i = 1 : size(allSets,2) +% allSets(:,i) = (allSets(:,i) - mMin(i)) ./ mRange(i); +% end +% +% trSet = allSets(1:size(trSet,1),:); +% vSet = allSets(length(trSet(:,1))+1 : length(trSet(:,1))+length(vSet(:,1)),:); +% %tSet = allSets(length(trSet(:,1))+length(vSet(:,1))+1 : length(trSet(:,1))+length(vSet(:,1))+length(tSet(:,1)),:); +% +% % \ No newline at end of file diff --git a/PatRec/Normalization/NormalizeSets_normLog.m b/PatRec/Normalization/NormalizeSets_normLog.m index d24507c..0428538 100644 --- a/PatRec/Normalization/NormalizeSets_normLog.m +++ b/PatRec/Normalization/NormalizeSets_normLog.m @@ -1,34 +1,37 @@ - -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -------------------------- Function Description ------------------------- -% logarithm Normalization -% -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function [trSet vSet mMin]= NormalizeSets_normLog(trSet, vSet) -mMin =min(trSet); - - -trSet = log(abs(trSet - repmat(mMin,size(trSet,1),[])) +1); - -vSet = log(abs(vSet - repmat(mMin,size(vSet ,1),[])) +1); - + +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% -------------------------- Function Description ------------------------- +% logarithm Normalization +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 2014-12-04 / Ali Fouad / Fixed warning message due to empty set + +function [trSet, vSet, mMin]= NormalizeSets_normLog(trSet, vSet) +mMin =min(trSet); + +trSet = log(abs(trSet - repmat(mMin,size(trSet,1),1)) +1); + +vSet = log(abs(vSet - repmat(mMin,size(vSet ,1),1)) +1); + +% the following code produce a warning after Matlab 13a +%trSet = log(abs(trSet - repmat(mMin,size(trSet,1),[])) +1); +%vSet = log(abs(vSet - repmat(mMin,size(vSet ,1),[])) +1); + diff --git a/PatRec/OfflinePatRec.m b/PatRec/OfflinePatRec.m index c510f88..87c5cd1 100644 --- a/PatRec/OfflinePatRec.m +++ b/PatRec/OfflinePatRec.m @@ -1,205 +1,230 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to execute the Offline traning once selected from the GUI in -% Matlab -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-07-26 / Max Ortiz / Creation -% 2011-10-03 / Max Ortiz / Addition of individual movements routine -% 2011-11-29 / Max Ortiz / Addition of the posibility different topologies -% 2012-10-10 / Max Ortiz / Addition of algConf -% 2012-11-02 / Max Ortiz / Addition of floor noise -% 2012-11-23 / Joel Falk-Dahlin / Added initialization of speeds -% 20xx-xx-xx / Author / Comment on update - -function patRec = OfflinePatRec(sigFeatures, selFeatures, randFeatures, normSetsType, alg, tType, algConf, movMix, topology, confMatFlag) - - %% patRec structure initialization - - patRec.nM = size(sigFeatures.mov,1); - patRec.mov = sigFeatures.mov; - patRec.sF = sigFeatures.sF; - patRec.tW = sigFeatures.tW; - patRec.nCh = sigFeatures.nCh; - patRec.dev = sigFeatures.dev; - patRec.fFilter = sigFeatures.fFilter; - patRec.sFilter = sigFeatures.sFilter; - patRec.selFeatures = selFeatures; - patRec.topology = topology; - - %% Start counting the training time - trStart = tic; - - %% Randomize data (if requested) - if randFeatures - sigFeatures = Rand_sigFeatures(sigFeatures); - end - - %% Get data sets - - if strcmp(movMix, 'All Mov') - [trSets, trOuts, vSets, vOuts, tSets, tOuts] = GetSets_Stack(sigFeatures, selFeatures); - movIdx = 1:patRec.nM; - movOutIdx = num2cell(movIdx); - elseif strcmp(movMix, 'Individual Mov') - - [trSets, trOuts, vSets, vOuts, tSets, tOuts, movIdx, movOutIdx] = GetSets_Stack_IndvMov(sigFeatures, selFeatures); - - elseif strcmp(movMix, 'Mixed Output') - - [trSets, trOuts, vSets, vOuts, tSets, tOuts, movIdx, movOutIdx] = GetSets_Stack_MixedOut(sigFeatures, selFeatures); - - end - patRec.movOutIdx = movOutIdx; - movLables = sigFeatures.mov(movIdx); - - - %% Normalize - [trSets vSets patRec] = NormalizeSets_TrV(normSetsType, trSets, vSets, patRec); - - - %% Floor noise - if strcmp(movLables(end),'Rest') - restIdx = find(trOuts(:,end)); % Get Indices of the rest sets - restSets = trSets(restIdx,:); % Get data sets related to rest - restSets = mean(restSets); % Mean for all windows - sCh = 1; % starting channel - for i = 1 : size(patRec.selFeatures,1) - eCh = sCh + size(patRec.nCh,2)-1; - floorNoise(i) = mean(restSets(sCh:eCh)); - sCh = eCh +1; - end - patRec.floorNoise = floorNoise; - end - % Note: The floor noise must be computed after normalization so it can - % be used as a threshold after SignalProcessing_Realtime in the - % real-time testing - - %% Topology and algorithm selection - if strcmp(patRec.topology,'Single Classifier') - - %patRec.patRecTrained = OfflinePatRecTraining(alg, tType, trSets, trOuts, vSets, vOuts, trLables, vLables, movLables); - patRec.patRecTrained = OfflinePatRecTraining(alg, tType, algConf, trSets, trOuts, vSets, vOuts, sigFeatures.mov, movIdx); - - elseif strcmp(patRec.topology,'Ago-antagonist') || ... - strcmp(patRec.topology,'Ago-antagonistAndMixed') - %Compute the number of DoF involved - if strcmp(movLables(end),'Rest') - nMov = size(movIdx,2) - 1; - restIdx = size(movIdx,2); - else - nMov = size(movIdx,2); - restIdx = []; - end - - % Run through all DoF and get mov 1 and 2 from each DoF plus a Mix. - for i = 1 : 2 : nMov - movIdxX = [i i+1]; - disp(['Training DoF:' num2str(round(i/2))]); - - % Train only with the Ago-antagonist movements or also the - % mixed of the remaining movements. - % If the rest movement is present. It would be considered in - % the training of each DoF - if strcmp(patRec.topology,'Ago-antagonist') - [trSetsX, trOutsX, vSetsX, vOutsX] = ... - ExtractSets_Stack(trSets, trOuts, vSets, vOuts, [movIdxX restIdx]); - movIdxX(end+1) = restIdx; - elseif strcmp(patRec.topology,'Ago-antagonistAndMixed') - [trSetsX, trOutsX, vSetsX, vOutsX, movIdxX, movLables] = ... - ExtractSets_Stack_MixRest(trSets, trOuts, vSets, vOuts, [movIdxX restIdx], movLables); - end - - patRec.patRecTrained(round(i/2)) = OfflinePatRecTraining(alg, tType, algConf, trSetsX, trOutsX, vSetsX, vOutsX, movLables, movIdxX); - - end - - elseif strcmp(patRec.topology,'One-vs-All') - % Number of selected movementes, it might be different than patRec.nM - nM = size(movIdx,2); - - % Creat a binary classifiers per movements - for i = 1 : nM - disp(['Training movement:' num2str(i)]); - movIdxX = i; - [trSetsX, trOutsX, vSetsX, vOutsX, movIdxX, movLables] = ExtractSets_Stack_MixRest(trSets, trOuts, vSets, vOuts, movIdxX, movLables); - patRec.patRecTrained(i) = OfflinePatRecTraining(alg, tType, algConf, trSetsX, trOutsX, vSetsX, vOutsX, movLables, movIdxX); - end - - - elseif strcmp(patRec.topology,'One-vs-One') || strcmp(patRec.topology,'One-vs-One DoF') - - nM = size(movIdx,2); % Number of selected movementes, it might be different than patRec.nM - - % Creat a matrix with binary classifiers between each movements - for i = 1 : nM - for j = i+1 : nM - disp(['Training movement:' num2str(i) ' vs ' num2str(j)]); - movIdxX = [i j]; - [trSetsX, trOutsX, vSetsX, vOutsX] = ExtractSets_Stack(trSets, trOuts, vSets, vOuts, movIdxX); - patRec.patRecTrained(i,j) = OfflinePatRecTraining(alg, tType, algConf, trSetsX, trOutsX, vSetsX, vOutsX, movLables, movIdxX); - end - end - - elseif strcmp(patRec.topology,'All-and-One') - - % Number of selected movementes, it might be different than patRec.nM - nM = size(movIdx,2); - % Creat a binary classifiers per movements (One-Vs-All) - for i = 1 : nM - movIdxX = i; - disp(['Training movement:' num2str(i)]); - [trSetsX, trOutsX, vSetsX, vOutsX, movIdxX, movLables] = ExtractSets_Stack_MixRest(trSets, trOuts, vSets, vOuts, movIdxX, movLables); - patRec.patRecTrained(i) = OfflinePatRecTraining(alg, tType, algConf, trSetsX, trOutsX, vSetsX, vOutsX, movLables, movIdxX); - end - - % Creat a matrix with binary classifiers between each movements - % (One-Vs-One) - for i = 1 : nM - for j = i+1 : nM - movIdxX = [i j]; - disp(['Training movement:' num2str(i) ' vs ' num2str(j)]); - [trSetsX, trOutsX, vSetsX, vOutsX] = ExtractSets_Stack(trSets, trOuts, vSets, vOuts, movIdxX); - patRec.patRecTrainedAux(i,j) = OfflinePatRecTraining(alg, tType, algConf, trSetsX, trOutsX, vSetsX, vOutsX, movLables, movIdxX); - end - end - - else - disp('Select topology'); - return; - end - - % Compute training/validation time. - patRec.trTime = toc(trStart); - - %% Test accuracy of the patRec - - [acc confMat tTime] = Accuracy_patRec(patRec, tSets, tOuts, confMatFlag); - patRec.tTime = tTime; - - %% Final data to the patRec - patRec.acc = acc; - patRec.confMat = confMat; - patRec.date = fix(clock); - patRec.indMovIdx = movIdx; - patRec.nOuts = size(movIdx,2); - patRec.control.maxDegPerMov = ones(1,patRec.nOuts); -end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to execute the Offline traning once selected from the GUI in +% Matlab +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-26 / Max Ortiz / Creation +% 2011-10-03 / Max Ortiz / Addition of individual movements routine +% 2011-11-29 / Max Ortiz / Addition of the posibility different topologies +% 2012-10-10 / Max Ortiz / Addition of algConf +% 2012-11-02 / Max Ortiz / Addition of floor noise +% 2012-11-23 / Joel Falk-Dahlin / Added initialization of speeds +% 2013-01-02 / Max Ortiz / Added Feature Reduction variables +% Modified floor noise to compute only the first +% feature +% 2014-12-01 / Enzo Mastinu / Added the handling part for the COM port number + % information into the parameters +% 20xx-xx-xx / Author / Comment on update + +function patRec = OfflinePatRec(sigFeatures, selFeatures, randFeatures, normSetsType, alg, tType, algConf, movMix, topology, confMatFlag, featReducAlg) + + %% patRec structure initialization + + patRec.nM = size(sigFeatures.mov,1); + patRec.mov = sigFeatures.mov; + patRec.sF = sigFeatures.sF; + patRec.tW = sigFeatures.tW; + patRec.nCh = sigFeatures.nCh; + patRec.dev = sigFeatures.dev; + patRec.fFilter = sigFeatures.fFilter; + patRec.sFilter = sigFeatures.sFilter; + patRec.selFeatures = selFeatures; + patRec.topology = topology; + patRec.featureReduction.Alg = featReducAlg; + + if isfield(sigFeatures, 'comm') + patRec.comm = sigFeatures.comm; + if isfield(sigFeatures, 'comn') + patRec.comn = sigFeatures.comn; + end + else + patRec.comm = 'N/A'; + end + + %% Start counting the training time + trStart = tic; + + %% Randomize data (if requested) + if randFeatures + sigFeatures = Rand_sigFeatures(sigFeatures); + end + + %% Get data sets + + if strcmp(movMix, 'All Mov') + [trSets, trOuts, vSets, vOuts, tSets, tOuts] = GetSets_Stack(sigFeatures, selFeatures); + movIdx = 1:patRec.nM; + movOutIdx = num2cell(movIdx); + elseif strcmp(movMix, 'Individual Mov') + + [trSets, trOuts, vSets, vOuts, tSets, tOuts, movIdx, movOutIdx] = GetSets_Stack_IndvMov(sigFeatures, selFeatures); + + elseif strcmp(movMix, 'Mixed Output') + + [trSets, trOuts, vSets, vOuts, tSets, tOuts, movIdx, movOutIdx] = GetSets_Stack_MixedOut(sigFeatures, selFeatures); + + end + patRec.movOutIdx = movOutIdx; + movLables = sigFeatures.mov(movIdx); + + + %% Normalize + [trSets vSets patRec] = NormalizeSets_TrV(normSetsType, trSets, vSets, patRec); + + %% Floor noise + if strcmp(movLables(end),'Rest') + restIdx = find(trOuts(:,end)); % Get Indices of the rest sets + restSets = trSets(restIdx,:); % Get data sets related to rest + restSets = mean(restSets); % Mean for all windows + + % Compute the floor noise of the first feature only + floorNoise = mean(restSets(1:size(patRec.nCh,2))); + + % Loop to compute the floor noise of each feature +% sCh = 1; % starting channel +% for i = 1 : size(patRec.selFeatures,1) +% eCh = sCh + size(patRec.nCh,2)-1; +% floorNoise(i) = mean(restSets(sCh:eCh)); +% sCh = eCh +1; +% end + + % Save floor noise in patRec + patRec.floorNoise = floorNoise; + end + % Note: The floor noise must be computed after normalization so it can + % be used as a threshold after SignalProcessing_Realtime in the + % real-time testing + + %% Feature Reduction + [trSets vSets,patRec]=FeatureReduction(trSets,vSets,patRec); + + %% Topology and algorithm selection + if strcmp(patRec.topology,'Single Classifier') + + %patRec.patRecTrained = OfflinePatRecTraining(alg, tType, trSets, trOuts, vSets, vOuts, trLables, vLables, movLables); + patRec.patRecTrained = OfflinePatRecTraining(alg, tType, algConf, trSets, trOuts, vSets, vOuts, sigFeatures.mov, movIdx); + + elseif strcmp(patRec.topology,'Ago-antagonist') || ... + strcmp(patRec.topology,'Ago-antagonistAndMixed') + %Compute the number of DoF involved + if strcmp(movLables(end),'Rest') + nMov = size(movIdx,2) - 1; + restIdx = size(movIdx,2); + else + nMov = size(movIdx,2); + restIdx = []; + end + + % Run through all DoF and get mov 1 and 2 from each DoF plus a Mix. + for i = 1 : 2 : nMov + movIdxX = [i i+1]; + disp(['Training DoF:' num2str(round(i/2))]); + + % Train only with the Ago-antagonist movements or also the + % mixed of the remaining movements. + % If the rest movement is present. It would be considered in + % the training of each DoF + if strcmp(patRec.topology,'Ago-antagonist') + [trSetsX, trOutsX, vSetsX, vOutsX] = ... + ExtractSets_Stack(trSets, trOuts, vSets, vOuts, [movIdxX restIdx]); + movIdxX(end+1) = restIdx; + elseif strcmp(patRec.topology,'Ago-antagonistAndMixed') + [trSetsX, trOutsX, vSetsX, vOutsX, movIdxX, movLables] = ... + ExtractSets_Stack_MixRest(trSets, trOuts, vSets, vOuts, [movIdxX restIdx], movLables); + end + + patRec.patRecTrained(round(i/2)) = OfflinePatRecTraining(alg, tType, algConf, trSetsX, trOutsX, vSetsX, vOutsX, movLables, movIdxX); + + end + + elseif strcmp(patRec.topology,'One-vs-All') || ... + strcmp(patRec.topology,'One-vs-All Th') + % Number of selected movementes, it might be different than patRec.nM + nM = size(movIdx,2); + + % Creat a binary classifiers per movements + for i = 1 : nM + disp(['Training movement:' num2str(i)]); + movIdxX = i; + [trSetsX, trOutsX, vSetsX, vOutsX, movIdxX, movLables] = ExtractSets_Stack_MixRest(trSets, trOuts, vSets, vOuts, movIdxX, movLables); + patRec.patRecTrained(i) = OfflinePatRecTraining(alg, tType, algConf, trSetsX, trOutsX, vSetsX, vOutsX, movLables, movIdxX); + end + + + elseif strcmp(patRec.topology,'One-vs-One') || strcmp(patRec.topology,'One-vs-One DoF') + + nM = size(movIdx,2); % Number of selected movementes, it might be different than patRec.nM + + % Creat a matrix with binary classifiers between each movements + for i = 1 : nM + for j = i+1 : nM + disp(['Training movement:' num2str(i) ' vs ' num2str(j)]); + movIdxX = [i j]; + [trSetsX, trOutsX, vSetsX, vOutsX] = ExtractSets_Stack(trSets, trOuts, vSets, vOuts, movIdxX); + patRec.patRecTrained(i,j) = OfflinePatRecTraining(alg, tType, algConf, trSetsX, trOutsX, vSetsX, vOutsX, movLables, movIdxX); + end + end + + elseif strcmp(patRec.topology,'All-and-One') + + % Number of selected movementes, it might be different than patRec.nM + nM = size(movIdx,2); + % Creat a binary classifiers per movements (One-Vs-All) + for i = 1 : nM + movIdxX = i; + disp(['Training movement:' num2str(i)]); + [trSetsX, trOutsX, vSetsX, vOutsX, movIdxX, movLables] = ExtractSets_Stack_MixRest(trSets, trOuts, vSets, vOuts, movIdxX, movLables); + patRec.patRecTrained(i) = OfflinePatRecTraining(alg, tType, algConf, trSetsX, trOutsX, vSetsX, vOutsX, movLables, movIdxX); + end + + % Creat a matrix with binary classifiers between each movements + % (One-Vs-One) + for i = 1 : nM + for j = i+1 : nM + movIdxX = [i j]; + disp(['Training movement:' num2str(i) ' vs ' num2str(j)]); + [trSetsX, trOutsX, vSetsX, vOutsX] = ExtractSets_Stack(trSets, trOuts, vSets, vOuts, movIdxX); + patRec.patRecTrainedAux(i,j) = OfflinePatRecTraining(alg, tType, algConf, trSetsX, trOutsX, vSetsX, vOutsX, movLables, movIdxX); + end + end + + else + disp('Select topology'); + return; + end + + % Compute training/validation time. + patRec.trTime = toc(trStart); + + %% Test accuracy of the patRec + + [performance confMat tTime] = Accuracy_patRec(patRec, tSets, tOuts, confMatFlag); + patRec.tTime = tTime; + + %% Final data to the patRec + patRec.performance = performance; + patRec.confMat = confMat; + patRec.date = fix(clock); + patRec.indMovIdx = movIdx; + patRec.nOuts = size(movIdx,2); + patRec.control.maxDegPerMov = ones(1,patRec.nOuts); +end + diff --git a/PatRec/OfflinePatRecTraining.m b/PatRec/OfflinePatRecTraining.m index 2602029..04a3101 100644 --- a/PatRec/OfflinePatRecTraining.m +++ b/PatRec/OfflinePatRecTraining.m @@ -1,103 +1,89 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function indentifies the selected PatRec algorithm and then calls -% its selected training method. It requires data sets: -% -% * Training sets: trSets with trOuts -% * Validation sets: vSets with vOuts -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-11-29 / Max Ortiz / Creation out of OfflinePatRec -% 2012-05-20 / Max Ortiz / Moved the GetSetLables inside -% DiscrimiantAnalysis -% 2012-10-10 / Max Ortiz / Addition of algConf -% 20xx-xx-xx / Author / Comment on update - -function patRec = OfflinePatRecTraining(alg, tType, algConf, trSets, trOuts, vSets, vOuts, mov, movIdx) - % Select and run training - - % MLP - if strcmp(alg,'MLP') - - [MLP accV] = ANN_Perceptron(trSets, trOuts, vSets, vOuts, tType); - patRec.algorithm = 'MLP'; - patRec.training = tType; - patRec.ANN = MLP; - - elseif strcmp(alg,'MLP thOut') - - [MLP accV] = ANN_Perceptron(trSets, trOuts, vSets, vOuts, tType); - patRec.algorithm = 'MLP thOut'; - patRec.training = tType; - patRec.ANN = MLP; - % Temp hard-coded threshold - patRec.thOut(1:size(trOuts,2)) = 0.5; - - % DA - elseif strcmp(alg,'Discriminant A.') - - [coeff accV] = DiscriminantAnalysis(tType, trSets, trOuts, vSets, vOuts, mov, movIdx); - patRec.algorithm = 'DA'; - patRec.training = tType; - patRec.coeff = coeff; - - % RFN - elseif strcmp(alg,'RFN') - [connMat accV] = RegulationFeedback(tType, trSets, trOuts, vSets, vOuts); - patRec.algorithm = 'RFN'; - patRec.training = tType; - patRec.connMat = connMat; - - % RFN threshold output - elseif strcmp(alg,'RFN thOut') - [connMat accV thOut] = RegulationFeedback_thOut(tType, trSets, trOuts, vSets, vOuts); - patRec.algorithm = 'RFN thOut'; - patRec.training = tType; - patRec.connMat = connMat; - % temp fix - thOut(1:size(connMat,1)) = thOut; - patRec.thOut = thOut; - - elseif strcmp(alg,'SOM') - [SOM accV] = SOM_Mapping(trSets, trOuts, vSets, vOuts, tType, algConf); - patRec.algorithm = 'SOM'; - patRec.training = tType; - patRec.SOM=SOM; - - elseif strcmp(alg,'SSOM') - [SSOM accV] = SSOM_Mapping(trSets, trOuts, vSets, vOuts, tType, algConf); - patRec.algorithm = 'SSOM'; - patRec.training = tType; - patRec.SSOM=SSOM; - - elseif strcmp(alg,'KNN') - [KNN accV] = EvaluateKNN(trSets, trOuts, vSets, vOuts, tType); - patRec.algorithm = 'KNN'; - patRec.training = tType; - patRec.KNN=KNN; - - end - - % Save the accuracy of the validation set as part of patRec structure; - patRec.accV = accV; - % Add the algConf variable - patRec.algConf = algConf; - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function indentifies the selected PatRec algorithm and then calls +% its selected training method. It requires data sets: +% +% * Training sets: trSets with trOuts +% * Validation sets: vSets with vOuts +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-11-29 / Max Ortiz / Creation out of OfflinePatRec +% 2012-05-20 / Max Ortiz / Moved the GetSetLables inside +% DiscrimiantAnalysis +% 2012-10-10 / Max Ortiz / Addition of algConf +% 20xx-xx-xx / Author / Comment on update + +function patRec = OfflinePatRecTraining(alg, tType, algConf, trSets, trOuts, vSets, vOuts, mov, movIdx) + % Select and run training + + % MLP + if strcmp(alg,'MLP') + + [MLP accV] = ANN_Perceptron(trSets, trOuts, vSets, vOuts, tType); + patRec.algorithm = 'MLP'; + patRec.training = tType; + patRec.ANN = MLP; + + elseif strcmp(alg,'MLP thOut') + + [MLP accV] = ANN_Perceptron(trSets, trOuts, vSets, vOuts, tType); + patRec.algorithm = 'MLP thOut'; + patRec.training = tType; + patRec.ANN = MLP; + % Temp hard-coded threshold + patRec.thOut(1:size(trOuts,2)) = 0.5; + + % DA + elseif strcmp(alg,'Discriminant A.') + + [coeff accV] = DiscriminantAnalysis(tType, trSets, trOuts, vSets, vOuts, mov, movIdx); + patRec.algorithm = 'DA'; + patRec.training = tType; + patRec.coeff = coeff; + + % RFN + elseif strcmp(alg,'RFN') + [connMat accV] = RegulationFeedback(tType, trSets, trOuts, vSets, vOuts); + patRec.algorithm = 'RFN'; + patRec.training = tType; + patRec.connMat = connMat; + + % SOM + elseif strcmp(alg,'SOM') + [SOM accV] = SOM_Mapping(trSets, trOuts, vSets, vOuts, tType, algConf); + patRec.algorithm = 'SOM'; + patRec.training = tType; + patRec.SOM=SOM; + + % SSOM + elseif strcmp(alg,'SSOM') + [SSOM accV] = SSOM_Mapping(trSets, trOuts, vSets, vOuts, tType, algConf); + patRec.algorithm = 'SSOM'; + patRec.training = tType; + patRec.SSOM=SSOM; + + end + + % Save the accuracy of the validation set as part of patRec structure; + patRec.accV = accV; + % Add the algConf variable + patRec.algConf = algConf; + end \ No newline at end of file diff --git a/PatRec/OneShotPatRec.m b/PatRec/OneShotPatRec.m index 818e4bb..0015e99 100644 --- a/PatRec/OneShotPatRec.m +++ b/PatRec/OneShotPatRec.m @@ -1,70 +1,67 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to execute a one shot pattern recognition for a single testing -% set. The patrec algorithm is here selected according to how it was -% previusly trainned -% -% OutMov : Is the index of the selected movement (only one) -% OutVector : Is a vector with the raw output of the classifier per -% movement. Multiple movements can be selected from it. -% -% ------------------------- Updates & Contributors ------------------------ -% 2011-09-11 / Max Ortiz / Creation -% 2012-02-26 / Max Ortiz / Created MLPTest to standarize calls for testing -% routines -% 2012-03-04 / Max Ortiz / Added a validation to prevent sending empty -% outMov -% 20xx-xx-xx / Author / Comment on update - -function [outMov outVector] = OneShotPatRec(patRecTrained,tSet) - - if strcmp(patRecTrained.algorithm,'MLP') || ... - strcmp(patRecTrained.algorithm,'MLP thOut') - - [outMov outVector] = MLPTest(patRecTrained, tSet); - - elseif strcmp(patRecTrained.algorithm,'DA') - - [outMov outVector] = DiscriminantTest(patRecTrained.coeff,tSet,patRecTrained.training); - - elseif strcmp(patRecTrained.algorithm,'RFN') - - [outMov outVector] = RegulationFeedbackTest(patRecTrained.connMat, tSet'); - - elseif strcmp(patRecTrained.algorithm,'SOM') - - [outMov outVector] = SOMTest(patRecTrained,tSet); - - elseif strcmp(patRecTrained.algorithm,'SSOM') - - [outMov outVector] = SSOMTest(patRecTrained,tSet); - - elseif strcmp(patRecTrained.algorithm,'KNN') - - [outMov outVector] = KNNTest(patRecTrained,tSet); - - end - - % Validation to prevent outMov to be empty which cause problems on the - % GUI - if isempty(outMov) - outMov = 0; - end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to execute a one shot pattern recognition for a single testing +% set. The patrec algorithm is here selected according to how it was +% previusly trainned +% +% OutMov : Is the index of the selected movement (only one) +% OutVector : Is a vector with the raw output of the classifier per +% movement. Multiple movements can be selected from it. +% +% ------------------------- Updates & Contributors ------------------------ +% 2011-09-11 / Max Ortiz / Creation +% 2012-02-26 / Max Ortiz / Created MLPTest to standarize calls for testing +% routines +% 2012-03-04 / Max Ortiz / Added a validation to prevent sending empty +% outMov +% 2014-11-07 / Diep Khong / Added SVM +% 20xx-xx-xx / Author / Comment on update + +function [outMov outVector] = OneShotPatRec(patRecTrained,tSet) + + if strcmp(patRecTrained.algorithm,'MLP') || ... + strcmp(patRecTrained.algorithm,'MLP thOut') + + [outMov outVector] = MLPTest(patRecTrained, tSet); + + elseif strcmp(patRecTrained.algorithm,'DA') + + [outMov outVector] = DiscriminantTest(patRecTrained.coeff,tSet,patRecTrained.training); + + elseif strcmp(patRecTrained.algorithm,'RFN') + + [outMov outVector] = RegulationFeedbackTest(patRecTrained.connMat, tSet'); + + elseif strcmp(patRecTrained.algorithm,'SOM') + + [outMov outVector] = SOMTest(patRecTrained,tSet); + + elseif strcmp(patRecTrained.algorithm,'SSOM') + + [outMov outVector] = SSOMTest(patRecTrained,tSet); + + end + + % Validation to prevent outMov to be empty which cause problems on the + % GUI + if isempty(outMov) + outMov = 0; + end + end \ No newline at end of file diff --git a/PatRec/OneShotPatRecClassifier.m b/PatRec/OneShotPatRecClassifier.m index efc10b9..7dfc997 100644 --- a/PatRec/OneShotPatRecClassifier.m +++ b/PatRec/OneShotPatRecClassifier.m @@ -1,63 +1,71 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to compute the classification output of a single classifier -% Simple function to keep standarizeed the call for different classifiers -% topologies -% -% outMov : Index of the selected patterns -% outVector : Vector the raw classification output -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-12-08 / Max Ortiz / Creation -% 2012-02-29 / Max Ortiz / Added a validation to prevent sending empty -% outMov -% 2012-03-04 / Max Ortiz / Moved the outMov validation to OneShotPatRec -% / this will keep cleaner this routine -% 20xx-xx-xx / Author / Comment on update - -function [outMov outVector] = OneShotPatRecClassifier(patRec, x) - - if strcmp(patRec.topology,'Single Classifier') - - [outMov outVector] = PatRec_SingleClassifier(patRec, x); - - elseif strcmp(patRec.topology,'Ago-antagonist') - - [outMov outVector] = PatRec_AgoAntagonist(patRec, x); - - elseif strcmp(patRec.topology,'Ago-antagonistAndMixed') - - [outMov outVector] = PatRec_AgoAntagonistAndMixed(patRec, x); - - elseif strcmp(patRec.topology,'One-vs-All') - - [outMov outVector] = PatRec_OneVsAll(patRec, x); - - elseif strcmp(patRec.topology,'One-vs-One') - - [outMov outVector] = PatRec_OneVsOne(patRec, x); - - elseif strcmp(patRec.topology,'All-and-One') - - [outMov outVector] = PatRec_AllAndOne(patRec, x); - - end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to compute the classification output of a single classifier +% Simple function to keep standarizeed the call for different classifiers +% topologies +% +% outMov : Index of the selected patterns +% outVector : Vector the raw classification output +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-12-08 / Max Ortiz / Creation +% 2012-02-29 / Max Ortiz / Added a validation to prevent sending empty +% outMov +% 2012-03-04 / Max Ortiz / Moved the outMov validation to OneShotPatRec +% / this will keep cleaner this routine +% 20xx-xx-xx / Author / Comment on update + +function [outMov outVector] = OneShotPatRecClassifier(patRec, x) + + if strcmp(patRec.topology,'Single Classifier') + + [outMov outVector] = PatRec_SingleClassifier(patRec, x); + + elseif strcmp(patRec.topology,'Ago-antagonist') + + [outMov outVector] = PatRec_AgoAntagonist(patRec, x); + + elseif strcmp(patRec.topology,'Ago-antagonistAndMixed') + + [outMov outVector] = PatRec_AgoAntagonistAndMixed(patRec, x); + + elseif strcmp(patRec.topology,'One-vs-All') + + [outMov outVector] = PatRec_OneVsAll(patRec, x); + + elseif strcmp(patRec.topology,'One-vs-All Th') + + [outMov outVector] = PatRec_OneVsAllT(patRec, x); + + elseif strcmp(patRec.topology,'One-vs-One') + + [outMov outVector] = PatRec_OneVsOne(patRec, x); + + elseif strcmp(patRec.topology,'One-vs-One DoF') + + [outMov outVector] = PatRec_OneVsOneDoF(patRec, x); + + elseif strcmp(patRec.topology,'All-and-One') + + [outMov outVector] = PatRec_AllAndOne(patRec, x); + + end + end \ No newline at end of file diff --git a/PatRec/OneShotRealtimePatRec.m b/PatRec/OneShotRealtimePatRec.m index 744c71a..6139604 100644 --- a/PatRec/OneShotRealtimePatRec.m +++ b/PatRec/OneShotRealtimePatRec.m @@ -1,95 +1,100 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to execute the Offline traning once selected from the GUI in -% Matlab -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-11-06 / Max Ortiz / Creation - Made an independent routine out of -% RealtimePatRec -% 2012-11-23 / Joel Falk-Dahlin / Removed handles in from control -% functions, speeds are now in patRec -% 20xx-xx-xx / Author / Comment on update - -function [outMov, outVector, patRec, handles] = OneShotRealtimePatRec(tData, patRec, handles, thresholdGUIData) - - %% Signal processing - tSet = SignalProcessing_RealtimePatRec(tData, patRec); - - %% Floor noise - % Only predict when signal is over floor noise? - if(isfield(patRec,'floorNoise')); - meanFeature1 = mean(tSet(1:size(patRec.nCh,2))); - fnoiseDiv = 1; - if meanFeature1 < (patRec.floorNoise(1)/fnoiseDiv) - outMov = patRec.nOuts; - outVector = zeros(patRec.nOuts,1); - outVector(end) = 1; - else - % One shoot PatRec - [outMov, outVector] = OneShotPatRecClassifier(patRec, tSet); - end - else - % One shoot PatRec - [outMov, outVector] = OneShotPatRecClassifier(patRec, tSet); - end - - % Safety check so the classifier cannot predict rest + a movement - % This condition assumes that rest is always the last movement - if strcmp(patRec.mov(end),'Rest') - if ismember(patRec.nOuts, outMov) | outMov == 0 - outMov = patRec.nOuts; - end - end - - %% Threshold GUI update - if(isfield(patRec.patRecTrained,'thOut')); - %set(handles.(sprintf('thPatch%d',outMov-1)), 'FaceColor', [0 1 0]); - for i=0:patRec.nOuts-1 - s0 = sprintf('thPatch%d',i); - s1 = sprintf('movementSelector%d',i); - s2 = sprintf('value%d',i); - s3 = sprintf('overrideButton%d',i); - set(handles.(s0), 'YData', [0 outVector(get(thresholdGUIData.(s1),'Value')) outVector(get(thresholdGUIData.(s1),'Value')) 0]); - % Change face colour - if find(get(thresholdGUIData.(s1),'Value') == outMov); - set(handles.(s0), 'FaceColor', [0 1 0]); - else - set(handles.(s0), 'FaceColor', [0 0 1]); - end - % Update threshold value - if(get(thresholdGUIData.(s3), 'Value') == 1); - patRec.patRecTrained.thOut(get(thresholdGUIData.(s1),'Value')) = str2num(get(thresholdGUIData.(s2),'String')); - else - set(thresholdGUIData.(s2), 'String', patRec.patRecTrained.thOut(get(thresholdGUIData.(s1),'Value'))); - end - end - end - - %% Apply Proportional control - patRec = ApplyProportionalControl(tSet, patRec); - - %% Control algorithm - [patRec, outMov] = ApplyControl(patRec, outMov, outVector); - - %% Update result on the GUI - set(handles.lb_movements,'Value',outMov); - drawnow; - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to execute the Offline traning once selected from the GUI in +% Matlab +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-11-06 / Max Ortiz / Creation - Made an independent routine out of +% RealtimePatRec +% 2012-11-23 / Joel Falk-Dahlin / Removed handles in from control +% functions, speeds are now in patRec +% 2013-01-02 / Max Ortiz / Added routines for feature reduction +% 20xx-xx-xx / Author / Comment on update + +function [outMov, outVector, patRec, handles] = OneShotRealtimePatRec(tData, patRec, handles, thresholdGUIData) + + %% Signal processing + tSet = SignalProcessing_RealtimePatRec(tData, patRec); + + %% Floor noise + % Only predict when signal is over floor noise? + if(isfield(patRec,'floorNoise')); + meanFeature1 = mean(tSet(1:size(patRec.nCh,2))); + fnoiseDiv = 1; + if meanFeature1 < (patRec.floorNoise(1)/fnoiseDiv) + outMov = patRec.nOuts; + outVector = zeros(patRec.nOuts,1); + outVector(end) = 1; + else + % Apply feature reduction + tSet = ApplyFeatureReduction(tSet, patRec); + % One shoot PatRec + [outMov, outVector] = OneShotPatRecClassifier(patRec, tSet); + end + else + % Apply feature reduction + tSet = ApplyFeatureReduction(tSet, patRec); + % One shoot PatRec + [outMov, outVector] = OneShotPatRecClassifier(patRec, tSet); + end + + % Safety check so the classifier cannot predict rest + a movement + % This condition assumes that rest is always the last movement + if strcmp(patRec.mov(end),'Rest') + if ismember(patRec.nOuts, outMov) | outMov == 0 + outMov = patRec.nOuts; + end + end + + %% Threshold GUI update + if(isfield(patRec.patRecTrained,'thOut')); + %set(handles.(sprintf('thPatch%d',outMov-1)), 'FaceColor', [0 1 0]); + for i=0:patRec.nOuts-1 + s0 = sprintf('thPatch%d',i); + s1 = sprintf('movementSelector%d',i); + s2 = sprintf('value%d',i); + s3 = sprintf('overrideButton%d',i); + set(handles.(s0), 'YData', [0 outVector(get(thresholdGUIData.(s1),'Value')) outVector(get(thresholdGUIData.(s1),'Value')) 0]); + % Change face colour + if find(get(thresholdGUIData.(s1),'Value') == outMov); + set(handles.(s0), 'FaceColor', [0 1 0]); + else + set(handles.(s0), 'FaceColor', [0 0 1]); + end + % Update threshold value + if(get(thresholdGUIData.(s3), 'Value') == 1); + patRec.patRecTrained.thOut(get(thresholdGUIData.(s1),'Value')) = str2num(get(thresholdGUIData.(s2),'String')); + else + set(thresholdGUIData.(s2), 'String', patRec.patRecTrained.thOut(get(thresholdGUIData.(s1),'Value'))); + end + end + end + + %% Apply Proportional control + patRec = ApplyProportionalControl(tSet, patRec); + + %% Control algorithm + [patRec, outMov] = ApplyControl(patRec, outMov, outVector); + + %% Update result on the GUI + set(handles.lb_movements,'Value',outMov); + drawnow; + end \ No newline at end of file diff --git a/PatRec/PCA/ApplyFeatureReduction.m b/PatRec/PCA/ApplyFeatureReduction.m new file mode 100644 index 0000000..814531f --- /dev/null +++ b/PatRec/PCA/ApplyFeatureReduction.m @@ -0,0 +1,36 @@ +% % ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% This function is used as a hub to call any algorithm for feature reduction. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-08-01 / Tanuj Kumar Aluru / Creation +% 2013-01-02 / Max Ortiz / Review and corrected documentation +% / All the calls are hard coded, +% / see ApplyControl for further improvement. +% 20xx-xx-xx / Author / Comment on update + +function x = ApplyFeatureReduction(x, patRec) + + if strcmp(patRec.featureReduction.Alg,'PCA') + + x = PCATest(x ,patRec.featureReduction.eigVecTrData,patRec.selFeatures); + + end diff --git a/PatRec/PCA/FeatureReduction.m b/PatRec/PCA/FeatureReduction.m new file mode 100644 index 0000000..9308304 --- /dev/null +++ b/PatRec/PCA/FeatureReduction.m @@ -0,0 +1,36 @@ +% % ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This allows the selection of the feature reduction algorithm to be used +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-08-01 / Tanuj Kumar Aluru / Creation +% 20xx-xx-xx / Author / Comment on update +%%------------------------------------------------------------------------- + +function [trSets vSets patRec] = FeatureReduction(trSets,vSets,patRec) + + if strcmp(patRec.featureReduction.Alg,'PCA') + % Calculating PCA on training set + [trSets eigVecTrData] = PCAFeatureReduction(trSets,patRec.selFeatures); + % Calculating PCA on validation set + vSets = PCATest(vSets,eigVecTrData,patRec.selFeatures); + patRec.featureReduction.eigVecTrData = eigVecTrData; + + end diff --git a/PatRec/PCA/PCAFeatureReduction.m b/PatRec/PCA/PCAFeatureReduction.m new file mode 100644 index 0000000..70d9d7a --- /dev/null +++ b/PatRec/PCA/PCAFeatureReduction.m @@ -0,0 +1,78 @@ + +% % ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This Function is the main algorithm perform Principal Component Analysis +% for Feature Reduction +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-08-01 / Tanuj Kumar Aluru / Creation +% 20xx-xx-xx / Author / Comment on update +%%------------------------------------------------------------------------- +function [PCAout eigVec] = PCAFeatureReduction(inputData,selFeatures) + +recData = reshape(inputData,[],size(selFeatures,1)); + +% Computing Covariance and Eigen Decomposition +variance = cov(recData); +[E D ] = eig(variance); + +%Sort Eigen Values in Descending Order + +[eigVal index] = sort(diag(D),'descend'); + +% ---- Feature Reduction Step---------------------------------------------- +% Chose first k Principal Components + +eigVec = []; + +Threshold = 0.99; + +k= 0; + +for i = 1:length(eigVal) + Totalvar(i) = sum(eigVal(1:i))/sum(eigVal); + if Totalvar(i) >= Threshold ; + k = i; + break + end +end + +% Consider K largest Eigen vectors with corrosepoding Eigen Values + +eigVec = [eigVec,E(:,index(1:k))]; + + +% Project the Data on to EigenVectors + +ProjectedData = (recData*eigVec); +PCAout = reshape(ProjectedData,size(inputData,1),[]); +end + + + + + + + + + + + + diff --git a/PatRec/PCA/PCATest.m b/PatRec/PCA/PCATest.m new file mode 100644 index 0000000..5ecf6d7 --- /dev/null +++ b/PatRec/PCA/PCATest.m @@ -0,0 +1,31 @@ +% % ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function of Principal Component Analysis Feature Reduction +% for testing and validation sets. +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-08-01 / Tanuj Kumar Aluru / Creation +% 20xx-xx-xx / Author / Comment on update +%%------------------------------------------------------------------------- + +function PCAData=PCATest(inputData ,eigVecTrData,selFeatures) + + recData = reshape(inputData,[],size(selFeatures,1)); + PCAData = (recData*eigVecTrData); + PCAData=reshape(PCAData,size(inputData,1),[]); diff --git a/PatRec/PSO/InitVelocities.m b/PatRec/PSO/InitVelocities.m index 1cdd023..77001a9 100644 --- a/PatRec/PSO/InitVelocities.m +++ b/PatRec/PSO/InitVelocities.m @@ -1,40 +1,40 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to initialize the positions according to the range -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-07-27 / Max Ortiz / Creation -% 2012-03-27 / Max Ortiz / Adapted for BioPatRec from EMG_AQ -% 2012-06-03 / Max Ortiz / Commented the velocity validation, it's not -% necessary since vMax is set for the xMax-xMin range. - -function vel = InitVelocities(sSize, nVar, xMin, xMax, dt, alpha, vMax) - -vel = alpha .* (xMin + rand(sSize,nVar) .* (xMax - xMin)) ./ dt; - -% for i = 1:sSize -% for j = 1:nVar -% if vel(i,j) < 0 && vel(i,j) < -vMax -% vel(i,j) = vMax * -1; -% elseif vel(i,j) > 0 && vel(i,j) > vMax -% vel(i,j) = vMax; -% end -% end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to initialize the positions according to the range +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-07-27 / Max Ortiz / Creation +% 2012-03-27 / Max Ortiz / Adapted for BioPatRec from EMG_AQ +% 2012-06-03 / Max Ortiz / Commented the velocity validation, it's not +% necessary since vMax is set for the xMax-xMin range. + +function vel = InitVelocities(sSize, nVar, xMin, xMax, dt, alpha, vMax) + +vel = alpha .* (xMin + rand(sSize,nVar) .* (xMax - xMin)) ./ dt; + +% for i = 1:sSize +% for j = 1:nVar +% if vel(i,j) < 0 && vel(i,j) < -vMax +% vel(i,j) = vMax * -1; +% elseif vel(i,j) > 0 && vel(i,j) > vMax +% vel(i,j) = vMax; +% end +% end % end \ No newline at end of file diff --git a/PatRec/PSO/UpdateVel.m b/PatRec/PSO/UpdateVel.m index 3874815..8673001 100644 --- a/PatRec/PSO/UpdateVel.m +++ b/PatRec/PSO/UpdateVel.m @@ -1,49 +1,49 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to initialize the positions according to the range -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-07-27 / Max Ortiz/ Creation -% 2012-03-29 / Max Ortiz/ Adapted for BioPatRec from EMG_AQ - -function tempvel = UpdateVel(pos, velocities, positions, pBestPos, sBestPos, c1, c2, nVar, dt,vMax, w) - -tempvel = zeros(nVar,1); -for j = 1:nVar - q=rand; - r=rand; - tempvel(j) = w * velocities(pos,j) + c1*q*((pBestPos(pos,j) - positions(pos,j))/dt) +... - c2*r*((sBestPos(j) - positions(pos,j))/dt); - -% % Contain velocity -% if tempvel(j) < -vMax -% tempvel(j) = -vMax; -% elseif tempvel(j) > vMax -% tempvel(j) = vMax; -% end - - % Contain velocity considering inertial weight - if tempvel(j) < -vMax - tempvel(j) = w * -vMax; - elseif tempvel(j) > vMax - tempvel(j) = w * vMax; - end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to initialize the positions according to the range +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-07-27 / Max Ortiz/ Creation +% 2012-03-29 / Max Ortiz/ Adapted for BioPatRec from EMG_AQ + +function tempvel = UpdateVel(pos, velocities, positions, pBestPos, sBestPos, c1, c2, nVar, dt,vMax, w) + +tempvel = zeros(nVar,1); +for j = 1:nVar + q=rand; + r=rand; + tempvel(j) = w * velocities(pos,j) + c1*q*((pBestPos(pos,j) - positions(pos,j))/dt) +... + c2*r*((sBestPos(j) - positions(pos,j))/dt); + +% % Contain velocity +% if tempvel(j) < -vMax +% tempvel(j) = -vMax; +% elseif tempvel(j) > vMax +% tempvel(j) = vMax; +% end + + % Contain velocity considering inertial weight + if tempvel(j) < -vMax + tempvel(j) = w * -vMax; + elseif tempvel(j) > vMax + tempvel(j) = w * vMax; + end + end \ No newline at end of file diff --git a/PatRec/RFN/Get_connMat_TrV.m b/PatRec/RFN/Get_connMat_TrV.m index a138bf5..e0b18ac 100644 --- a/PatRec/RFN/Get_connMat_TrV.m +++ b/PatRec/RFN/Get_connMat_TrV.m @@ -1,61 +1,61 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to create the connectivity matrix -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-10-02 / Max Ortiz / Created -% 2012-03-04 / Max Ortiz / Modified to handle non-equal amout of sets per -% class -% 2012-04-07 / Max Ortiz / Modified to handle mixed outputs -% 20xx-xx-xx / Author / Comment on update - -function connMat = Get_connMat_TrV(trSet, trOut, vSet, vOut) - -% Integration of training and validation sets in one set -trSet = [trSet ; vSet]; -trOut = [trOut ; vOut]; - -%Variable -nM = size(trOut,2); % Number of Movements -nFeatures = size(trSet,2); % Number of Features -nSets = size(trSet,1); % Number of Total sets -connMat = zeros(nM,nFeatures); % Connectivity Matrix - -% Separate data sets per pattern -for i = 1 : nSets - movIdx = find(trOut(i,:)); - % This loop takes care of mixed outputs - for j = 1 : size(movIdx,2) - k = movIdx(j); - allSetsPerPattern(i,:,k) = trSet(i,:); - %(sets x features x classes) - end -end - -% Create connectivity matrix by computing the mean -for i = 1 : nM; - idx = find(allSetsPerPattern(:,1,i)); -% setsPerPattern(:,:,i) = allSetsPerPattern(idx,:,i); -% connMat(i,:) = mean(setsPerPattern(:,:,i)); - setsPerPattern = allSetsPerPattern(idx,:,i); - connMat(i,:) = mean(setsPerPattern); -end - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to create the connectivity matrix +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-10-02 / Max Ortiz / Created +% 2012-03-04 / Max Ortiz / Modified to handle non-equal amout of sets per +% class +% 2012-04-07 / Max Ortiz / Modified to handle mixed outputs +% 20xx-xx-xx / Author / Comment on update + +function connMat = Get_connMat_TrV(trSet, trOut, vSet, vOut) + +% Integration of training and validation sets in one set +trSet = [trSet ; vSet]; +trOut = [trOut ; vOut]; + +%Variable +nM = size(trOut,2); % Number of Movements +nFeatures = size(trSet,2); % Number of Features +nSets = size(trSet,1); % Number of Total sets +connMat = zeros(nM,nFeatures); % Connectivity Matrix + +% Separate data sets per pattern +for i = 1 : nSets + movIdx = find(trOut(i,:)); + % This loop takes care of mixed outputs + for j = 1 : size(movIdx,2) + k = movIdx(j); + allSetsPerPattern(i,:,k) = trSet(i,:); + %(sets x features x classes) + end +end + +% Create connectivity matrix by computing the mean +for i = 1 : nM; + idx = find(allSetsPerPattern(:,1,i)); +% setsPerPattern(:,:,i) = allSetsPerPattern(idx,:,i); +% connMat(i,:) = mean(setsPerPattern(:,:,i)); + setsPerPattern = allSetsPerPattern(idx,:,i); + connMat(i,:) = mean(setsPerPattern); +end + + diff --git a/PatRec/RFN/Get_connMat_eMean.m b/PatRec/RFN/Get_connMat_eMean.m index 829215d..c9b3a16 100644 --- a/PatRec/RFN/Get_connMat_eMean.m +++ b/PatRec/RFN/Get_connMat_eMean.m @@ -1,64 +1,64 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to create the connectivity matrix computing the mean with -% exclusion of values that are higher that 2 times the variance -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-05-15 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function connMat = Get_connMat_eMean(trSet, trOut, vSet, vOut) - -% Integration of training and validation sets in one set -trSet = [trSet ; vSet]; -trOut = [trOut ; vOut]; - -%Variable -nM = size(trOut,2); % Number of Movements -nFeatures = size(trSet,2); % Number of Features -nSets = size(trSet,1); % Number of Total sets -connMat = zeros(nM,nFeatures); % Connectivity Matrix - -% Separate data sets per pattern -% Tese loops are in a way necessary for mixed outputs, think about that -% before "optimizing" the routine -for i = 1 : nSets - movIdx = find(trOut(i,:)); - % This loop takes care of mixed outputs - for j = 1 : size(movIdx,2) - k = movIdx(j); - allSetsPerPattern(i,:,k) = trSet(i,:); - %(sets x features x classes) - end -end - -% Create connectivity matrix by computing the mean -for i = 1 : nM; - idx = find(allSetsPerPattern(:,1,i)); - setsPerPattern = allSetsPerPattern(idx,:,i); - % Only learn values where the mean is bigger than 2 std - meanSets = mean(setsPerPattern); - stdSets = std(setsPerPattern); - mask = (meanSets - (2*stdSets)) > 0; - connMat(i,:) = mask .* meanSets; -end -% Give to 0 values a small contribution -connMat(connMat == 0) = 0.0000000001; - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to create the connectivity matrix computing the mean with +% exclusion of values that are higher that 2 times the variance +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-05-15 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function connMat = Get_connMat_eMean(trSet, trOut, vSet, vOut) + +% Integration of training and validation sets in one set +trSet = [trSet ; vSet]; +trOut = [trOut ; vOut]; + +%Variable +nM = size(trOut,2); % Number of Movements +nFeatures = size(trSet,2); % Number of Features +nSets = size(trSet,1); % Number of Total sets +connMat = zeros(nM,nFeatures); % Connectivity Matrix + +% Separate data sets per pattern +% Tese loops are in a way necessary for mixed outputs, think about that +% before "optimizing" the routine +for i = 1 : nSets + movIdx = find(trOut(i,:)); + % This loop takes care of mixed outputs + for j = 1 : size(movIdx,2) + k = movIdx(j); + allSetsPerPattern(i,:,k) = trSet(i,:); + %(sets x features x classes) + end +end + +% Create connectivity matrix by computing the mean +for i = 1 : nM; + idx = find(allSetsPerPattern(:,1,i)); + setsPerPattern = allSetsPerPattern(idx,:,i); + % Only learn values where the mean is bigger than 2 std + meanSets = mean(setsPerPattern); + stdSets = std(setsPerPattern); + mask = (meanSets - (2*stdSets)) > 0; + connMat(i,:) = mask .* meanSets; +end +% Give to 0 values a small contribution +connMat(connMat == 0) = 0.0000000001; + diff --git a/PatRec/RFN/PSO/DecodeConnMat.m b/PatRec/RFN/PSO/DecodeConnMat.m index 5e6283b..de679d6 100644 --- a/PatRec/RFN/PSO/DecodeConnMat.m +++ b/PatRec/RFN/PSO/DecodeConnMat.m @@ -1,32 +1,32 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Decode the flat connMat to connMat -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-03-29 / Max Ortiz/ Creation - -function connMat = DecodeConnMat(cM, nM, nF) - -% Flat connMat -for i=1:nM - sIdx = 1 + (nF * (i-1)); - eIdx = sIdx + nF - 1; - connMat(i,:) = cM(sIdx:eIdx); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Decode the flat connMat to connMat +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-03-29 / Max Ortiz/ Creation + +function connMat = DecodeConnMat(cM, nM, nF) + +% Flat connMat +for i=1:nM + sIdx = 1 + (nF * (i-1)); + eIdx = sIdx + nF - 1; + connMat(i,:) = cM(sIdx:eIdx); end \ No newline at end of file diff --git a/PatRec/RFN/PSO/InitPSO_RFN.m b/PatRec/RFN/PSO/InitPSO_RFN.m index 0d6bfa7..a44c5cf 100644 --- a/PatRec/RFN/PSO/InitPSO_RFN.m +++ b/PatRec/RFN/PSO/InitPSO_RFN.m @@ -1,82 +1,82 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Initialization of PSO for RFN -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-07-27 / Max Ortiz/ Creation -% 2012-03-29 / Max Ortiz/ Adapted for RFN - -function PSO = InitPSO_RFN(connMat) - -PSO.sSize = 20; %Swarm size -PSO.nVar = size(connMat,1)*size(connMat,2); %Number of variables -PSO.xMin = 0; %minumum -PSO.xMax = 1; %maximum - -PSO.dt = 1; %time spet lengh -PSO.alpha = 1; %constant -PSO.c1 = 2; %Constant C1 -PSO.c2 = 2; %Constant C2 - -PSO.beta = .9; %Beta for inertia weight -PSO.w = 1.4; %weigh for inertia weight -PSO.pcr = 0.1; %Craziness probability - -PSO.maxSimulations = 50; %max number of simulations - -% Initialization of Vmax -PSO.vMax = (PSO.xMax - PSO.xMin) / PSO.dt; - -PSO.opelite = 1; %input('Do you want to use elite particle? Yes (1) or No (2): '); -PSO.opcrazy = 1; %input('Do you want to use crazy operator? Yes (1) or No (2): '); - -% Flat connMat -cM = []; -for i=1:size(connMat,1) - cM = [cM connMat(i,:)]; -end - -%Initializations -% The first position will be as the connMat -PSO.positions(1,:) = cM; -% The remianing position will vary +-.5 -for i = 2 : PSO.sSize - randPos = -.5 + rand(1,PSO.nVar); % the rest +- .5 - PSO.positions(i,:) = cM + randPos; - - % Limit negative values - negIdx = find(PSO.positions(i,:) < 0); - PSO.positions(i,negIdx) = 0.0000000001; - % Limit values over 1 - negIdx = find(PSO.positions(i,:) > 1); - PSO.positions(i,negIdx) = 1; - -end - -% Random initialization of the position -% PSO.positions = PSO.xMin + rand(PSO.sSize,PSO.nVar).*(PSO.xMax - PSO.xMin); - -PSO.velocities = InitVelocities(PSO.sSize,PSO.nVar,PSO.xMin,PSO.xMax,PSO.dt,PSO.alpha,PSO.vMax); - - - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Initialization of PSO for RFN +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-07-27 / Max Ortiz/ Creation +% 2012-03-29 / Max Ortiz/ Adapted for RFN + +function PSO = InitPSO_RFN(connMat) + +PSO.sSize = 20; %Swarm size +PSO.nVar = size(connMat,1)*size(connMat,2); %Number of variables +PSO.xMin = 0; %minumum +PSO.xMax = 1; %maximum + +PSO.dt = 1; %time spet lengh +PSO.alpha = 1; %constant +PSO.c1 = 2; %Constant C1 +PSO.c2 = 2; %Constant C2 + +PSO.beta = .9; %Beta for inertia weight +PSO.w = 1.4; %weigh for inertia weight +PSO.pcr = 0.1; %Craziness probability + +PSO.maxSimulations = 50; %max number of simulations + +% Initialization of Vmax +PSO.vMax = (PSO.xMax - PSO.xMin) / PSO.dt; + +PSO.opelite = 1; %input('Do you want to use elite particle? Yes (1) or No (2): '); +PSO.opcrazy = 1; %input('Do you want to use crazy operator? Yes (1) or No (2): '); + +% Flat connMat +cM = []; +for i=1:size(connMat,1) + cM = [cM connMat(i,:)]; +end + +%Initializations +% The first position will be as the connMat +PSO.positions(1,:) = cM; +% The remianing position will vary +-.5 +for i = 2 : PSO.sSize + randPos = -.5 + rand(1,PSO.nVar); % the rest +- .5 + PSO.positions(i,:) = cM + randPos; + + % Limit negative values + negIdx = find(PSO.positions(i,:) < 0); + PSO.positions(i,negIdx) = 0.0000000001; + % Limit values over 1 + negIdx = find(PSO.positions(i,:) > 1); + PSO.positions(i,negIdx) = 1; + +end + +% Random initialization of the position +% PSO.positions = PSO.xMin + rand(PSO.sSize,PSO.nVar).*(PSO.xMax - PSO.xMin); + +PSO.velocities = InitVelocities(PSO.sSize,PSO.nVar,PSO.xMin,PSO.xMax,PSO.dt,PSO.alpha,PSO.vMax); + + + + + + diff --git a/PatRec/RFN/PSO/PSO_RFN.m b/PatRec/RFN/PSO/PSO_RFN.m index bbf4139..00f287e 100644 --- a/PatRec/RFN/PSO/PSO_RFN.m +++ b/PatRec/RFN/PSO/PSO_RFN.m @@ -1,103 +1,103 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Particle Swarm Optimization to find ... -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-04-23 / Max Ortiz/ Creation -% 2012-04-01 / Max Ortiz/ Adapted for RFN - -function [PSO, connMat] = PSO_RFN(PSO, connMat, vSets, vOuts, thO) - -if PSO.opcrazy ~= 1 - PSO.pcr = 0; -end - -% Flat connMat -nM = size(connMat,1); % Number of movements -nF = size(connMat,2); % Number of features - -% Initialization of best -fitness(1:PSO.sSize) = inf; -pBestFit(1:PSO.sSize) = inf; -sBestFit = inf; - - -%Run until the max number of generations -for sim = 1:PSO.maxSimulations - - %Fitness evaluation - for i = 1:PSO.sSize - connMat = DecodeConnMat(PSO.positions(i,:), nM, nF); - fitness(i) = RegulationFeedbackFit(connMat, vSets, vOuts, nM, thO); - - %Particle Best Positions - if (fitness(i) < pBestFit(i)) - pBestFit(i) = fitness(i); %particle best fitness - pBestPos(i,:) = PSO.positions(i,:); %particle best position - end - end - - %Find the best in the swarm - for i=1:PSO.sSize - if pBestFit(i) < sBestFit - sBestFit = pBestFit(i); %Swarm best fitness - sBestPos = PSO.positions(i,:); %Swarm best position - - connMat = DecodeConnMat(sBestPos, nM, nF); - disp(RegulationFeedbackAcc(connMat, vSets, vOuts, nM)); - end - end - - %Update velocity - for i = 1:PSO.sSize - cr=rand; - if cr > PSO.pcr - tempvel = UpdateVel(i, PSO.velocities, PSO.positions, pBestPos, sBestPos, PSO.c1, PSO.c2, PSO.nVar, PSO.dt, PSO.vMax, PSO.w); - else - tempvel = -PSO.vMax + 2 * rand(PSO.nVar,1) * PSO.vMax; - end - PSO.velocities(i,:) = tempvel; - end - - %Reduce inertia weight for velocity - if PSO.w > 0.3 - PSO.w = PSO.beta * PSO.w; - end - - %Update position - PSO.positions = PSO.positions + PSO.velocities * PSO.dt; -% % Limit negative values - negIdx = find(PSO.positions < 0); - PSO.positions(negIdx) = 0.0000000001; -% % Limit values over 1 - negIdx = find(PSO.positions > 1); - PSO.positions(negIdx) = 1; - - - %Elite Particle - if PSO.opelite == 1 - PSO.positions(1,:) = sBestPos; %This instruction will always keep the best partical position - end - - disp(sBestFit); - -end -% Return the best position +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Particle Swarm Optimization to find ... +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-04-23 / Max Ortiz/ Creation +% 2012-04-01 / Max Ortiz/ Adapted for RFN + +function [PSO, connMat] = PSO_RFN(PSO, connMat, vSets, vOuts, thO) + +if PSO.opcrazy ~= 1 + PSO.pcr = 0; +end + +% Flat connMat +nM = size(connMat,1); % Number of movements +nF = size(connMat,2); % Number of features + +% Initialization of best +fitness(1:PSO.sSize) = inf; +pBestFit(1:PSO.sSize) = inf; +sBestFit = inf; + + +%Run until the max number of generations +for sim = 1:PSO.maxSimulations + + %Fitness evaluation + for i = 1:PSO.sSize + connMat = DecodeConnMat(PSO.positions(i,:), nM, nF); + fitness(i) = RegulationFeedbackFit(connMat, vSets, vOuts, nM, thO); + + %Particle Best Positions + if (fitness(i) < pBestFit(i)) + pBestFit(i) = fitness(i); %particle best fitness + pBestPos(i,:) = PSO.positions(i,:); %particle best position + end + end + + %Find the best in the swarm + for i=1:PSO.sSize + if pBestFit(i) < sBestFit + sBestFit = pBestFit(i); %Swarm best fitness + sBestPos = PSO.positions(i,:); %Swarm best position + + connMat = DecodeConnMat(sBestPos, nM, nF); + disp(RegulationFeedbackAcc(connMat, vSets, vOuts, nM)); + end + end + + %Update velocity + for i = 1:PSO.sSize + cr=rand; + if cr > PSO.pcr + tempvel = UpdateVel(i, PSO.velocities, PSO.positions, pBestPos, sBestPos, PSO.c1, PSO.c2, PSO.nVar, PSO.dt, PSO.vMax, PSO.w); + else + tempvel = -PSO.vMax + 2 * rand(PSO.nVar,1) * PSO.vMax; + end + PSO.velocities(i,:) = tempvel; + end + + %Reduce inertia weight for velocity + if PSO.w > 0.3 + PSO.w = PSO.beta * PSO.w; + end + + %Update position + PSO.positions = PSO.positions + PSO.velocities * PSO.dt; +% % Limit negative values + negIdx = find(PSO.positions < 0); + PSO.positions(negIdx) = 0.0000000001; +% % Limit values over 1 + negIdx = find(PSO.positions > 1); + PSO.positions(negIdx) = 1; + + + %Elite Particle + if PSO.opelite == 1 + PSO.positions(1,:) = sBestPos; %This instruction will always keep the best partical position + end + + disp(sBestFit); + +end +% Return the best position connMat = DecodeConnMat(sBestPos, nM, nF); \ No newline at end of file diff --git a/PatRec/RFN/RFN.m b/PatRec/RFN/RFN.m index 9158081..5f407a5 100644 --- a/PatRec/RFN/RFN.m +++ b/PatRec/RFN/RFN.m @@ -1,71 +1,71 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Implementation of the RFN -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-09-26 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function [acc, connMat] = RFN(trSet, trOut, vSet, vOut, tSet, tOut, dType) - -% Integration of training set and validation set -trSet = [trSet ; vSet]; -trOut = [trOut ; vOut]; - -%Variable -nM = size(trOut,2); % Number of Movements -nFeatures = size(trSet,2); % Number of Features -nSets = size(trSet,1); % Number of Total sets -allSetsPerPattern = zeros(nSets,nFeatures,nM); -setsPerPattern = zeros(nSets/nM,nFeatures,nM); -connMat = zeros(nM,nFeatures); % Connectivity Matrix -tSetsPerPattern = size(tOut,1)/size(tOut,2); - - -% Separate data sets per pattern -for i = 1 : nSets - allSetsPerPattern(i,:,find(trOut(i,:))) = trSet(i,:); -end - -% Create connectivity matrix -for i = 1 : nM; - idx = find(allSetsPerPattern(:,1,i)); - setsPerPattern(:,:,i) = allSetsPerPattern(idx,:,i); - connMat(i,:) = mean(setsPerPattern(:,:,i)); -end - -%connMat = Get_connMat(trSet, trOut, vSet, vOut); - -% Test the tSet (test set) - -% for i = 1 : size(tSet,1) -% vRes = RegulationFeedback(connMat, tSet(i,:)'); -% [Y I] = max(vRes); -% -% if I == find(tOut(i,:)) -% accAll(i,find(tOut(i,:))) = 1; -% end -% end -% -% acc = sum(accAll) ./ tSetsPerPattern; -% acc(nM+1) = mean(acc); -% acc = acc'; - -acc = RegulationFeedbackAcc(connMat, tSet, tOut); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Implementation of the RFN +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-09-26 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function [acc, connMat] = RFN(trSet, trOut, vSet, vOut, tSet, tOut, dType) + +% Integration of training set and validation set +trSet = [trSet ; vSet]; +trOut = [trOut ; vOut]; + +%Variable +nM = size(trOut,2); % Number of Movements +nFeatures = size(trSet,2); % Number of Features +nSets = size(trSet,1); % Number of Total sets +allSetsPerPattern = zeros(nSets,nFeatures,nM); +setsPerPattern = zeros(nSets/nM,nFeatures,nM); +connMat = zeros(nM,nFeatures); % Connectivity Matrix +tSetsPerPattern = size(tOut,1)/size(tOut,2); + + +% Separate data sets per pattern +for i = 1 : nSets + allSetsPerPattern(i,:,find(trOut(i,:))) = trSet(i,:); +end + +% Create connectivity matrix +for i = 1 : nM; + idx = find(allSetsPerPattern(:,1,i)); + setsPerPattern(:,:,i) = allSetsPerPattern(idx,:,i); + connMat(i,:) = mean(setsPerPattern(:,:,i)); +end + +%connMat = Get_connMat(trSet, trOut, vSet, vOut); + +% Test the tSet (test set) + +% for i = 1 : size(tSet,1) +% vRes = RegulationFeedback(connMat, tSet(i,:)'); +% [Y I] = max(vRes); +% +% if I == find(tOut(i,:)) +% accAll(i,find(tOut(i,:))) = 1; +% end +% end +% +% acc = sum(accAll) ./ tSetsPerPattern; +% acc(nM+1) = mean(acc); +% acc = acc'; + +acc = RegulationFeedbackAcc(connMat, tSet, tOut); diff --git a/PatRec/RFN/RegulationFeedback.m b/PatRec/RFN/RegulationFeedback.m index 70e7278..6273781 100644 --- a/PatRec/RFN/RegulationFeedback.m +++ b/PatRec/RFN/RegulationFeedback.m @@ -1,53 +1,53 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to select the way in which the connectivity matrix will be -% created -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-09-26 / Max Ortiz / Creation -% 2012-03-29 / Max Ortiz / Added the option to use PSO for the connMat -% 20xx-xx-xx / Author / Comment on update - -function [connMat accV] = RegulationFeedback(tType, trSets, trOuts, vSets, vOuts) - -% Get connectivity Matrix -%connMat = Get_connMat(trSet, trOut); %Get connMat using all sets -if strcmp(tType,'Mean') - - connMat = Get_connMat_TrV(trSets, trOuts, vSets, vOuts); - -elseif strcmp(tType,'Mean + PSO') - - thO = 0; % Threshold output is only use in the RegFeedback_ThO - connMat = Get_connMat_TrV(trSets, trOuts, vSets, vOuts); - PSO = InitPSO_RFN(connMat); - [PSO, connMat] = PSO_RFN(PSO, connMat, vSets, vOuts, thO); - -elseif strcmp(tType,'Exclusive Mean') - - connMat = Get_connMat_eMean(trSets, trOuts, vSets, vOuts); - -else - errordlg('No training algorithm was selected','Error'); - return; -end - -% Get accuracy of the validation set -accV = RegulationFeedbackAcc(connMat, vSets, vOuts, size(vOuts,2)); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to select the way in which the connectivity matrix will be +% created +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-09-26 / Max Ortiz / Creation +% 2012-03-29 / Max Ortiz / Added the option to use PSO for the connMat +% 20xx-xx-xx / Author / Comment on update + +function [connMat accV] = RegulationFeedback(tType, trSets, trOuts, vSets, vOuts) + +% Get connectivity Matrix +%connMat = Get_connMat(trSet, trOut); %Get connMat using all sets +if strcmp(tType,'Mean') + + connMat = Get_connMat_TrV(trSets, trOuts, vSets, vOuts); + +elseif strcmp(tType,'Mean + PSO') + + thO = 0; % Threshold output is only use in the RegFeedback_ThO + connMat = Get_connMat_TrV(trSets, trOuts, vSets, vOuts); + PSO = InitPSO_RFN(connMat); + [PSO, connMat] = PSO_RFN(PSO, connMat, vSets, vOuts, thO); + +elseif strcmp(tType,'Exclusive Mean') + + connMat = Get_connMat_eMean(trSets, trOuts, vSets, vOuts); + +else + errordlg('No training algorithm was selected','Error'); + return; +end + +% Get accuracy of the validation set +accV = RegulationFeedbackAcc(connMat, vSets, vOuts, size(vOuts,2)); diff --git a/PatRec/RFN/RegulationFeedbackAcc.m b/PatRec/RFN/RegulationFeedbackAcc.m index 1834d62..cec8be8 100644 --- a/PatRec/RFN/RegulationFeedbackAcc.m +++ b/PatRec/RFN/RegulationFeedbackAcc.m @@ -1,102 +1,102 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------- Function Description ------------- -% Compute the overal all Accuracy for a data set using RFN -% -% ------------- Updates ------------- -% [Contributors are welcome to add their email] -% 2011-09-26 / Max Ortiz / Created -% 2011-09-26 / Max Ortiz / Addition of nM and modification of the accuracy -% computation -% 2012-05-20 / Max Ortiz / Incorporated thOut to delete the additional -% thOut routines -% 20xx-xx-xx / Author / Comment on update - -function acc = RegulationFeedbackAcc(connMat, tSet, tOut, nM, thOut) - -% Variables -sM = size(tOut,1)/nM; % Sets per movement -good = zeros(size(tSet,1),1);% Keep track of the good prediction -nOut = size(tOut,2); % Number of outputs -accMat = zeros(nM,nOut); -rightOutMat = zeros(nM,nOut); -if ~exist('thOut','var') - thOut = []; -end - -for i = 1 : size(tSet,1) - - % Evaluate the data sets with he Regulation Feedback Algorithm - [outMov outVector] = RegulationFeedbackTest(connMat, tSet(i,:)',[],thOut); - -% % Quick and dirty routine to evaluate mixed-classes -% % IF it's known that more than two patterns are presented -% nExpMov = length(find(tOut(i,:))); -% if nExpMov ~= 1 % More than one movement -% [Y, I] = sort(outVector,'descend'); -% outMov = I(1:nExpMov)'; -% end - - %% Count the number of correct predictions - expectedOutIdx = find(tOut(i,:)); - if ~isempty(outMov) - if outMov ~= 0 - mask = zeros(1,nOut); - mask(outMov) = 1; - % Are these the right movements? - if tOut(i,:) == mask - good(i) = 1; - - %Confusion Matrix - % This will only considered perfect match of mixed-classes - % this must be modified to handle the accuracy of each - % pattern inside the mix - accMat(expectedOutIdx,outMov) = accMat(expectedOutIdx,outMov) + 1; - end -% %Evaluate only a single movement -% if tOut(i,outMov) == 1 -% good(i) = 1; -% end - end - end - - rightOutMat(expectedOutIdx,expectedOutIdx) = rightOutMat(expectedOutIdx,expectedOutIdx) + 1; - % This way of compute the accuracy and confMat is necessary for - % handling unbalanced data sets, such as in the case of mixed-classes - -end - -accMat = accMat ./ rightOutMat; -accMat(isinf(accMat))= NaN; -acc = nanmean(accMat,2); % This requires the statistics toolbox -% without the statistics toolbox -%accMat(isnan(accMat))= 0; -%acc = sum(accMat(accMat~=0),2)./sum(accMat~=0,2); - -acc(end+1) = mean(acc); - -% Compute the accuracy per movement -% Only work if the number of sets is the same per movement. -% acc = zeros(nM+1,1); -% for i = 1 : nM -% s = 1+((i-1)*sM); -% e = sM*i; -% acc(i) = sum(good(s:e))/sM; -% end -% acc(i+1) = sum(good) / length(tSet); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------- Function Description ------------- +% Compute the overal all Accuracy for a data set using RFN +% +% ------------- Updates ------------- +% [Contributors are welcome to add their email] +% 2011-09-26 / Max Ortiz / Created +% 2011-09-26 / Max Ortiz / Addition of nM and modification of the accuracy +% computation +% 2012-05-20 / Max Ortiz / Incorporated thOut to delete the additional +% thOut routines +% 20xx-xx-xx / Author / Comment on update + +function acc = RegulationFeedbackAcc(connMat, tSet, tOut, nM, thOut) + +% Variables +sM = size(tOut,1)/nM; % Sets per movement +good = zeros(size(tSet,1),1);% Keep track of the good prediction +nOut = size(tOut,2); % Number of outputs +accMat = zeros(nM,nOut); +rightOutMat = zeros(nM,nOut); +if ~exist('thOut','var') + thOut = []; +end + +for i = 1 : size(tSet,1) + + % Evaluate the data sets with he Regulation Feedback Algorithm + [outMov outVector] = RegulationFeedbackTest(connMat, tSet(i,:)',[],thOut); + +% % Quick and dirty routine to evaluate mixed-classes +% % IF it's known that more than two patterns are presented +% nExpMov = length(find(tOut(i,:))); +% if nExpMov ~= 1 % More than one movement +% [Y, I] = sort(outVector,'descend'); +% outMov = I(1:nExpMov)'; +% end + + %% Count the number of correct predictions + expectedOutIdx = find(tOut(i,:)); + if ~isempty(outMov) + if outMov ~= 0 + mask = zeros(1,nOut); + mask(outMov) = 1; + % Are these the right movements? + if tOut(i,:) == mask + good(i) = 1; + + %Confusion Matrix + % This will only considered perfect match of mixed-classes + % this must be modified to handle the accuracy of each + % pattern inside the mix + accMat(expectedOutIdx,outMov) = accMat(expectedOutIdx,outMov) + 1; + end +% %Evaluate only a single movement +% if tOut(i,outMov) == 1 +% good(i) = 1; +% end + end + end + + rightOutMat(expectedOutIdx,expectedOutIdx) = rightOutMat(expectedOutIdx,expectedOutIdx) + 1; + % This way of compute the accuracy and confMat is necessary for + % handling unbalanced data sets, such as in the case of mixed-classes + +end + +accMat = accMat ./ rightOutMat; +accMat(isinf(accMat))= NaN; +acc = nanmean(accMat,2); % This requires the statistics toolbox +% without the statistics toolbox +%accMat(isnan(accMat))= 0; +%acc = sum(accMat(accMat~=0),2)./sum(accMat~=0,2); + +acc(end+1) = mean(acc); + +% Compute the accuracy per movement +% Only work if the number of sets is the same per movement. +% acc = zeros(nM+1,1); +% for i = 1 : nM +% s = 1+((i-1)*sM); +% e = sM*i; +% acc(i) = sum(good(s:e))/sM; +% end +% acc(i+1) = sum(good) / length(tSet); + diff --git a/PatRec/RFN/RegulationFeedbackFit.m b/PatRec/RFN/RegulationFeedbackFit.m index b6d8973..862f1b3 100644 --- a/PatRec/RFN/RegulationFeedbackFit.m +++ b/PatRec/RFN/RegulationFeedbackFit.m @@ -1,66 +1,66 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------- Function Description ------------- -% Compute the overal all Accuracy for a data set using RFN -% -% ------------- Updates ------------- -% 2012-04-01 / Max Ortiz / Adapted from RegulationFeedbackAcc -% 20xx-xx-xx / Author / Comment on update - -function fitness = RegulationFeedbackFit(connMat, tSets, tOut, nM, thO) - -% Variables -% Compute fitness as the root mean square error of the outVector -nSets = size(tSets,1); -eSum = 0; - -% Compute fitness as the acuracy error -nOut = size(tOut,2); -good = zeros(size(tSets,1),1); % Keep track of the good prediction - -for i = 1 : size(tSets,1) - - % Evaluate the data sets with he Regulation Feedback Algorithm - if thO == 0 - [outMov outVector] = RegulationFeedbackTest(connMat, tSets(i,:)'); - else - [outMov outVector] = RegulationFeedbackTest_thOut(connMat, tSets(i,:)', thO); - end - % Compute fitness as the root mean square error of the OutVector - eSum = eSum + sum((outVector'-tOut(i,:)).^2); - - % Compute fitness as the acuracy error -% if ~isempty(outMov) -% if outMov ~= 0 -% mask = zeros(1,nOut); -% mask(outMov) = 1; -% % Are these the right movements? -% if tOut(i,:) == mask -% good(i) = 1; -% end -% end -% end - -end - -% Compute fitness as the root mean square error of the outVector -fitness = sqrt(eSum/(nSets*nM)); % for minimum - -% Compute fitness as the acuracy error -%fitness = 1-(sum(good)/nSets); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------- Function Description ------------- +% Compute the overal all Accuracy for a data set using RFN +% +% ------------- Updates ------------- +% 2012-04-01 / Max Ortiz / Adapted from RegulationFeedbackAcc +% 20xx-xx-xx / Author / Comment on update + +function fitness = RegulationFeedbackFit(connMat, tSets, tOut, nM, thO) + +% Variables +% Compute fitness as the root mean square error of the outVector +nSets = size(tSets,1); +eSum = 0; + +% Compute fitness as the acuracy error +nOut = size(tOut,2); +good = zeros(size(tSets,1),1); % Keep track of the good prediction + +for i = 1 : size(tSets,1) + + % Evaluate the data sets with he Regulation Feedback Algorithm + if thO == 0 + [outMov outVector] = RegulationFeedbackTest(connMat, tSets(i,:)'); + else + [outMov outVector] = RegulationFeedbackTest_thOut(connMat, tSets(i,:)', thO); + end + % Compute fitness as the root mean square error of the OutVector + eSum = eSum + sum((outVector'-tOut(i,:)).^2); + + % Compute fitness as the acuracy error +% if ~isempty(outMov) +% if outMov ~= 0 +% mask = zeros(1,nOut); +% mask(outMov) = 1; +% % Are these the right movements? +% if tOut(i,:) == mask +% good(i) = 1; +% end +% end +% end + +end + +% Compute fitness as the root mean square error of the outVector +fitness = sqrt(eSum/(nSets*nM)); % for minimum + +% Compute fitness as the acuracy error +%fitness = 1-(sum(good)/nSets); + diff --git a/PatRec/RFN/RegulationFeedbackTest.m b/PatRec/RFN/RegulationFeedbackTest.m index 6df7b60..74aa347 100644 --- a/PatRec/RFN/RegulationFeedbackTest.m +++ b/PatRec/RFN/RegulationFeedbackTest.m @@ -1,96 +1,96 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Regulation Feedback Algorithm created from the "rf_alg" originally -% by Tsvi Achler -% -% Inputs: -% connMat: The conectivity Matrix is made of a vector of features per pattern -% tVector: Test vector -% newY: New vector. In the absence of a suggested output, RFN randomly -% initializes an ouput in newY. -% thOut: If an output threshold is provided, it will -% consider it for the computation of the prediction. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-09-26 / Max Ortiz / Creation from "rf_alg" by Tsvi Achler -% 2011-10-09 / Max Ortiz / Added newY like an optional input -% 2012-04-01 / Max Ortiz / Ouput fixed to a single max value -% 2012-05-20 / Max Ortiz / Incorporated thOut to delete the additional -% thOut routines -% 20xx-xx-xx / Author / Comment on update - -% Regulatory Feedback Algorithm -function [outMov outVector unstable] = RegulationFeedbackTest(connMat, tVector, newY, thOut) - - numOutputs = size(connMat,1); % number of y's - steadyStateTolerance = 0.00001; % determines when steady state criteria -% salienceValues = zeros(size(tVector)); -% feedback = zeros(size(tVector)); - - itr = 0; - unstable = 0; - - numConnectionsInY = sum(connMat, 2); % calculate number of connections for normalization - - % NOTE: newY could be substitude by the output of the ANN to test - % its stability 2011-09-26 - if ~exist('newY','var') || isempty(newY) - newY = rand(numOutputs, 1);% random initial values for all y's. alternate: initial*ones(numOutputs, 1); initial = .1; -% newY = ones(numOutputs, 1);% random initial values for all y's. alternate: initial*ones(numOutputs, 1); initial = .1; - end - - oldY = Inf(numOutputs, 1); %start with infinity to overcome tolerance - - - % caluclulate new Y values and salience from old Y values and inputs. - while( sum(abs(newY - oldY)) > steadyStateTolerance ) %iterate until steady state - - itr = itr+1; %count number of iterations - oldY = newY; %advance t to t+1 - - feedback = connMat'*oldY; %determine feedback to inputs - - % It will cause an error if the feedback vector has zero values - % which comes form the connMat with only 0 values per column - salienceValues = tVector ./ feedback(feedback ~= 0); % calculate salience - newY = oldY .* (connMat * salienceValues) ./ numConnectionsInY; % calculate updated Y's - - %Watchdog - if itr == 200 - unstable = 1; - break; - end - end - outVector = newY; - - % Ouput using threshold? - if exist('thOut','var') - if ~isempty(thOut) - %mask(1:numOutputs) = thOut; - %outMov = find(outVector>mask'); - outMov = find(outVector>thOut'); - else - [maxV, outMov] = max(outVector); - end - else - [maxV, outMov] = max(outVector); - end -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Regulation Feedback Algorithm created from the "rf_alg" originally +% by Tsvi Achler +% +% Inputs: +% connMat: The conectivity Matrix is made of a vector of features per pattern +% tVector: Test vector +% newY: New vector. In the absence of a suggested output, RFN randomly +% initializes an ouput in newY. +% thOut: If an output threshold is provided, it will +% consider it for the computation of the prediction. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-09-26 / Max Ortiz / Creation from "rf_alg" by Tsvi Achler +% 2011-10-09 / Max Ortiz / Added newY like an optional input +% 2012-04-01 / Max Ortiz / Ouput fixed to a single max value +% 2012-05-20 / Max Ortiz / Incorporated thOut to delete the additional +% thOut routines +% 20xx-xx-xx / Author / Comment on update + +% Regulatory Feedback Algorithm +function [outMov outVector unstable] = RegulationFeedbackTest(connMat, tVector, newY, thOut) + + numOutputs = size(connMat,1); % number of y's + steadyStateTolerance = 0.00001; % determines when steady state criteria +% salienceValues = zeros(size(tVector)); +% feedback = zeros(size(tVector)); + + itr = 0; + unstable = 0; + + numConnectionsInY = sum(connMat, 2); % calculate number of connections for normalization + + % NOTE: newY could be substitude by the output of the ANN to test + % its stability 2011-09-26 + if ~exist('newY','var') || isempty(newY) + newY = rand(numOutputs, 1);% random initial values for all y's. alternate: initial*ones(numOutputs, 1); initial = .1; +% newY = ones(numOutputs, 1);% random initial values for all y's. alternate: initial*ones(numOutputs, 1); initial = .1; + end + + oldY = Inf(numOutputs, 1); %start with infinity to overcome tolerance + + + % caluclulate new Y values and salience from old Y values and inputs. + while( sum(abs(newY - oldY)) > steadyStateTolerance ) %iterate until steady state + + itr = itr+1; %count number of iterations + oldY = newY; %advance t to t+1 + + feedback = connMat'*oldY; %determine feedback to inputs + + % It will cause an error if the feedback vector has zero values + % which comes form the connMat with only 0 values per column + salienceValues = tVector ./ feedback(feedback ~= 0); % calculate salience + newY = oldY .* (connMat * salienceValues) ./ numConnectionsInY; % calculate updated Y's + + %Watchdog + if itr == 200 + unstable = 1; + break; + end + end + outVector = newY; + + % Ouput using threshold? + if exist('thOut','var') + if ~isempty(thOut) + %mask(1:numOutputs) = thOut; + %outMov = find(outVector>mask'); + outMov = find(outVector>thOut'); + else + [maxV, outMov] = max(outVector); + end + else + [maxV, outMov] = max(outVector); + end +end \ No newline at end of file diff --git a/PatRec/RFN/RegulationFeedback_thOut.m b/PatRec/RFN/RegulationFeedback_thOut.m index abf7a4f..aeb1328 100644 --- a/PatRec/RFN/RegulationFeedback_thOut.m +++ b/PatRec/RFN/RegulationFeedback_thOut.m @@ -1,59 +1,59 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Similar to RegulationFeedback but using a threshold to selected the -% predicted movements -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-09-26 / Max Ortiz / Creation -% 2012-03-29 / Max Ortiz / Divided for original version to _thOut -% 2012-05-20 / Max Ortiz / Eliminate the use of additional _thOut routines -% by incorporating thOut into the standard RFN *Acc -% and *Test routines -% 20xx-xx-xx / Author / Comment on update - -function [connMat accV thOut] = RegulationFeedback_thOut(tType, trSets, trOuts, vSets, vOuts) - -% Get connectivity Matrix -%connMat = Get_connMat(trSet, trOut); -%connMat = Get_connMat_TrV(trSets, trOuts, vSets, vOuts); -connMat = RegulationFeedback(tType, trSets, trOuts, vSets, vOuts); - -% Hard-coded threshold -thOut = 0.45; -% Brute force methode to calculate the threshold -bestAcc = 0; -%bestVar = Inf; -for i = .4:0.01:.6 - %accV = RegulationFeedbackAcc_thOut(connMat, i, vSets, vOuts, size(vOuts,2)); - accV = RegulationFeedbackAcc(connMat, vSets, vOuts, size(vOuts,2), i); -% varV = var(accV(1:end-1)); - if accV(end) >= bestAcc %&& varV <= bestVar - bestAcc = accV(end); -% bestVar =varV; - thOut = i; - end -end - -disp(bestAcc); -disp(thOut); - -% Get accuracy of the validation set -%accV = RegulationFeedbackAcc_thOut(connMat, thOut, vSets, vOuts, size(vOuts,2)); -accV = RegulationFeedbackAcc(connMat, vSets, vOuts, size(vOuts,2), thOut); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Similar to RegulationFeedback but using a threshold to selected the +% predicted movements +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-09-26 / Max Ortiz / Creation +% 2012-03-29 / Max Ortiz / Divided for original version to _thOut +% 2012-05-20 / Max Ortiz / Eliminate the use of additional _thOut routines +% by incorporating thOut into the standard RFN *Acc +% and *Test routines +% 20xx-xx-xx / Author / Comment on update + +function [connMat accV thOut] = RegulationFeedback_thOut(tType, trSets, trOuts, vSets, vOuts) + +% Get connectivity Matrix +%connMat = Get_connMat(trSet, trOut); +%connMat = Get_connMat_TrV(trSets, trOuts, vSets, vOuts); +connMat = RegulationFeedback(tType, trSets, trOuts, vSets, vOuts); + +% Hard-coded threshold +thOut = 0.45; +% Brute force methode to calculate the threshold +bestAcc = 0; +%bestVar = Inf; +for i = .4:0.01:.6 + %accV = RegulationFeedbackAcc_thOut(connMat, i, vSets, vOuts, size(vOuts,2)); + accV = RegulationFeedbackAcc(connMat, vSets, vOuts, size(vOuts,2), i); +% varV = var(accV(1:end-1)); + if accV(end) >= bestAcc %&& varV <= bestVar + bestAcc = accV(end); +% bestVar =varV; + thOut = i; + end +end + +disp(bestAcc); +disp(thOut); + +% Get accuracy of the validation set +%accV = RegulationFeedbackAcc_thOut(connMat, thOut, vSets, vOuts, size(vOuts,2)); +accV = RegulationFeedbackAcc(connMat, vSets, vOuts, size(vOuts,2), thOut); diff --git a/PatRec/Rand_TrainingData.m b/PatRec/Rand_TrainingData.m index 1eb8029..cbd8ac6 100644 --- a/PatRec/Rand_TrainingData.m +++ b/PatRec/Rand_TrainingData.m @@ -1,46 +1,46 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to randomize the order of the validation data -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Ortiz / Creation -% 2009-07-21 / Max Ortiz / Change to use the treated_data struct -% 2011-06-23 / Max Ortiz / New names and coding standard -% 2011-07-19 / Max Ortiz / New names and coding standard to new struct -% sigFeatures - -function sigFeatures = Rand_TrainingData(sigFeatures ) - -temp(1:sigFeatures.trSets,:) = sigFeatures.trFeatures; -temp(sigFeatures.trSets + 1 : sigFeatures.trSets + sigFeatures.vSets,:) = sigFeatures.vFeatures; -temp(sigFeatures.trSets + sigFeatures.vSets + 1 : sigFeatures.trSets + sigFeatures.vSets + sigFeatures.tSets,:) = sigFeatures.tFeatures; - -temp2 = temp; - -for i = 1 : size(temp,1) - j = round(1 + rand * (size(temp,1)-1)); - temp(i,:) = temp2(j,:); - temp(j,:) = temp2(i,:); - temp2 = temp; -end - -sigFeatures.trFeatures = temp(1:sigFeatures.trSets,:); -sigFeatures.vFeatures = temp(sigFeatures.trSets+1:sigFeatures.trSets+sigFeatures.vSets,:); -sigFeatures.tFeatures = temp(sigFeatures.trSets+sigFeatures.vSets+1:sigFeatures.trSets+sigFeatures.vSets+sigFeatures.tSets,:); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to randomize the order of the validation data +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Ortiz / Creation +% 2009-07-21 / Max Ortiz / Change to use the treated_data struct +% 2011-06-23 / Max Ortiz / New names and coding standard +% 2011-07-19 / Max Ortiz / New names and coding standard to new struct +% sigFeatures + +function sigFeatures = Rand_TrainingData(sigFeatures ) + +temp(1:sigFeatures.trSets,:) = sigFeatures.trFeatures; +temp(sigFeatures.trSets + 1 : sigFeatures.trSets + sigFeatures.vSets,:) = sigFeatures.vFeatures; +temp(sigFeatures.trSets + sigFeatures.vSets + 1 : sigFeatures.trSets + sigFeatures.vSets + sigFeatures.tSets,:) = sigFeatures.tFeatures; + +temp2 = temp; + +for i = 1 : size(temp,1) + j = round(1 + rand * (size(temp,1)-1)); + temp(i,:) = temp2(j,:); + temp(j,:) = temp2(i,:); + temp2 = temp; +end + +sigFeatures.trFeatures = temp(1:sigFeatures.trSets,:); +sigFeatures.vFeatures = temp(sigFeatures.trSets+1:sigFeatures.trSets+sigFeatures.vSets,:); +sigFeatures.tFeatures = temp(sigFeatures.trSets+sigFeatures.vSets+1:sigFeatures.trSets+sigFeatures.vSets+sigFeatures.tSets,:); diff --git a/PatRec/Rand_sigFeatures.m b/PatRec/Rand_sigFeatures.m index c79f6ca..45e23fd 100644 --- a/PatRec/Rand_sigFeatures.m +++ b/PatRec/Rand_sigFeatures.m @@ -1,47 +1,47 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to randomize the order of the validation data -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Ortiz / Creation -% 2009-07-21 / Max Ortiz / Change to use the treated_data struct -% 2011-06-23 / Max Ortiz / New names and coding standard -% 2011-07-19 / Max Ortiz / New names and coding standard to new struct -% sigFeatures -% - -function sigFeatures = Rand_sigFeatures(sigFeatures) - -temp(1:sigFeatures.trSets,:) = sigFeatures.trFeatures; -temp(sigFeatures.trSets + 1 : sigFeatures.trSets + sigFeatures.vSets,:) = sigFeatures.vFeatures; -temp(sigFeatures.trSets + sigFeatures.vSets + 1 : sigFeatures.trSets + sigFeatures.vSets + sigFeatures.tSets,:) = sigFeatures.tFeatures; - -temp2 = temp; - -for i = 1 : size(temp,1) - j = round(1 + rand * (size(temp,1)-1)); - temp(i,:) = temp2(j,:); - temp(j,:) = temp2(i,:); - temp2 = temp; -end - -sigFeatures.trFeatures = temp(1:sigFeatures.trSets,:); -sigFeatures.vFeatures = temp(sigFeatures.trSets+1:sigFeatures.trSets+sigFeatures.vSets,:); -sigFeatures.tFeatures = temp(sigFeatures.trSets+sigFeatures.vSets+1:sigFeatures.trSets+sigFeatures.vSets+sigFeatures.tSets,:); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to randomize the order of the validation data +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Ortiz / Creation +% 2009-07-21 / Max Ortiz / Change to use the treated_data struct +% 2011-06-23 / Max Ortiz / New names and coding standard +% 2011-07-19 / Max Ortiz / New names and coding standard to new struct +% sigFeatures +% + +function sigFeatures = Rand_sigFeatures(sigFeatures) + +temp(1:sigFeatures.trSets,:) = sigFeatures.trFeatures; +temp(sigFeatures.trSets + 1 : sigFeatures.trSets + sigFeatures.vSets,:) = sigFeatures.vFeatures; +temp(sigFeatures.trSets + sigFeatures.vSets + 1 : sigFeatures.trSets + sigFeatures.vSets + sigFeatures.tSets,:) = sigFeatures.tFeatures; + +temp2 = temp; + +for i = 1 : size(temp,1) + j = round(1 + rand * (size(temp,1)-1)); + temp(i,:) = temp2(j,:); + temp(j,:) = temp2(i,:); + temp2 = temp; +end + +sigFeatures.trFeatures = temp(1:sigFeatures.trSets,:); +sigFeatures.vFeatures = temp(sigFeatures.trSets+1:sigFeatures.trSets+sigFeatures.vSets,:); +sigFeatures.tFeatures = temp(sigFeatures.trSets+sigFeatures.vSets+1:sigFeatures.trSets+sigFeatures.vSets+sigFeatures.tSets,:); diff --git a/PatRec/RealtimePatRec.m b/PatRec/RealtimePatRec.m index 4648d29..7ad7b10 100644 --- a/PatRec/RealtimePatRec.m +++ b/PatRec/RealtimePatRec.m @@ -1,357 +1,394 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to execute real-time patrec using the offline trained algorithm -% which information is stored in patRec -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-07-27 / Max Ortiz / Creation -% 2011-08-03 / Max Ortiz / Separation of "SignalProcessing_RealtimePatRec" -% 2011-11-17 / Max Ortiz / Added the motors coupling -% 2011-12-07 / Nichlas Sander / Added VRE Coupling and VRE commands. -% 2011-12-07 / Max Ortiz / Upgrade to Session-based interface for DAQ,the -% prevous version was called RealtimePatRec_Legacy.m -% 2012-08-13 / Morten Kristoffersen / Added GUI interface to see and control -% the threshold values for the MLP ThOut algorithm. -% 2012-10-05 / Joel Falk-Dahlin / Changed inputs and outputs of -% ApplyControl to work with updated version. -% Removed InitControl since it is -% performed inside the GUI -% 2012-10-26 / Joel Falk-Dahlin / Added ApplyProportionalControl -% 2012-11-06 / Max Ortiz / Create RealimePatRec_OneShot to concentrate -% critical routines of realtimepatrec -% 2012-11-23 / Joel Falk-Dahlin / Moved speeds from handles to patRec, -% changed VRE to use the new speeds - -% 20xx-xx-xx / Author / Comment on update -function handlesX = RealtimePatRec(patRecX, handlesX) - -clear global; -clear persistent; - -global patRec; -global handles; -global nTW; -global procT; -global motorCoupling; -global vreCoupling; -global pwmIDs; -global pwmAs; -global pwmBs; -global tempData; -global outVectorMotorLast; -%global data; % only needed for testing -global thresholdGUIData; - - -patRec = patRecX; -handles = handlesX; -nTW = 1; -procT = []; -tempData = []; -outVectorMotorLast = zeros(patRec.nOuts,1); -pDiv = 2; % Peeking devider, the fastest allowed for the DAQ - % which is 20 times per second, so 0.05 for 2k Hz -% Get sampling time -sT = str2double(get(handles.et_testingT,'String')); - -% Initialze control algorithms -%patRec = InitControl(patRec); % No longer needed, Initialization is done in GUI - -%% Is threshold (thOut) used? -if(isfield(patRec.patRecTrained,'thOut')); - %Threshold GUI init - thresholdGUI = GUI_Threshold; - thresholdGUIData = guidata(thresholdGUI); - set(GUI_Threshold,'CloseRequestFcn', 'set(GUI_Threshold, ''Visible'', ''off'')'); - xpatch = [1 1 0 0]; - ypatch = [0 0 0 0]; - for i=0:patRec.nOuts-1 - s = sprintf('movementSelector%d',i); - s0 = sprintf('thPatch%d',i); - s1 = sprintf('meter%d',i); - axes(thresholdGUIData.(s1)); - handles.(s0) = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','normal','visible','on'); - ylim(thresholdGUIData.(s1), [0 1]); - xlim('auto'); - set(thresholdGUIData.(s),'String',patRec.mov(patRec.indMovIdx)); - if (size(patRec.mov(patRec.indMovIdx),1) < i+1); - set(thresholdGUIData.(s),'Value',size(patRec.indMovIdx,2)); - else - set(thresholdGUIData.(s),'Value',i+1); - end - end -end - -%% Is the VRE selected? -vreCoupling = 0; -if isfield(handles,'vre_Com'); - %If there is a vre communication, is it open? - vreCoupling = strcmp(handles.vre_Com.Status,'open'); -end - - -%% Is there an option for coupling with the motors? -if isfield(handles,'cb_motorCoupling') %&& ~isfield(handles,'com') - motorCoupling = get(handles.cb_motorCoupling,'Value'); -else - motorCoupling = 0; -end - -%Is the motor coupling selected? -if motorCoupling - - %Initialize - motorIdx = zeros(1,10); - pwmAs = zeros(1,10); - pwmBs = zeros(1,10); - movSpeeds = zeros(1,10); - - % Get the links to the motors - for i = 1 : size(motorIdx,2) - pmID = ['handles.pm_m' num2str(i)]; - motorIdx(i) = get(eval(pmID),'Value'); - speedID = ['handles.et_speed' num2str(i)]; - movSpeeds(i) = str2double(get(eval(speedID),'String')); - end - - % Init variables for control - pwmIDs = ['A';'A';'B';'B';'C';'C';'D';'D';'E';'E']; - for i = 1 : 2 : size(pwmIDs) - pwmAs(i) = movSpeeds(i); - pwmBs(i) = 0; - pwmAs(i+1) = 0; - pwmBs(i+1) = movSpeeds(i+1); - end; - - % Arrenge according to selection - pwmIDs = pwmIDs(motorIdx); - pwmAs = pwmAs(motorIdx); - pwmBs = pwmBs(motorIdx); - -end - -%% Initialize DAQ card -% Note: A function for DAQ selection will be required when more cards are -% added -if strcmp(patRec.dev,'ADH') - -else % at this poin everything else is the NI - USB6009 - chAI = zeros(1,8); % 8 ch is the limit for the USB6009 - chAI(patRec.nCh) = 1; - %Init the SBI - s = InitSBI_NI(patRec.sF,sT,chAI); - % Change the interruption time - s.NotifyWhenDataAvailableExceeds = (patRec.sF*patRec.tW)/pDiv; - lh = s.addlistener('DataAvailable', @RealtimePatRec_OneShot); - %Test the DAQ by ploting the data - %lh = s.addlistener('DataAvailable', @plotDataTest); -end - - -%% Run the Realtime PatRec -% Note: Probabily this way of testing only works for the NI -% Specific funtions per card might be required. -s.startBackground(); - -% Wait until it has finished done -%s.IsDone % will report 0 -s.wait(); % rather than while -%s.IsDone % will report 1 - -%% Finish session -%Delete listener SBI -delete (lh) - -%Stop motors -if motorCoupling - if(isfield(handles,'movList')) - for i=1 : length(handles.movList) - [handles.motors, ~] = MotorsOff(handles.com, handles.movList(i), handles.motors); - end - else - ActivateMotors(handles.com, pwmIDs, pwmAs, pwmBs, 0); - end -end - -% Write the average processing time -set(handles.et_avgProcTime,'String',num2str(mean(procT(2:end)))); - -handlesX = handles; - -%Reset Threshold GUI -if(isfield(patRec.patRecTrained,'thOut')); - for i=0:patRec.nOuts-1 - s0 = sprintf('thPatch%d',i); - set(handles.(s0), 'YData', [0 0 0 0]); - %delete(GUI_Threshold); - end -end - -%Plot processing time -figure(); -hist(procT(2:end)); - -end - -function RealtimePatRec_OneShot(src,event) - - global patRec; - global nTW; - global procT; - global motorCoupling; - global vreCoupling; - global handles; - global TAC; - global pwmIDs; - global pwmAs; - global pwmBs; - global tempData; - global outVectorMotorLast; - - global thresholdGUIData; - - %Keep saving all recorded data - tempData = [tempData; event.Data]; - - %Output vector - outVectorMotor = zeros(patRec.nOuts,1); - - %Only considered the data once it has at least the size of time window - if size(tempData,1) >= (patRec.sF*patRec.tW) - - tData = tempData(end-patRec.sF*patRec.tW+1:end,:); %Copy the temporal data to the test data - - % Start of processing time - procTimeS = tic; - - % General routine for RealtimePatRec - [outMov outVector patRec handles] = OneShotRealtimePatRec(tData, patRec, handles, thresholdGUIData); - - % GUI and control Updates - % Game control - if get(handles.cb_keys,'Value') && outMov(1) ~= 0 - keys = zeros(patRec.nM,1); - keys(outMov) = 1; - savedKeys = []; - if isfield(handles,'savedKeys') - savedKeys = handles.savedKeys; - end - SendKeys(keys,savedKeys); - end - - % Send vector to the motors - if(isfield(handles,'movList')) - if motorCoupling - % Update outvectorMotor - outVectorMotor(outMov) = 1; - % Check if the state has change - outChanged = find(xor(outVectorMotor, outVectorMotorLast)); - % Only send information to the motors that have changed - % state - for i = 1 : size(outChanged,1) - if outVectorMotor(outChanged(i)) - [handles.motors, ~] = MotorsOn(handles.com, handles.movList(outChanged(i)), handles.motors, patRec.control.currentDegPerMov(outChanged(i))); - else - [handles.motors, ~] = MotorsOff(handles.com, handles.movList(outChanged(i)), handles.motors); - end - end - end - - % To slow for hand open and close since it continuesly send - % data to the microcontroller -% perfMov = handles.movList(outMov); -% if motorCoupling -% %Activate motors here using MoveMotor (for now). -% [handles.motors, ~] = MoveMotor(handles.com, perfMov, 1, z§handles.motors); -% end - - - % VRE - if vreCoupling - for i = 1:length(outMov) - speed = patRec.control.currentDegPerMov(outMov(i)); - perfMov = handles.movList(outMov(i)); - if VREActivation(handles.vre_Com, speed, [], perfMov.idVRE, perfMov.vreDir, get(handles.cb_moveTAC,'Value')) - TAC.acktimes = TAC.acktimes + 1; - end - end - end - else % alternative way for motor control - if motorCoupling - ActivateMotors(handles.com, pwmIDs, pwmAs, pwmBs, outMov); - end - - if vreCoupling - dof = round(outMov/2); - dofs = [9,7,8,0,10]; - dir = mod(outMov,2); - - if (dof == 1 && dir == 1) - dof = 5; - end - - if (dir == 1) - dir = 0; - else - dir = 1; - end - - if VREActivation(handles.vre_Com, 25, [], dofs(dof), dir, get(handles.cb_moveTAC,'Value')) == 1 - % TAC Test Completed - TAC.ackTimes = TAC.ackTimes + 1; - end - end - end - - % Next cycle - nTW = nTW + 1; - outVectorMotorLast = outVectorMotor; - % Finish of processing time - procT(nTW) = toc(procTimeS); - - % quick and dirty patch to keep the servos moving - % this is provisional - if motorCoupling - for i = 1 : size(outChanged,1) - if or(handles.movList(outChanged(i)).motor == 8, ... - handles.movList(outChanged(i)).motor == 7) - outVectorMotorLast(outChanged(i)) = 0; - end - end - end - end - end - - function plotDataTest(src,event) - persistent tempData; - global data - if(isempty(tempData)) - tempData = []; - figure(); - end - plot(event.TimeStamps, event.Data) - tempData = [tempData;event.Data]; - data = tempData; - end - - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to execute real-time patrec using the offline trained algorithm +% which information is stored in patRec +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-27 / Max Ortiz / Creation +% 2011-08-03 / Max Ortiz / Separation of "SignalProcessing_RealtimePatRec" +% 2011-11-17 / Max Ortiz / Added the motors coupling +% 2011-12-07 / Nichlas Sander / Added VRE Coupling and VRE commands. +% 2011-12-07 / Max Ortiz / Upgrade to Session-based interface for DAQ, the +% prevous version was called RealtimePatRec_Legacy.m +% 2012-08-13 / Morten Kristoffersen / Added GUI interface to see and control +% the threshold values for the MLP ThOut algorithm. +% 2012-10-05 / Joel Falk-Dahlin / Changed inputs and outputs of +% ApplyControl to work with updated version. +% Removed InitControl since it is +% performed inside the GUI +% 2012-10-26 / Joel Falk-Dahlin / Added ApplyProportionalControl +% 2012-11-06 / Max Ortiz / Create RealimePatRec_OneShot to concentrate +% critical routines of realtimepatrec +% 2012-11-23 / Joel Falk-Dahlin / Moved speeds from handles to patRec, +% changed VRE to use the new speeds +% 2013-04-17 / Max Ortiz / Made independent routines for control of +% differend devices +% 2013-06-03 / Max Ortiz / Addition of necessary routines to control +% Standard proshetic components (SPC) using wifi. +% 2014-11-12 / Enzo Mastinu / Added code for ADS1299 AFE RealTime PatRec +% 2015-01-20 / Enzo Mastinu / All this function has been re-organized +% to be compatible with the functions +% placed into COMM/AFE folder. For more +% details read about RecordingSession.m with +% other custom devices for BioPatRec_TRE. + +% 20xx-xx-xx / Author / Comment on update + +function handlesX = RealtimePatRec(patRecX, handlesX) + + clear global; + clear persistent; + + global patRec; + global handles; + global nTW; + global procT; + global motorCoupling; + global vreCoupling; + global pwmIDs; + global pwmAs; + global pwmBs; + global tempData; + global outVectorMotorLast; + + %global data; % only needed for testing + global thresholdGUIData; + + patRec = patRecX; + handles = handlesX; + nTW = 1; + procT = []; + tempData = []; + outVectorMotorLast = zeros(patRec.nOuts,1); + + % Get needed info from patRec structure + sF = patRec.sF; + nCh = length(patRec.nCh); + ComPortType = patRec.comm; + deviceName = patRec.dev; + + % Get sampling time + sT = str2double(get(handles.et_testingT,'String')); + tW = sT/100; % Time window size + tWs = tW*sF; % Time window samples + + + % Initialze control algorithms + %patRec = InitControl(patRec); % No longer needed, Initialization is done in GUI + + %% Is threshold (thOut) used? + if(isfield(patRec.patRecTrained,'thOut')); + %Threshold GUI init + thresholdGUI = GUI_Threshold; + thresholdGUIData = guidata(thresholdGUI); + set(GUI_Threshold,'CloseRequestFcn', 'set(GUI_Threshold, ''Visible'', ''off'')'); + xpatch = [1 1 0 0]; + ypatch = [0 0 0 0]; + for i=0:patRec.nOuts-1 + s = sprintf('movementSelector%d',i); + s0 = sprintf('thPatch%d',i); + s1 = sprintf('meter%d',i); + axes(thresholdGUIData.(s1)); + handles.(s0) = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','normal','visible','on'); + ylim(thresholdGUIData.(s1), [0 1]); + xlim('auto'); + set(thresholdGUIData.(s),'String',patRec.mov(patRec.indMovIdx)); + if (size(patRec.mov(patRec.indMovIdx),1) < i+1); + set(thresholdGUIData.(s),'Value',size(patRec.indMovIdx,2)); + else + set(thresholdGUIData.(s),'Value',i+1); + end + end + end + + %% Is the VRE selected? + vreCoupling = 0; + if isfield(handles,'vre_Com'); + %If there is a vre communication, is it open? + vreCoupling = strcmp(handles.vre_Com.Status,'open'); + end + + %% Is there an option for coupling with the motors? + if isfield(handles,'cb_motorCoupling') %&& ~isfield(handles,'com') + motorCoupling = get(handles.cb_motorCoupling,'Value'); + else + motorCoupling = 0; + end + +% % Init variables for hard-coded control (not using objects) %% CHECK +% if motorCoupling && ~isfield(handles,'movList') +% if deviceID == 1 % Multifunctinal prosthesis with DC motors +% [pwmIDs pwmAs pwmBs] = InitMF_Hand_DC_Hardcoded(handles); +% elseif deviceID == 3 % Standard prosthetic units (wireless) +% +% end +% end + + % Get connection object for control external robotic devices + if motorCoupling + if ~isfield(handles,'Control_obj') + set(handles.t_msg,'String','No connection obj found'); + return; + end + if ~strcmp(handles.Control_obj.status,'open') + fopen(handles.Control_obj); + end + end + + cData = zeros(tWs,nCh); + + %%%%% Real Time PatRec with NI DAQ card %%%%% + if strcmp (ComPortType, 'NI') + + % Init SBI + sCh = 1:nCh; + s = InitSBI_NI(sF,sT,sCh); + s.NotifyWhenDataAvailableExceeds = tWs; % PEEK time + lh = s.addlistener('DataAvailable', @RealtimePatRec_OneShot); + + % Start DAQ + s.startBackground(); % Run in the backgroud + + if ~s.IsDone % check if is done + s.wait(); + end + delete(lh); + + %%%%% Real Time PatRec with other custom device %%%%% + else + + % Prepare handles for next function calls + handles.deviceName = deviceName; + handles.ComPortType = ComPortType; + if strcmp (ComPortType, 'COM') + handles.ComPortName = patRec.comn; + end + handles.sF = sF; + handles.sT = sT; + handles.nCh = nCh; + handles.sTall = sT; + + % Delete connection object + if isfield(handles,'obj') + delete(handles.obj); + end + + % Connect the chosen device, it returns the connection object + obj = ConnectDevice(handles); + handles.obj = obj; + + % Set the selected device and Start the acquisition + SetDeviceStartAcquisition(handles, obj); + + for timeWindowNr = 1:sT/tW + + cData = Acquire_tWs(deviceName, obj, nCh, tWs); % acquire a new time window of samples + acquireEvent.Data = cData; + RealtimePatRec_OneShot(0, acquireEvent); + end + + % Stop acquisition + StopAcquisition(deviceName, obj); + end + + %Stop motors + if motorCoupling + if ~strcmp(handles.Control_obj.status,'open') + fopen(handles.Control_obj); + end + if(isfield(handles,'movList')) % Is the movement obj used? + for i=1 : length(handles.movList) + [handles.motors, ~] = MotorsOff(handles.Control_obj, handles.movList(i), handles.motors); + end + else % from hardcoded GUI GUI_TestPatRec + ActivateMotors(handles.com, pwmIDs, pwmAs, pwmBs, 0); + end + end + + % Write the average processing time + set(handles.et_avgProcTime,'String',num2str(mean(procT(2:end)))); + + handlesX = handles; + + %Reset Threshold GUI + if(isfield(patRec.patRecTrained,'thOut')); + for i=0:patRec.nOuts-1 + s0 = sprintf('thPatch%d',i); + set(handles.(s0), 'YData', [0 0 0 0]); + %delete(GUI_Threshold); + end + end + + %Plot processing time + figure(); + hist(procT(2:end)); + set(handles.pb_RealtimePatRec, 'Enable', 'on'); +end + + +function RealtimePatRec_OneShot(src,event) + + global patRec; + global nTW; + global procT; + global motorCoupling; + global vreCoupling; + global handles; + global TAC; + global pwmIDs; + global pwmAs; + global pwmBs; + global tempData; + global outVectorMotorLast; + global thresholdGUIData; + + %Keep saving all recorded data + tempData = [tempData; event.Data]; + + %Output vector + outVectorMotor = zeros(patRec.nOuts,1); + + %Only considered the data once it has at least the size of time window + if size(tempData,1) >= (patRec.sF*patRec.tW) + + tData = tempData(end-patRec.sF*patRec.tW+1:end,:); %Copy the temporal data to the test data + + % Start of processing time + procTimeS = tic; + + % General routine for RealtimePatRec + [outMov outVector patRec handles] = OneShotRealtimePatRec(tData, patRec, handles, thresholdGUIData); + + % GUI and control Updates + + % Game control + if get(handles.cb_keys,'Value') && outMov(1) ~= 0 + keys = zeros(patRec.nM,1); + keys(outMov) = 1; + savedKeys = []; + if isfield(handles,'savedKeys') + savedKeys = handles.savedKeys; + end + SendKeys(keys,savedKeys); + end + + + % Robot control + if get(handles.cb_controls,'Value') &&outMov(1) ~=0 + controls = zeros(patRec.nM,1); + controls(outMov) = 1; + savedControls = []; + if isfield(handles,'savedControls') + savedControls = handles.savedControls; + end + SendControls(controls,savedControls); + end + + % Send vector to the motors + if(isfield(handles,'movList')) + if motorCoupling + % Update outvectorMotor + outVectorMotor(outMov) = 1; + % Check if the state has change + outChanged = find(xor(outVectorMotor, outVectorMotorLast)); + % Only send information to the motors that have changed state + for i = 1 : size(outChanged,1) + if outVectorMotor(outChanged(i)) + [handles.motors, ~] = MotorsOn(handles.Control_obj, handles.movList(outChanged(i)), handles.motors, patRec.control.currentDegPerMov(outChanged(i))); + else + [handles.motors, ~] = MotorsOff(handles.Control_obj, handles.movList(outChanged(i)), handles.motors); + end + end + end + + % VRE + if vreCoupling + for i = 1:length(outMov) + speed = patRec.control.currentDegPerMov(outMov(i)); + perfMov = handles.movList(outMov(i)); + if VREActivation(handles.vre_Com, speed, [], perfMov.idVRE, perfMov.vreDir, get(handles.cb_moveTAC,'Value')) + TAC.acktimes = TAC.acktimes + 1; + end + end + end + else % alternative way for motor control (hard-coded) + if motorCoupling + ActivateMotors(handles.com, pwmIDs, pwmAs, pwmBs, outMov); + end + + if vreCoupling + dof = round(outMov/2); + dofs = [9,7,8,0,10]; + dir = mod(outMov,2); + + if (dof == 1 && dir == 1) + dof = 5; + end + + if (dir == 1) + dir = 0; + else + dir = 1; + end + + if VREActivation(handles.vre_Com, 25, [], dofs(dof), dir, get(handles.cb_moveTAC,'Value')) == 1 + % TAC Test Completed + TAC.ackTimes = TAC.ackTimes + 1; + end + end + end + + % Next cycle + nTW = nTW + 1; + outVectorMotorLast = outVectorMotor; + % Finish of processing time + procT(nTW) = toc(procTimeS); + + % quick and dirty patch to keep the servos moving + % this is provisional + if motorCoupling + for i = 1 : size(outChanged,1) + if or(handles.movList(outChanged(i)).motor == 8, ... + handles.movList(outChanged(i)).motor == 7) + outVectorMotorLast(outChanged(i)) = 0; + end + end + end + + + end + end + + function plotDataTest(src,event) + persistent tempData; + global data + if(isempty(tempData)) + tempData = []; + figure(); + end + plot(event.TimeStamps, event.Data) + tempData = [tempData;event.Data]; + data = tempData; + end + + + + + diff --git a/PatRec/RealtimePatRec_Legacy.m b/PatRec/RealtimePatRec_Legacy.m index d7c07f7..f76036b 100644 --- a/PatRec/RealtimePatRec_Legacy.m +++ b/PatRec/RealtimePatRec_Legacy.m @@ -1,168 +1,168 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% Function to execute real-time patrec using the offline trained algorithm -% which information is stored in patRec -% -% --------------------------Updates-------------------------- -% 2011-07-27 / Max Ortiz / Creation -% 2011-08-03 / Max Ortiz / Separation of "SignalProcessing_RealtimePatRec" -% 2011-11-17 / Max Ortiz / Added the motors coupling -% 2011-12-07 / Nichlas Sander / Added VRE Coupling and VRE commands. - -function RealtimePatRec(patRec, handles) - -% Get sampling time -sT = str2double(get(handles.et_testingT,'String')); - -%% Is there an option for coupling with the motors? -if isfield(handles,'cb_motorCoupling') %&& ~isfield(handles,'com') - motorCoupling = get(handles.cb_motorCoupling,'Value'); -else - motorCoupling = 0; -end - -if isfield(handles,'cb_VRE') %&& ~isfield(handles,'com') - vreCoupling = get(handles.cb_VRE,'Value'); -else - vreCoupling = 0; -end - -%Is the motor coupling selected? -if motorCoupling - - %Initialize - motorIdx = zeros(1,10); - pwmAs = zeros(1,10); - pwmBs = zeros(1,10); - movSpeeds = zeros(1,10); - - % Get the links to the motors - for i = 1 : size(motorIdx,2) - pmID = ['handles.pm_m' num2str(i)]; - motorIdx(i) = get(eval(pmID),'Value'); - speedID = ['handles.et_speed' num2str(i)]; - movSpeeds(i) = str2double(get(eval(speedID),'String')); - end - - % Init variables for control - pwmIDs = ['A';'A';'B';'B';'C';'C';'D';'D';'E';'E']; - for i = 1 : 2 : size(pwmIDs) - pwmAs(i) = movSpeeds(i); - pwmBs(i) = 0; - pwmAs(i+1) = 0; - pwmBs(i+1) = movSpeeds(i+1); - end; - - % Arrenge according to selection - pwmIDs = pwmIDs(motorIdx); - pwmAs = pwmAs(motorIdx); - pwmBs = pwmBs(motorIdx); - -end - -%% Initialize DAQ card -% Note: A function for DAQ selection will be required when more cards are -% added -if strcmp(patRec.dev,'ADH') - -else % at this poin everything else is the NI - USB6009 - chAI = zeros(1,8); - chAI(patRec.nCh) = 1; - %ai = InitNI(patRec.sF,sT,chAI); - s = InitSBI(patRec.sF,sT,chAI); - lh = s.addlistener('DataAvailable', @plotData); - - % Change the peek time - s.NotifyWhenDataAvailableExceeds = patRec.sF*patRec.tW; -end - -%% Testing -% Note: Probabily this way of testing only works for the NI -% Specific funtions per card might be required. - -start(ai); % Start the daya acquisition -nTW = 1; % Number of time windows - -% Wait until the first samples are aquired -while ai.SamplesAcquired < patRec.sF*patRec.tW -end -% start DAQ -while ai.SamplesAcquired < patRec.sF*sT - - % Processing time start - procTimeS = tic; - - %Data Peek - data = peekdata(ai,patRec.sF*patRec.tW); - - %Signal processing - tSet = SignalProcessing_RealtimePatRec(data, patRec); - - % One shoot PatRec - [outMov outVector] = OneShotPatRecClassifier(patRec, tSet); - % [outMov outVector] = OneShotPatRec(patRec.patRecTrained, tSet); - - % Quick patch for simultaneous rec using neural nets -% if strcmp(patRec.patRecTrained.algorithm,'ANN') -% outMov = find(round(outVector)); -% if isempty(outMov) -% outMov = size(outVector,1); -% end -% end - -% if strcmp(patRec.algorithm,'ANN'); -% -% elseif strcmp(patRec.algorithm,'DA') -% outMov = DiscriminantTest(patRec.coeff, tSet, patRec.training); -% end - - % Compute time - procT(nTW) = toc(procTimeS); - - % Show results - set(handles.lb_movements,'Value',outMov); - drawnow; - - % Send vector to the motors - if motorCoupling - ActivateMotors(handles.com, pwmIDs, pwmAs, pwmBs, outMov); - end - if vreCoupling - dof = round(outMov/2); - dir = mod(outMov,2); - VREActivation(handles.vre_Com, 10, 0.1, dof, dir) - end - % Next cycle - nTW = nTW + 1; - -end -%% Finish session -%Stop motors -if motorCoupling - ActivateMotors(handles.com, pwmIDs, pwmAs, pwmBs, 0); -end -%Stop acquisition -stop(ai); -delete(ai); - -set(handles.et_avgProcTime,'String',num2str(mean(procT(2:end)))); - -%Plot processing time -figure(); -hist(procT(2:end)); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% Function to execute real-time patrec using the offline trained algorithm +% which information is stored in patRec +% +% --------------------------Updates-------------------------- +% 2011-07-27 / Max Ortiz / Creation +% 2011-08-03 / Max Ortiz / Separation of "SignalProcessing_RealtimePatRec" +% 2011-11-17 / Max Ortiz / Added the motors coupling +% 2011-12-07 / Nichlas Sander / Added VRE Coupling and VRE commands. + +function RealtimePatRec(patRec, handles) + +% Get sampling time +sT = str2double(get(handles.et_testingT,'String')); + +%% Is there an option for coupling with the motors? +if isfield(handles,'cb_motorCoupling') %&& ~isfield(handles,'com') + motorCoupling = get(handles.cb_motorCoupling,'Value'); +else + motorCoupling = 0; +end + +if isfield(handles,'cb_VRE') %&& ~isfield(handles,'com') + vreCoupling = get(handles.cb_VRE,'Value'); +else + vreCoupling = 0; +end + +%Is the motor coupling selected? +if motorCoupling + + %Initialize + motorIdx = zeros(1,10); + pwmAs = zeros(1,10); + pwmBs = zeros(1,10); + movSpeeds = zeros(1,10); + + % Get the links to the motors + for i = 1 : size(motorIdx,2) + pmID = ['handles.pm_m' num2str(i)]; + motorIdx(i) = get(eval(pmID),'Value'); + speedID = ['handles.et_speed' num2str(i)]; + movSpeeds(i) = str2double(get(eval(speedID),'String')); + end + + % Init variables for control + pwmIDs = ['A';'A';'B';'B';'C';'C';'D';'D';'E';'E']; + for i = 1 : 2 : size(pwmIDs) + pwmAs(i) = movSpeeds(i); + pwmBs(i) = 0; + pwmAs(i+1) = 0; + pwmBs(i+1) = movSpeeds(i+1); + end; + + % Arrenge according to selection + pwmIDs = pwmIDs(motorIdx); + pwmAs = pwmAs(motorIdx); + pwmBs = pwmBs(motorIdx); + +end + +%% Initialize DAQ card +% Note: A function for DAQ selection will be required when more cards are +% added +if strcmp(patRec.dev,'ADH') + +else % at this poin everything else is the NI - USB6009 + chAI = zeros(1,8); + chAI(patRec.nCh) = 1; + %ai = InitNI(patRec.sF,sT,chAI); + s = InitSBI(patRec.sF,sT,chAI); + lh = s.addlistener('DataAvailable', @plotData); + + % Change the peek time + s.NotifyWhenDataAvailableExceeds = patRec.sF*patRec.tW; +end + +%% Testing +% Note: Probabily this way of testing only works for the NI +% Specific funtions per card might be required. + +start(ai); % Start the daya acquisition +nTW = 1; % Number of time windows + +% Wait until the first samples are aquired +while ai.SamplesAcquired < patRec.sF*patRec.tW +end +% start DAQ +while ai.SamplesAcquired < patRec.sF*sT + + % Processing time start + procTimeS = tic; + + %Data Peek + data = peekdata(ai,patRec.sF*patRec.tW); + + %Signal processing + tSet = SignalProcessing_RealtimePatRec(data, patRec); + + % One shoot PatRec + [outMov outVector] = OneShotPatRecClassifier(patRec, tSet); + % [outMov outVector] = OneShotPatRec(patRec.patRecTrained, tSet); + + % Quick patch for simultaneous rec using neural nets +% if strcmp(patRec.patRecTrained.algorithm,'ANN') +% outMov = find(round(outVector)); +% if isempty(outMov) +% outMov = size(outVector,1); +% end +% end + +% if strcmp(patRec.algorithm,'ANN'); +% +% elseif strcmp(patRec.algorithm,'DA') +% outMov = DiscriminantTest(patRec.coeff, tSet, patRec.training); +% end + + % Compute time + procT(nTW) = toc(procTimeS); + + % Show results + set(handles.lb_movements,'Value',outMov); + drawnow; + + % Send vector to the motors + if motorCoupling + ActivateMotors(handles.com, pwmIDs, pwmAs, pwmBs, outMov); + end + if vreCoupling + dof = round(outMov/2); + dir = mod(outMov,2); + VREActivation(handles.vre_Com, 10, 0.1, dof, dir) + end + % Next cycle + nTW = nTW + 1; + +end +%% Finish session +%Stop motors +if motorCoupling + ActivateMotors(handles.com, pwmIDs, pwmAs, pwmBs, 0); +end +%Stop acquisition +stop(ai); +delete(ai); + +set(handles.et_avgProcTime,'String',num2str(mean(procT(2:end)))); + +%Plot processing time +figure(); +hist(procT(2:end)); diff --git a/PatRec/SOM/Batch Training/BatchNeighborFunction.m b/PatRec/SOM/Batch Training/BatchNeighborFunction.m index db0fe94..a48eafb 100644 --- a/PatRec/SOM/Batch Training/BatchNeighborFunction.m +++ b/PatRec/SOM/Batch Training/BatchNeighborFunction.m @@ -1,54 +1,54 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function for different Neighbor Function Bubble,Gaussian ,Cut Gaussian, -% Epanechikov and Butter worth 2nd order. -% -% Note: Bubble,Gaussian ,Cut Gaussian and Epanechikov implemented according to -% SOM toolbox Team[1] -% -% [1]-Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs -% SOM Toolbox Team ,Helsinki University of Technology. -% http://www.cis.hut.fi/somtoolbox/package/papers/techrep.pdf -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-09-07 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - - -function neigh=BatchNeighborFunction(SOM,sigma) - -neighborFunction=SOM.neighFunc; -% The euclidean distance Matrix between the coordinates of the winning neuron and all -% others -neuronDist=SOM.neuronDist; - - -switch neighborFunction, - case 'bubb' - %neuronDist(neuronDist>sigma)=0; - neigh=(neuronDist<=sigma); - case 'gauss' - neigh = exp(-(neuronDist.^2)./(2*sigma^2)); - case 'cutGauss' - neigh = exp(-(neuronDist.^2)./(2*sigma^2)) .* (neuronDist<=sigma); - case 'trai' - neigh = (1-neuronDist./sigma) .* (neuronDist<=sigma); - case 'butter' - neigh =1./(1+(neuronDist./sigma).^4); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function for different Neighbor Function Bubble,Gaussian ,Cut Gaussian, +% Epanechikov and Butter worth 2nd order. +% +% Note: Bubble,Gaussian ,Cut Gaussian and Epanechikov implemented according to +% SOM toolbox Team[1] +% +% [1]-Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs +% SOM Toolbox Team ,Helsinki University of Technology. +% http://www.cis.hut.fi/somtoolbox/package/papers/techrep.pdf +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-09-07 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + + +function neigh=BatchNeighborFunction(SOM,sigma) + +neighborFunction=SOM.neighFunc; +% The euclidean distance Matrix between the coordinates of the winning neuron and all +% others +neuronDist=SOM.neuronDist; + + +switch neighborFunction, + case 'bubb' + %neuronDist(neuronDist>sigma)=0; + neigh=(neuronDist<=sigma); + case 'gauss' + neigh = exp(-(neuronDist.^2)./(2*sigma^2)); + case 'cutGauss' + neigh = exp(-(neuronDist.^2)./(2*sigma^2)) .* (neuronDist<=sigma); + case 'trai' + neigh = (1-neuronDist./sigma) .* (neuronDist<=sigma); + case 'butter' + neigh =1./(1+(neuronDist./sigma).^4); end \ No newline at end of file diff --git a/PatRec/SOM/Batch Training/BatchTrainig.m b/PatRec/SOM/Batch Training/BatchTrainig.m index ec8396c..5dd4a55 100644 --- a/PatRec/SOM/Batch Training/BatchTrainig.m +++ b/PatRec/SOM/Batch Training/BatchTrainig.m @@ -1,80 +1,80 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to run the batch training.The principle of updating weight vector -% step goes by replacing each map unit by the average of the data vectors that -% were in its neighborhood. The contribution, or activation, of data vectors -% in the mean can be varied with the neighborhood function. This activation -% is given by matrix neigh. So, for each map unit the new weight vector is -% w(t+1) = sum(neigh(t)*S(t)) ./ sum(neigh(t)*A), -% wherw S sum of input vectors corresponding to wining neuron i0 , -% A is the number of sample corresponding to that wining neuron i0 -% and neigh is the neighborhood function.[1] -% -% Note: This function implemented in the same way of SOM toolbox Team[1] -% References -% [1]-Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs -% SOM Toolbox Team ,Helsinki University of Technology. -% http://www.cis.hut.fi/somtoolbox/package/papers/techrep.pdf -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-15 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - - -function SOM=BatchTrainig(SOM,trSet,maxSim,sim) -radiusFin=SOM.radiusFin; -radiusIni=SOM.radiusIni; - -w=SOM.w; -% sample weights: each sample is weighted -weights=1; -% number of sets -nSet=size(trSet,1); -% the width of the neighbour function (linear). -radius = radiusFin + ((maxSim-(sim-1))/maxSim) * (radiusIni - radiusFin); -radius(radius==0) = eps; -knownData = ones(size(trSet,1),size(trSet,2)); -% winning neurons vector -i0 = zeros(1,nSet); -% loop over all data sets to find the winning neurons by measuring the min -% euclidean disttance -for i=1:nSet - inVec=repmat(trSet(i,:),size(w,1),1); - dist=VectorDistance(inVec,w); - [~, i0(i)]=min(dist); -end - -% Different neighborhood's functions -neigh = BatchNeighborFunction(SOM,radius); -% a partition matrix P with elements p_ij=1 if the i0(winning neuron) of data vector j is i. -p = sparse(i0,[1:nSet],weights,SOM.mUnits,nSet); -% neigh*sum of vectors corresponding to wining neuron i0 (in each Voronoi -% set) -s = neigh*(p*trSet); -% neigh*the number of vectors corresponding to wining neuron i0 (in Voronoi -% set) -a = neigh*(p*knownData); -nonZero = find(a > 0); -w(nonZero)=s(nonZero)./ a(nonZero); - - -% Save the w -SOM.w=w; - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to run the batch training.The principle of updating weight vector +% step goes by replacing each map unit by the average of the data vectors that +% were in its neighborhood. The contribution, or activation, of data vectors +% in the mean can be varied with the neighborhood function. This activation +% is given by matrix neigh. So, for each map unit the new weight vector is +% w(t+1) = sum(neigh(t)*S(t)) ./ sum(neigh(t)*A), +% wherw S sum of input vectors corresponding to wining neuron i0 , +% A is the number of sample corresponding to that wining neuron i0 +% and neigh is the neighborhood function.[1] +% +% Note: This function implemented in the same way of SOM toolbox Team[1] +% References +% [1]-Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs +% SOM Toolbox Team ,Helsinki University of Technology. +% http://www.cis.hut.fi/somtoolbox/package/papers/techrep.pdf +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-15 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + + +function SOM=BatchTrainig(SOM,trSet,maxSim,sim) +radiusFin=SOM.radiusFin; +radiusIni=SOM.radiusIni; + +w=SOM.w; +% sample weights: each sample is weighted +weights=1; +% number of sets +nSet=size(trSet,1); +% the width of the neighbour function (linear). +radius = radiusFin + ((maxSim-(sim-1))/maxSim) * (radiusIni - radiusFin); +radius(radius==0) = eps; +knownData = ones(size(trSet,1),size(trSet,2)); +% winning neurons vector +i0 = zeros(1,nSet); +% loop over all data sets to find the winning neurons by measuring the min +% euclidean disttance +for i=1:nSet + inVec=repmat(trSet(i,:),size(w,1),1); + dist=VectorDistance(inVec,w); + [~, i0(i)]=min(dist); +end + +% Different neighborhood's functions +neigh = BatchNeighborFunction(SOM,radius); +% a partition matrix P with elements p_ij=1 if the i0(winning neuron) of data vector j is i. +p = sparse(i0,[1:nSet],weights,SOM.mUnits,nSet); +% neigh*sum of vectors corresponding to wining neuron i0 (in each Voronoi +% set) +s = neigh*(p*trSet); +% neigh*the number of vectors corresponding to wining neuron i0 (in Voronoi +% set) +a = neigh*(p*knownData); +nonZero = find(a > 0); +w(nonZero)=s(nonZero)./ a(nonZero); + + +% Save the w +SOM.w=w; + diff --git a/PatRec/SOM/Batch Training/NeuronCoordinates.m b/PatRec/SOM/Batch Training/NeuronCoordinates.m index ed9ccdc..f59bc2c 100644 --- a/PatRec/SOM/Batch Training/NeuronCoordinates.m +++ b/PatRec/SOM/Batch Training/NeuronCoordinates.m @@ -1,51 +1,51 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to create the map grid, it's used to create hexagonal and rectangular -% grid -% Note: The Hexagonal shape implemented in the same way of SOM toolbox Team[1] -% -% [1]-Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs -% SOM Toolbox Team ,Helsinki University of Technology. -% http://www.cis.hut.fi/somtoolbox/package/papers/techrep.pdf -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-15 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function coords=NeuronCoordinates(mSize,shape) - - -nNurons = prod(mSize); -xUnits=mSize(1); -yUnits=mSize(2); -coords = zeros(nNurons,2); - -if strcmp(shape,'hexa') -[coords(:,1) coords(:,2)]=ind2sub([xUnits yUnits], 1:nNurons); -coords(:,[1 2]) = fliplr(coords(:,[1 2])); -rowIndx = (cumsum(repmat([1],yUnits,1))-1)*xUnits; -for i=2:2:xUnits, - coords(i+rowIndx,1) = coords(i+rowIndx,1) + 0.5; -end - -elseif strcmp(shape,'rect') - [coords(:,1) coords(:,2)]=ind2sub([xUnits yUnits], 1:nNurons); -end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to create the map grid, it's used to create hexagonal and rectangular +% grid +% Note: The Hexagonal shape implemented in the same way of SOM toolbox Team[1] +% +% [1]-Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs +% SOM Toolbox Team ,Helsinki University of Technology. +% http://www.cis.hut.fi/somtoolbox/package/papers/techrep.pdf +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-15 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function coords=NeuronCoordinates(mSize,shape) + + +nNurons = prod(mSize); +xUnits=mSize(1); +yUnits=mSize(2); +coords = zeros(nNurons,2); + +if strcmp(shape,'hexa') +[coords(:,1) coords(:,2)]=ind2sub([xUnits yUnits], 1:nNurons); +coords(:,[1 2]) = fliplr(coords(:,[1 2])); +rowIndx = (cumsum(repmat([1],yUnits,1))-1)*xUnits; +for i=2:2:xUnits, + coords(i+rowIndx,1) = coords(i+rowIndx,1) + 0.5; +end + +elseif strcmp(shape,'rect') + [coords(:,1) coords(:,2)]=ind2sub([xUnits yUnits], 1:nNurons); +end + diff --git a/PatRec/SOM/Batch Training/NeuronDistances.m b/PatRec/SOM/Batch Training/NeuronDistances.m index 674bd3e..c95a194 100644 --- a/PatRec/SOM/Batch Training/NeuronDistances.m +++ b/PatRec/SOM/Batch Training/NeuronDistances.m @@ -1,41 +1,41 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to find the distance between each neuron inside the grid -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-15 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - - -function dis=NeuronDistances(msize,coords) - -nNurons = prod(msize); -dis = zeros(nNurons,nNurons); - -% calculate distance from each location to each other location - -for i=1:(nNurons-1), - indx = [(i+1):nNurons]; - - dis(i,indx)=VectorDistance(coords(indx,:),coords(repmat([1]*i,nNurons-i,1),:))'; - -end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to find the distance between each neuron inside the grid +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-15 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + + +function dis=NeuronDistances(msize,coords) + +nNurons = prod(msize); +dis = zeros(nNurons,nNurons); + +% calculate distance from each location to each other location + +for i=1:(nNurons-1), + indx = [(i+1):nNurons]; + + dis(i,indx)=VectorDistance(coords(indx,:),coords(repmat([1]*i,nNurons-i,1),:))'; + +end + dis = dis + dis'; \ No newline at end of file diff --git a/PatRec/SOM/EvaluateSOM.m b/PatRec/SOM/EvaluateSOM.m index d1e9944..50f566a 100644 --- a/PatRec/SOM/EvaluateSOM.m +++ b/PatRec/SOM/EvaluateSOM.m @@ -1,124 +1,133 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function runs the selected training method then it's avoiding the overfitting -% learning by cross validation technique based on evaluation of RMSE. -% -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function [tempSOM accV]=EvaluateSOM(trSet, trOut, vSet, vOut,tType,algConf) - -graph2d = 1; % - -maxSim=50; %max simulation - passV=0; -sim=0; -SOM=InitSOM(trSet,trOut,algConf); % - -tempSOM=SOM; - - -if graph2d % 2D Graph - hfig = figure; - hold on - set(hfig, 'DoubleBuffer','on'); - axis([1 maxSim 0 .3]); - hbestplot = plot(1:maxSim+1,zeros(1,maxSim+1)); - htext = text(20,0.2,sprintf('Best RMSE: %4.4f',0.0)); - - xlabel('simulations'); - ylabel('rmse'); - hold off - drawnow; -end - - -while sim <= maxSim - % Training - sim = sim + 1; - %Stochastic Training - if strcmp(tType,'Stochastic') - SOM=StochasticTraining(trSet,SOM); - % Batch Trainig - elseif strcmp(tType,'Batch') - SOM=BatchTrainig(SOM,trSet,maxSim,sim); - % error - else - disp('Select Training Method.'); - errordlg('Select Training Method.'); - error('Select Training Method.'); - end - % add lable for each winnig neuron - neuroLabel=GetNeuronLabel(trSet,SOM); - % Save the neuron Label - SOM.neuroLabel=neuroLabel; - % test of the validation set - [apV,rmse(sim)]=FastTestSOM(SOM,vOut,vSet); - SOM.apV = apV; - SOM.fV = rmse(sim); - - % Save the best so far - if SOM.apV >= tempSOM.apV && SOM.fV <= tempSOM.fV - tempSOM = SOM; - end - - - if graph2d % 2D Graph - plotvector = get(hbestplot,'YData'); - plotvector(sim) = rmse(sim); - set(hbestplot,'YData',plotvector); - set(htext,'String',sprintf('Best RMSE: %4.4f',tempSOM.fV)); - - drawnow; - end - -end -if tempSOM.fV < 0.1 || tempSOM.apV > 95 - passV = 1; -end -[apV fV]=FullTestSOM(tempSOM,vOut,vSet); -accV=apV; -tempSOM.apV=apV; -tempSOM.fV=fV; -% Visualize U-matrix -if algConf.visualizeSOM - ShowUDMatrix(tempSOM) -end - -disp(['Simulations: ' num2str(sim)]); -disp(['General rmse: ' num2str(rmse(sim))]); -disp('RMES V: '); -disp(tempSOM.fV'); -disp('Acc V: '); -disp(tempSOM.apV'); - - -if passV - disp('%%%%%%%%%%% SOM training completed %%%%%%%%%%%%%'); - -else - - disp('%%%%%%%%%%% SOM training failed %%%%%%%%%%%%%'); -end - -close(hfig); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function runs the selected training method then it's avoiding the overfitting +% learning by cross validation technique based on evaluation of RMSE. +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 2014-01-14 / Ali Fouad / Addition of "if statment" to reduce the training time +% 20xx-xx-xx / Author / Comment on update + +function [tempSOM accV]=EvaluateSOM(trSet, trOut, vSet, vOut,tType,algConf) + +graph2d = 1; % + +maxSim=50; %max simulation + passV=0; +sim=0; +SOM=InitSOM(trSet,trOut,algConf); % + +tempSOM=SOM; + + +if graph2d % 2D Graph + hfig = figure; + hold on + set(hfig, 'DoubleBuffer','on'); + axis([1 maxSim 0 .3]); + hbestplot = plot(1:maxSim+1,zeros(1,maxSim+1)); + htext = text(20,0.2,sprintf('Best RMSE: %4.4f',0.0)); + + xlabel('simulations'); + ylabel('rmse'); + hold off + drawnow; +end + + +while sim <= maxSim && passV == 0 + % Training + sim = sim + 1; + %Stochastic Training + if strcmp(tType,'Stochastic') + SOM=StochasticTraining(trSet,SOM); + % Batch Trainig + elseif strcmp(tType,'Batch') + SOM=BatchTrainig(SOM,trSet,maxSim,sim); + % error + else + disp('Select Training Method.'); + errordlg('Select Training Method.'); + error('Select Training Method.'); + end + % add lable for each winnig neuron + neuroLabel=GetNeuronLabel(trSet,SOM); + % Save the neuron Label + SOM.neuroLabel=neuroLabel; + % test of the validation set + [apV,rmse(sim)]=FastTestSOM(SOM,vOut,vSet); + SOM.apV = apV; + SOM.fV = rmse(sim); + + % Save the best so far + if SOM.apV >= tempSOM.apV && SOM.fV <= tempSOM.fV + tempSOM = SOM; + end + % terminate the training if there is no further convergence in the RMSE + if SOM.fV < 0.1 + if sim > 10 + if mean(rmse(sim-fix(sim/3):sim)) <= mean(rmse(sim-fix(sim/5):sim)) + + passV = 1; + end + end + end + + if graph2d % 2D Graph + plotvector = get(hbestplot,'YData'); + plotvector(sim) = rmse(sim); + set(hbestplot,'YData',plotvector); + set(htext,'String',sprintf('Best RMSE: %4.4f',tempSOM.fV)); + + drawnow; + end + +end +if tempSOM.fV < 0.1 || tempSOM.apV > 95 + passV = 1; +end +[apV fV]=FullTestSOM(tempSOM,vOut,vSet); +accV=apV; +tempSOM.apV=apV; +tempSOM.fV=fV; +% Visualize U-matrix +if algConf.visualizeSOM + ShowUDMatrix(tempSOM) +end + +disp(['Simulations: ' num2str(sim)]); +disp(['General rmse: ' num2str(rmse(sim))]); +disp('RMES V: '); +disp(tempSOM.fV'); +disp('Acc V: '); +disp(tempSOM.apV'); + + +if passV + disp('%%%%%%%%%%% SOM training completed %%%%%%%%%%%%%'); + +else + + disp('%%%%%%%%%%% SOM training failed %%%%%%%%%%%%%'); +end + +close(hfig); + diff --git a/PatRec/SOM/FastTestSOM.m b/PatRec/SOM/FastTestSOM.m index 297fee0..3ca2f24 100644 --- a/PatRec/SOM/FastTestSOM.m +++ b/PatRec/SOM/FastTestSOM.m @@ -1,95 +1,95 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function classify the validation sets. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function [accP rmse]=FastTestSOM(SOM,vOut,x) - - - -neuroLabel=SOM.neuroLabel; -w=SOM.w; - -% The output vector. -somOut=zeros(size(vOut,1),size(vOut,2)); -% Number of data sets. -nSets=size(x,1); -% square Error. -sqErr=0; -% Number of movements. -nM=size(SOM.trOut,2); -% the Coordinate of all Neurons. -mapCoordinates=SOM.mapCoordinates; - -% loop over all validation data. -for i=1:nSets - % finding the location of winning neuron i0(x) on the map. - [i0 ,~]=FindClosest(w,x(i,:)); - % the Coordinate of the winning neuron i0. - i0Coord=mapCoordinates(i0,:); - - % loop over all coordinates of labeled neurons to find the closest . - for j=1:nM - % all labeled neurons of mov J - neuronLabelMovJ=neuroLabel(:,j); - % the index of labeled neurons movement J in neuron label matrix; - neuronLabelIndx{j}=neuronLabelMovJ(neuronLabelMovJ~=0); - % all Coordinate of all neurons movement j. - neurLabledCoords=mapCoordinates(neuronLabelIndx{j},:); - % finding the closest movement to neuron i0. - [neuronMovJ(1,j) ,d]=FindClosest(neurLabledCoords,i0Coord); - % save the min distans to movement j. - disVec(1,j)=d; - end - % min distance over all mavement neurons - minDis=min(disVec); - % finding the label movement. - [~,mov]=find(disVec==minDis); - % loop to search if the closeste neuron belong to another movement also or not. - for k=1:length(mov) - % the index of the closest neuron in neuron label matrix - neuronIndx(k)=neuronLabelIndx{mov(k)}(neuronMovJ(mov(k))); - % search that index in another movement - [~,winMov]=find(neuroLabel==neuronIndx(k)); - - outMov=unique(winMov); - - somOut(i,outMov)=1; - end - - - - % sum of square Error. - sqErr = sqErr + sum((somOut(i,:)-vOut(i,:)).^2); -end - -% error. -er = find(sum(abs(somOut-vOut),2) >= 1); -% accuracy. -accP = (1 - (length(er)/nSets)) * 100; -% root mean square error. -rmse = sqrt(sqErr/(nSets*nM)); - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function classify the validation sets. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function [accP rmse]=FastTestSOM(SOM,vOut,x) + + + +neuroLabel=SOM.neuroLabel; +w=SOM.w; + +% The output vector. +somOut=zeros(size(vOut,1),size(vOut,2)); +% Number of data sets. +nSets=size(x,1); +% square Error. +sqErr=0; +% Number of movements. +nM=size(SOM.trOut,2); +% the Coordinate of all Neurons. +mapCoordinates=SOM.mapCoordinates; + +% loop over all validation data. +for i=1:nSets + % finding the location of winning neuron i0(x) on the map. + [i0 ,~]=FindClosest(w,x(i,:)); + % the Coordinate of the winning neuron i0. + i0Coord=mapCoordinates(i0,:); + + % loop over all coordinates of labeled neurons to find the closest . + for j=1:nM + % all labeled neurons of mov J + neuronLabelMovJ=neuroLabel(:,j); + % the index of labeled neurons movement J in neuron label matrix; + neuronLabelIndx{j}=neuronLabelMovJ(neuronLabelMovJ~=0); + % all Coordinate of all neurons movement j. + neurLabledCoords=mapCoordinates(neuronLabelIndx{j},:); + % finding the closest movement to neuron i0. + [neuronMovJ(1,j) ,d]=FindClosest(neurLabledCoords,i0Coord); + % save the min distans to movement j. + disVec(1,j)=d; + end + % min distance over all mavement neurons + minDis=min(disVec); + % finding the label movement. + [~,mov]=find(disVec==minDis); + % loop to search if the closeste neuron belong to another movement also or not. + for k=1:length(mov) + % the index of the closest neuron in neuron label matrix + neuronIndx(k)=neuronLabelIndx{mov(k)}(neuronMovJ(mov(k))); + % search that index in another movement + [~,winMov]=find(neuroLabel==neuronIndx(k)); + + outMov=unique(winMov); + + somOut(i,outMov)=1; + end + + + + % sum of square Error. + sqErr = sqErr + sum((somOut(i,:)-vOut(i,:)).^2); +end + +% error. +er = find(sum(abs(somOut-vOut),2) >= 1); +% accuracy. +accP = (1 - (length(er)/nSets)) * 100; +% root mean square error. +rmse = sqrt(sqErr/(nSets*nM)); + + + + diff --git a/PatRec/SOM/FullTestSOM.m b/PatRec/SOM/FullTestSOM.m index 9bc73f4..4b89fd9 100644 --- a/PatRec/SOM/FullTestSOM.m +++ b/PatRec/SOM/FullTestSOM.m @@ -1,105 +1,105 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function classify the validation sets and returns the RMSE and accuracy -% for each mov. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-09-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function [accP rmse]=FullTestSOM(SOM,vOut,x) -neuroLabel=SOM.neuroLabel; -w=SOM.w; -mapCoordinates=SOM.mapCoordinates; -% Number of data sets. -nSets=size(x,1); - -% Number of movements. -nM=size(SOM.trOut,2); -nSetPerMov = nSets/nM ; % Number of sets per movement. -setIdx=1; -% loop over all validation data. -for i=1:nM - - % square Error. - sqErr=0; - cP=0; - - for j = 1 : nSetPerMov - somOut=zeros(1,size(vOut,2)); - % finding the location of winning neuron i0(x) on the map. - [i0 ~]=FindClosest(w,x(setIdx,:)); - % the Coordinate of the winning neuron i0. - i0Coord=mapCoordinates(i0,:); - % loop over all coordinates of labeled neuron to find the closest . - for k=1:nM - % all labeled neurons of mov k. - neuronLabelMovK=neuroLabel(:,k); - % the index of labeled neurons movement k in neuron label matrix; - neuronLabelIndx{k}=neuronLabelMovK(neuronLabelMovK~=0); - % all Coordinate of all neurons movement k. - neurLabledCoords=mapCoordinates(neuronLabelIndx{k},:); - - % finding the closest movement neuron i0. - [labeldNeur(1,k) ,d]=FindClosest(neurLabledCoords,i0Coord); - % save the min distans of movement k. - disVec(1,k)=d; - end - - % min distance over all mavement neurons - minDis=min(disVec); - % finding the label movement . - [~,mov]=find(disVec==minDis); - - % loop to search if the closeste neuron belong to another movement also or not. - for l=1:length(mov) - % the index of the closest neuron in neuron label matrix - neuronIndx(l)=neuronLabelIndx{mov(l)}(labeldNeur(mov(l))); - % search that index in another movement - [~,winMov]=find(neuroLabel==neuronIndx(l)); - outMov=unique(winMov); - somOut(1,outMov)=1; - end - - %correct Movement - - cM= length(find(sum(abs(somOut-vOut(setIdx,:)),2) >= 1)); - if cM==0 - correct=1; - else - correct=0; - end - cP = cP + correct; - % sum of square Error. - sqErr = sqErr + sum((somOut-vOut(setIdx,:)).^2); - setIdx=setIdx+1; - end - accP(i) = cP / nSetPerMov; - rmse(i) = sqrt(sqErr/(nSetPerMov*nM)); - -end -accP(end + 1) = mean(accP); -rmse(end + 1) = mean(rmse); - - - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function classify the validation sets and returns the RMSE and accuracy +% for each mov. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-09-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function [accP rmse]=FullTestSOM(SOM,vOut,x) +neuroLabel=SOM.neuroLabel; +w=SOM.w; +mapCoordinates=SOM.mapCoordinates; +% Number of data sets. +nSets=size(x,1); + +% Number of movements. +nM=size(SOM.trOut,2); +nSetPerMov = nSets/nM ; % Number of sets per movement. +setIdx=1; +% loop over all validation data. +for i=1:nM + + % square Error. + sqErr=0; + cP=0; + + for j = 1 : nSetPerMov + somOut=zeros(1,size(vOut,2)); + % finding the location of winning neuron i0(x) on the map. + [i0 ~]=FindClosest(w,x(setIdx,:)); + % the Coordinate of the winning neuron i0. + i0Coord=mapCoordinates(i0,:); + % loop over all coordinates of labeled neuron to find the closest . + for k=1:nM + % all labeled neurons of mov k. + neuronLabelMovK=neuroLabel(:,k); + % the index of labeled neurons movement k in neuron label matrix; + neuronLabelIndx{k}=neuronLabelMovK(neuronLabelMovK~=0); + % all Coordinate of all neurons movement k. + neurLabledCoords=mapCoordinates(neuronLabelIndx{k},:); + + % finding the closest movement neuron i0. + [labeldNeur(1,k) ,d]=FindClosest(neurLabledCoords,i0Coord); + % save the min distans of movement k. + disVec(1,k)=d; + end + + % min distance over all mavement neurons + minDis=min(disVec); + % finding the label movement . + [~,mov]=find(disVec==minDis); + + % loop to search if the closeste neuron belong to another movement also or not. + for l=1:length(mov) + % the index of the closest neuron in neuron label matrix + neuronIndx(l)=neuronLabelIndx{mov(l)}(labeldNeur(mov(l))); + % search that index in another movement + [~,winMov]=find(neuroLabel==neuronIndx(l)); + outMov=unique(winMov); + somOut(1,outMov)=1; + end + + %correct Movement + + cM= length(find(sum(abs(somOut-vOut(setIdx,:)),2) >= 1)); + if cM==0 + correct=1; + else + correct=0; + end + cP = cP + correct; + % sum of square Error. + sqErr = sqErr + sum((somOut-vOut(setIdx,:)).^2); + setIdx=setIdx+1; + end + accP(i) = cP / nSetPerMov; + rmse(i) = sqrt(sqErr/(nSetPerMov*nM)); + +end +accP(end + 1) = mean(accP); +rmse(end + 1) = mean(rmse); + + + + + + diff --git a/PatRec/SOM/GetNeuronLabel.m b/PatRec/SOM/GetNeuronLabel.m index 14d34b4..18dfbf4 100644 --- a/PatRec/SOM/GetNeuronLabel.m +++ b/PatRec/SOM/GetNeuronLabel.m @@ -1,42 +1,42 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function is set the class label of the all winning neuron and it returns -% culmuns of the wining neuron for each mov. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function neurLab=GetNeuronLabel(trSet,SOM) -trOut=SOM.trOut; -w=SOM.w; -for i=1:size(trSet,1); - - x=trSet(i,:); - % Index of closest neuron - [indx ,~]=FindClosest(w,x); - % finding the class lable of x - [~ ,movLabel] =find(trOut(i,:)==1); - % adding this lable to the winning neuron - i0(i,movLabel)=indx; - -end - -neurLab=i0; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function is set the class label of the all winning neuron and it returns +% culmuns of the wining neuron for each mov. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function neurLab=GetNeuronLabel(trSet,SOM) +trOut=SOM.trOut; +w=SOM.w; +for i=1:size(trSet,1); + + x=trSet(i,:); + % Index of closest neuron + [indx ,~]=FindClosest(w,x); + % finding the class lable of x + [~ ,movLabel] =find(trOut(i,:)==1); + % adding this lable to the winning neuron + i0(i,movLabel)=indx; + +end + +neurLab=i0; diff --git a/PatRec/SOM/InitSOM.m b/PatRec/SOM/InitSOM.m index 289e877..15d9c38 100644 --- a/PatRec/SOM/InitSOM.m +++ b/PatRec/SOM/InitSOM.m @@ -1,119 +1,119 @@ - -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to initialize the Artificial Neural Network (SOM). -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function SOM=InitSOM(trSet,trOut,algConf) - - - -switch algConf.shape - case 'Hexagonal Grid' - shape= 'hexa'; - case 'Rectangular Grid' - shape='rect'; - case 'Select Shape.' - - disp('%%%%%%%%%%Select Shape.%%%%%%%%%%%'); - errordlg('Select Shape.'); - error('Select Shape.'); -end -switch algConf.neighFunc - case 'Bubble' - neigh='bubb'; - case 'Gaussian' - neigh='gauss'; - case 'Cutgaussian' - neigh='cutGauss'; - case 'Triangular' - neigh='trai'; - case 'Butterworth' - neigh='butter'; - case 'Select Neighbor Function.' - disp('%%%%%%%%%%%%%%% Select Neighbor Function.%%%%%%%%%%%%%%%%%'); - errordlg('Select Neighbor Function.'); - error('%%%%%%%%%%%%%%%% Select Neighbor Function.%%%%%%%%%%%%%%%%'); - -end - - - -xUnits=ceil( (size(trSet,1)^0.5)*1.5); % the grid (map) width -yUnits=ceil( (size(trSet,1)^0.5)*1.5); % the grid (map) length -etaIni=.5; %initial learning rate parameter - -sigmaIni=ceil(xUnits/2); % the initial width of the topologic neighborhood function - -tow2=3000; % time constant's decay of the learning parameter - -tow1=tow2/log(sigmaIni); % time constant's decay of the initial width of the topologic neighborhood function - - - -mSize=[xUnits yUnits]; % the grid (map) size - - -mUnits=prod(mSize); % the total number of neuron - -mapCoordinates=NeuronCoordinates(mSize,shape); % the coordinates of the neuron in the grid and that depends on the grid's shape - - -dDim=size(trSet,2); % number of data variable - -w=RandomWeights(xUnits*yUnits,dDim,trSet); % initial weigth for each neuron in the grid where each neuron has the same number of data variable -% Unit Distance -neuronDist = NeuronDistances(mSize,mapCoordinates); - -%% - -% those parameters are for the batch training - -%mask = ones(dDim,1); - -radiusIni =max(1,ceil(max(mSize)/2)); % the initial width of the topologic neighborhood function -radiusFin=.01; % the final width of the topologic neighborhood function - -%% -SOM.t=1; -SOM.sigmaIni=sigmaIni; -SOM.tow1=tow1; -SOM.etaIni=etaIni; -SOM.tow2=tow2; -SOM.w=w; -SOM.mapCoordinates=mapCoordinates; -SOM.apV=0; -SOM.fV=Inf; -SOM.mSize=mSize; -SOM.trOut=trOut; -SOM.mUnits=mUnits; -SOM.radiusIni=radiusIni; -SOM.radiusFin=radiusFin; -SOM.neuronDist=neuronDist; -%SOM.mask=mask; -SOM.shape=shape; -SOM.neighFunc=neigh; - - - - + +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to initialize the Artificial Neural Network (SOM). +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function SOM=InitSOM(trSet,trOut,algConf) + + + +switch algConf.shape + case 'Hexagonal Grid' + shape= 'hexa'; + case 'Rectangular Grid' + shape='rect'; + case 'Select Shape.' + + disp('%%%%%%%%%%Select Shape.%%%%%%%%%%%'); + errordlg('Select Shape.'); + error('Select Shape.'); +end +switch algConf.neighFunc + case 'Bubble' + neigh='bubb'; + case 'Gaussian' + neigh='gauss'; + case 'Cutgaussian' + neigh='cutGauss'; + case 'Triangular' + neigh='trai'; + case 'Butterworth' + neigh='butter'; + case 'Select Neighbor Function.' + disp('%%%%%%%%%%%%%%% Select Neighbor Function.%%%%%%%%%%%%%%%%%'); + errordlg('Select Neighbor Function.'); + error('%%%%%%%%%%%%%%%% Select Neighbor Function.%%%%%%%%%%%%%%%%'); + +end + + + +xUnits=ceil( (size(trSet,1)^0.5)*1.5); % the grid (map) width +yUnits=ceil( (size(trSet,1)^0.5)*1.5); % the grid (map) length +etaIni=.5; %initial learning rate parameter + +sigmaIni=ceil(xUnits/2); % the initial width of the topologic neighborhood function + +tow2=3000; % time constant's decay of the learning parameter + +tow1=tow2/log(sigmaIni); % time constant's decay of the initial width of the topologic neighborhood function + + + +mSize=[xUnits yUnits]; % the grid (map) size + + +mUnits=prod(mSize); % the total number of neuron + +mapCoordinates=NeuronCoordinates(mSize,shape); % the coordinates of the neuron in the grid and that depends on the grid's shape + + +dDim=size(trSet,2); % number of data variable + +w=RandomWeights(xUnits*yUnits,dDim,trSet); % initial weigth for each neuron in the grid where each neuron has the same number of data variable +% Unit Distance +neuronDist = NeuronDistances(mSize,mapCoordinates); + +%% + +% those parameters are for the batch training + +%mask = ones(dDim,1); + +radiusIni =max(1,ceil(max(mSize)/2)); % the initial width of the topologic neighborhood function +radiusFin=.01; % the final width of the topologic neighborhood function + +%% +SOM.t=1; +SOM.sigmaIni=sigmaIni; +SOM.tow1=tow1; +SOM.etaIni=etaIni; +SOM.tow2=tow2; +SOM.w=w; +SOM.mapCoordinates=mapCoordinates; +SOM.apV=0; +SOM.fV=Inf; +SOM.mSize=mSize; +SOM.trOut=trOut; +SOM.mUnits=mUnits; +SOM.radiusIni=radiusIni; +SOM.radiusFin=radiusFin; +SOM.neuronDist=neuronDist; +%SOM.mask=mask; +SOM.shape=shape; +SOM.neighFunc=neigh; + + + + diff --git a/PatRec/SOM/RandomWeights.m b/PatRec/SOM/RandomWeights.m index 4dda51a..38d0502 100644 --- a/PatRec/SOM/RandomWeights.m +++ b/PatRec/SOM/RandomWeights.m @@ -1,32 +1,32 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to initialize the W matrix randomly in the uniform disterbution -% in [max(w) ,min(w)] -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function weights=RandomWeights(nNeurons,nInputs,inputData) -minW=min(min(inputData)); -maxW=max(max(inputData)); - -weights=minW+(maxW-minW).*rand(nNeurons,nInputs); -% weights=rand(nNeurons,nInputs); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to initialize the W matrix randomly in the uniform disterbution +% in [max(w) ,min(w)] +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function weights=RandomWeights(nNeurons,nInputs,inputData) +minW=min(min(inputData)); +maxW=max(max(inputData)); + +weights=minW+(maxW-minW).*rand(nNeurons,nInputs); +% weights=rand(nNeurons,nInputs); diff --git a/PatRec/SOM/SOMTest.m b/PatRec/SOM/SOMTest.m index e541c8d..55e445a 100644 --- a/PatRec/SOM/SOMTest.m +++ b/PatRec/SOM/SOMTest.m @@ -1,96 +1,96 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to classifies the testing sets. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function [outMov disVec]=SOMTest(patRecTrained,x) -% Check if there is any NaN value -tKnownNaN = ~isnan(x); -if ~isempty(find(tKnownNaN==0, 1)) - disp('You have NaN value in Testing sets'); - errordlg('You have NaN value in Testing sets'); - error('You have Complex number in Testing sets'); -end -% Check if there is any Inf value -tKnownInf = ~isinf(x); -if ~isempty(find(tKnownInf==0, 1)) - disp('You have Inf value in Testing sets'); - errordlg('You have Inf value in Testing sets'); - error('You have Complex number in Testing sets'); -end -% Check if there is any Complex number in Testing sets -tKnownComplex = ~isreal(x); -if ~isempty(find(tKnownComplex==1, 1)) - disp('You have Complex number in Testing sets'); - errordlg('You have Complex number in Testing sets'); - error('You have Complex number in Testing sets'); -end - - -w=patRecTrained.SOM.w; -nMov=size(patRecTrained.SOM.trOut,2); -somOut=zeros(1,nMov); - -neuroLabel=patRecTrained.SOM.neuroLabel; - - -% the Coordinate of all Neurons. -mapCoordinates=patRecTrained.SOM.mapCoordinates; - -% finding the location of winning neuron i0(x) on the map. -[i0 ~]=FindClosest(w,x); -% the Coordinate of the winning neuron i0. -i0Coord=mapCoordinates(i0,:); -% loop over all coordinates of labeled neurons to find the closest to i0(x). -for i=1:nMov - % all labeled neurons of mov I - neuronLabelMovI=neuroLabel(:,i); - % the index of labeled neurons movement I in neuron label matrix; - neuronLabelIndx{i}=neuronLabelMovI(neuronLabelMovI~=0); - % all Coordinate of all neurons movement I. - neurLabledCoords=mapCoordinates(neuronLabelIndx{i},:); - - % finding the closest movement to neuron i0. - [neuronMovI(1,i) ,d]=FindClosest(neurLabledCoords,i0Coord); - % save the min distans to movement I. - disVec(1,i)=d; -end -% min distance over all mavement neurons -minDis=min(disVec); -% finding the label movement. -[~,mov]=find(disVec==minDis); -% loop to search if the closeste neuron belong to another movement also or not. -for j=1:length(mov) - % the index of the closest neuron in neuron label matrix - neuronIndx(j)=neuronLabelIndx{mov(j)}(neuronMovI(mov(j))); - % search that index in another movement - [~,winMov]=find(neuroLabel==neuronIndx(j)); - - - somOut(1,unique(winMov))=1; -end -[~,outMov]=find(somOut==1); -% set the outMov has the highest prediction to let that works with different topologies. -disVec(disVec==0)=eps; -disVec=1./disVec; - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to classifies the testing sets. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function [outMov disVec]=SOMTest(patRecTrained,x) +% Check if there is any NaN value +tKnownNaN = ~isnan(x); +if ~isempty(find(tKnownNaN==0, 1)) + disp('You have NaN value in Testing sets'); + errordlg('You have NaN value in Testing sets'); + error('You have Complex number in Testing sets'); +end +% Check if there is any Inf value +tKnownInf = ~isinf(x); +if ~isempty(find(tKnownInf==0, 1)) + disp('You have Inf value in Testing sets'); + errordlg('You have Inf value in Testing sets'); + error('You have Complex number in Testing sets'); +end +% Check if there is any Complex number in Testing sets +tKnownComplex = ~isreal(x); +if ~isempty(find(tKnownComplex==1, 1)) + disp('You have Complex number in Testing sets'); + errordlg('You have Complex number in Testing sets'); + error('You have Complex number in Testing sets'); +end + + +w=patRecTrained.SOM.w; +nMov=size(patRecTrained.SOM.trOut,2); +somOut=zeros(1,nMov); + +neuroLabel=patRecTrained.SOM.neuroLabel; + + +% the Coordinate of all Neurons. +mapCoordinates=patRecTrained.SOM.mapCoordinates; + +% finding the location of winning neuron i0(x) on the map. +[i0 ~]=FindClosest(w,x); +% the Coordinate of the winning neuron i0. +i0Coord=mapCoordinates(i0,:); +% loop over all coordinates of labeled neurons to find the closest to i0(x). +for i=1:nMov + % all labeled neurons of mov I + neuronLabelMovI=neuroLabel(:,i); + % the index of labeled neurons movement I in neuron label matrix; + neuronLabelIndx{i}=neuronLabelMovI(neuronLabelMovI~=0); + % all Coordinate of all neurons movement I. + neurLabledCoords=mapCoordinates(neuronLabelIndx{i},:); + + % finding the closest movement to neuron i0. + [neuronMovI(1,i) ,d]=FindClosest(neurLabledCoords,i0Coord); + % save the min distans to movement I. + disVec(1,i)=d; +end +% min distance over all mavement neurons +minDis=min(disVec); +% finding the label movement. +[~,mov]=find(disVec==minDis); +% loop to search if the closeste neuron belong to another movement also or not. +for j=1:length(mov) + % the index of the closest neuron in neuron label matrix + neuronIndx(j)=neuronLabelIndx{mov(j)}(neuronMovI(mov(j))); + % search that index in another movement + [~,winMov]=find(neuroLabel==neuronIndx(j)); + + + somOut(1,unique(winMov))=1; +end +[~,outMov]=find(somOut==1); +% set the outMov has the highest prediction to let that works with different topologies. +disVec(disVec==0)=eps; +disVec=1./disVec; + diff --git a/PatRec/SOM/SOM_Mapping.m b/PatRec/SOM/SOM_Mapping.m index 3c1b28b..fd39baf 100644 --- a/PatRec/SOM/SOM_Mapping.m +++ b/PatRec/SOM/SOM_Mapping.m @@ -1,77 +1,77 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function check if there is any NaN or Inf value in training and -% validation sets then it run the training method of SOM. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - - -function [out accV]=SOM_Mapping(trSets, trOuts, vSets, vOuts, tType,algConf) -% Check if there is any NaN value in training sets -trKnownNaN = ~isnan(trSets); -if ~isempty(find(trKnownNaN==0, 1)) - disp('You have NaN value in Training sets'); - errordlg('You have NaN value in Training sets'); - error('You have NaN value in Training sets'); - -end -% Check if there is any Inf value in training sets -trKnownInf = ~isinf(trSets); -if ~isempty(find(trKnownInf==0, 1)) - disp('You have Inf value in Training sets'); - errordlg('You have Inf value in Training sets'); - error('You have Inf value in Training sets'); -end -% Check if there is any Complex number in training sets -trKnownComplex = ~isreal(trSets); -if ~isempty(find(trKnownComplex==1, 1)) - disp('You have Complex number in Training sets'); - errordlg('You have Complex number in Training sets'); - error('You have Complex number in Training sets'); -end -% Check if there is any NaN value in validation sets -vKnownNaN = ~isnan(vSets); -if ~isempty(find(vKnownNaN==0, 1)) - disp('You have NaN value in Validation sets'); - errordlg('You have NaN value in Validation sets'); - error('You have NaN value in Validation sets'); -end -% Check if there is any Inf value in validation sets -vKnownInf = ~isinf(vSets); -if ~isempty(find(vKnownInf==0, 1)) - disp('You have Inf value in Validation sets'); - errordlg('You have Inf value in Validation sets'); - error('You have Inf value in Validation sets'); -end -% Check if there is any Complex number in Validation sets -vKnownComplex = ~isreal(vSets); -if ~isempty(find(vKnownComplex==1, 1)) - disp('You have Complex number in Validation sets'); - errordlg('You have Complex number in Validation sets'); - error('You have Complex number in Validation sets'); -end - -[out accV]=EvaluateSOM(trSets, trOuts, vSets, vOuts,tType,algConf); - -end - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function check if there is any NaN or Inf value in training and +% validation sets then it run the training method of SOM. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + + +function [out accV]=SOM_Mapping(trSets, trOuts, vSets, vOuts, tType,algConf) +% Check if there is any NaN value in training sets +trKnownNaN = ~isnan(trSets); +if ~isempty(find(trKnownNaN==0, 1)) + disp('You have NaN value in Training sets'); + errordlg('You have NaN value in Training sets'); + error('You have NaN value in Training sets'); + +end +% Check if there is any Inf value in training sets +trKnownInf = ~isinf(trSets); +if ~isempty(find(trKnownInf==0, 1)) + disp('You have Inf value in Training sets'); + errordlg('You have Inf value in Training sets'); + error('You have Inf value in Training sets'); +end +% Check if there is any Complex number in training sets +trKnownComplex = ~isreal(trSets); +if ~isempty(find(trKnownComplex==1, 1)) + disp('You have Complex number in Training sets'); + errordlg('You have Complex number in Training sets'); + error('You have Complex number in Training sets'); +end +% Check if there is any NaN value in validation sets +vKnownNaN = ~isnan(vSets); +if ~isempty(find(vKnownNaN==0, 1)) + disp('You have NaN value in Validation sets'); + errordlg('You have NaN value in Validation sets'); + error('You have NaN value in Validation sets'); +end +% Check if there is any Inf value in validation sets +vKnownInf = ~isinf(vSets); +if ~isempty(find(vKnownInf==0, 1)) + disp('You have Inf value in Validation sets'); + errordlg('You have Inf value in Validation sets'); + error('You have Inf value in Validation sets'); +end +% Check if there is any Complex number in Validation sets +vKnownComplex = ~isreal(vSets); +if ~isempty(find(vKnownComplex==1, 1)) + disp('You have Complex number in Validation sets'); + errordlg('You have Complex number in Validation sets'); + error('You have Complex number in Validation sets'); +end + +[out accV]=EvaluateSOM(trSets, trOuts, vSets, vOuts,tType,algConf); + +end + + diff --git a/PatRec/SOM/Stochastic Training/Eta.m b/PatRec/SOM/Stochastic Training/Eta.m index 59b5eb0..cf59bca 100644 --- a/PatRec/SOM/Stochastic Training/Eta.m +++ b/PatRec/SOM/Stochastic Training/Eta.m @@ -1,30 +1,30 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to calculate learning rate parameter -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function eta=Eta(t,etaIni,tow2) -eta=etaIni.*exp(-t./tow2); - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to calculate learning rate parameter +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function eta=Eta(t,etaIni,tow2) +eta=etaIni.*exp(-t./tow2); + + + diff --git a/PatRec/SOM/Stochastic Training/FindClosest.m b/PatRec/SOM/Stochastic Training/FindClosest.m index 765587b..aaa60bd 100644 --- a/PatRec/SOM/Stochastic Training/FindClosest.m +++ b/PatRec/SOM/Stochastic Training/FindClosest.m @@ -1,35 +1,35 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to find the closest neuron -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function [closestIdx minDist]=FindClosest(w,inpVec) -nNeuron=size(w,1); -%Repeat the input vector to avoid the iterations -inpVec = repmat(inpVec, nNeuron, 1); -%The euclidean distance between inpVec and all neurons -d=sqrt(sum((inpVec-w).^2, 2)); -%find the minimum distance and the index -[minDist closestIdx]=min(d); - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to find the closest neuron +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function [closestIdx minDist]=FindClosest(w,inpVec) +nNeuron=size(w,1); +%Repeat the input vector to avoid the iterations +inpVec = repmat(inpVec, nNeuron, 1); +%The euclidean distance between inpVec and all neurons +d=sqrt(sum((inpVec-w).^2, 2)); +%find the minimum distance and the index +[minDist closestIdx]=min(d); + + diff --git a/PatRec/SOM/Stochastic Training/Sigma.m b/PatRec/SOM/Stochastic Training/Sigma.m index 5643241..e92a4ca 100644 --- a/PatRec/SOM/Stochastic Training/Sigma.m +++ b/PatRec/SOM/Stochastic Training/Sigma.m @@ -1,28 +1,28 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to calculate the width of topologic neighborhood function -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function sigma=Sigma(t,sigmaIni,tow1) - sigma=sigmaIni.*exp(-t./tow1); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to calculate the width of topologic neighborhood function +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function sigma=Sigma(t,sigmaIni,tow1) + sigma=sigmaIni.*exp(-t./tow1); + diff --git a/PatRec/SOM/Stochastic Training/StochasticNeighborFunction.m b/PatRec/SOM/Stochastic Training/StochasticNeighborFunction.m index f0cba28..563be2e 100644 --- a/PatRec/SOM/Stochastic Training/StochasticNeighborFunction.m +++ b/PatRec/SOM/Stochastic Training/StochasticNeighborFunction.m @@ -1,53 +1,53 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function for different Neighbor Function Bubble,Gaussian ,Cut Gaussian, -% Epanechikov and Butter worth 2nd order. -% -% Note: Bubble,Gaussian ,Cut Gaussian and Epanechikov implemented according to -% SOM toolbox Team[1] -% -% [1]-Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs -% SOM Toolbox Team ,Helsinki University of Technology. -% http://www.cis.hut.fi/somtoolbox/package/papers/techrep.pdf -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-09-07 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function neigh=StochasticNeighborFunction(SOM,sigma,i0) - -neighborFunction=SOM.neighFunc; -% The euclidean distance Matrix between the coordinates of the winning neuron and all -% others -neuronDist=SOM.neuronDist; - - -switch neighborFunction, - case 'bubb' - %neuronDist(neuronDist>sigma)=0; - neigh=(neuronDist(:,i0)<=sigma); - case 'gauss' - neigh = exp(-(neuronDist(:,i0).^2)./(2*sigma^2)); - case 'cutGauss' - neigh = exp(-(neuronDist(:,i0).^2)./(2*sigma^2)) .* (neuronDist(:,i0)<=sigma); - case 'trai' - neigh = (1-neuronDist(:,i0)./sigma) .* (neuronDist(:,i0)<=sigma); - case 'butter' - neigh =1./(1+(neuronDist(:,i0)./sigma).^4); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function for different Neighbor Function Bubble,Gaussian ,Cut Gaussian, +% Epanechikov and Butter worth 2nd order. +% +% Note: Bubble,Gaussian ,Cut Gaussian and Epanechikov implemented according to +% SOM toolbox Team[1] +% +% [1]-Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs +% SOM Toolbox Team ,Helsinki University of Technology. +% http://www.cis.hut.fi/somtoolbox/package/papers/techrep.pdf +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-09-07 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function neigh=StochasticNeighborFunction(SOM,sigma,i0) + +neighborFunction=SOM.neighFunc; +% The euclidean distance Matrix between the coordinates of the winning neuron and all +% others +neuronDist=SOM.neuronDist; + + +switch neighborFunction, + case 'bubb' + %neuronDist(neuronDist>sigma)=0; + neigh=(neuronDist(:,i0)<=sigma); + case 'gauss' + neigh = exp(-(neuronDist(:,i0).^2)./(2*sigma^2)); + case 'cutGauss' + neigh = exp(-(neuronDist(:,i0).^2)./(2*sigma^2)) .* (neuronDist(:,i0)<=sigma); + case 'trai' + neigh = (1-neuronDist(:,i0)./sigma) .* (neuronDist(:,i0)<=sigma); + case 'butter' + neigh =1./(1+(neuronDist(:,i0)./sigma).^4); end \ No newline at end of file diff --git a/PatRec/SOM/Stochastic Training/StochasticTraining.m b/PatRec/SOM/Stochastic Training/StochasticTraining.m index 41cf0e7..3741307 100644 --- a/PatRec/SOM/Stochastic Training/StochasticTraining.m +++ b/PatRec/SOM/Stochastic Training/StochasticTraining.m @@ -1,58 +1,60 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to run the Stochastic Training -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function SOM=StochasticTraining(trSet,SOM) - -t=SOM.t; -% width of neighbour function. -sigmaIni=SOM.sigmaIni; -% constant's decay of sigma -tow1=SOM.tow1; -% learning rate parameter -etaIni=SOM.etaIni; -% constant's decay of eta -tow2=SOM.tow2; -%the weight matrix -w=SOM.w; -% Draw random samples -p=randperm(length(trSet(:,1))); -for i=1:size(trSet,1) - % width of neighbour function at time t. - sigmaT=Sigma(t,sigmaIni,tow1); - sigmaT(sigmaT==0)=eps; - % learning rate parameter at time t. - eta= Eta(t,etaIni,tow2); - %Finding the winning neuron (min euclidean distance) - [i0 ~]=FindClosest(w,trSet(p(i),:)); - %Updating the weights - w=UpdateWeights(i0,trSet(p(i),:),w,eta,sigmaT,SOM); - % update the iteration - t=t+1; - SOM.t=t; -end - -%Save the w -SOM.w=w; - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to run the Stochastic Training +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 2014-01-14 / Ali Fouad / Updating of sampling step +% 20xx-xx-xx / Author / Comment on update + +function SOM=StochasticTraining(trSet,SOM) + +t=SOM.t; +% width of neighbour function. +sigmaIni=SOM.sigmaIni; +% constant's decay of sigma +tow1=SOM.tow1; +% learning rate parameter +etaIni=SOM.etaIni; +% constant's decay of eta +tow2=SOM.tow2; +%the weight matrix +w=SOM.w; +% Draw random samples +nTrPattern=fix(.7*length(trSet(:,1))); +p=randperm(length(trSet(:,1)),nTrPattern); +for i=1:nTrPattern + % width of neighbour function at time t. + sigmaT=Sigma(t,sigmaIni,tow1); + sigmaT(sigmaT==0)=eps; + % learning rate parameter at time t. + eta= Eta(t,etaIni,tow2); + %Finding the winning neuron (min euclidean distance) + [i0 ~]=FindClosest(w,trSet(p(i),:)); + %Updating the weights + w=UpdateWeights(i0,trSet(p(i),:),w,eta,sigmaT,SOM); + % update the iteration + t=t+1; + SOM.t=t; +end + +%Save the w +SOM.w=w; + diff --git a/PatRec/SOM/Stochastic Training/UpdateWeights.m b/PatRec/SOM/Stochastic Training/UpdateWeights.m index 6348793..d0cd0ec 100644 --- a/PatRec/SOM/Stochastic Training/UpdateWeights.m +++ b/PatRec/SOM/Stochastic Training/UpdateWeights.m @@ -1,51 +1,51 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to update the Weight matrix -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function newW=UpdateWeights(i0,x,w,eta,sigma,SOM) - -dim=size(w,2); -% nummber of neurons. -nNeurons=size(w,1); -% Repeat the input vector to avoid the iterations -inVecMat=repmat(x,nNeurons,1); - -% Different neighborhood's functions -neigh=StochasticNeighborFunction(SOM,sigma,i0); - -% Repeat the colum of neighborhood function to avoid the loop -neigh=repmat(neigh,1,dim); -% the learning rate should not be less than 0.02 -if eta<0.02 - eta=0.02; -end -% each ray of neighborhood function * the learning rate -neigh=neigh.*eta; - -deltaW=(inVecMat-w).*neigh; - - -newW=w+deltaW; - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to update the Weight matrix +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function newW=UpdateWeights(i0,x,w,eta,sigma,SOM) + +dim=size(w,2); +% nummber of neurons. +nNeurons=size(w,1); +% Repeat the input vector to avoid the iterations +inVecMat=repmat(x,nNeurons,1); + +% Different neighborhood's functions +neigh=StochasticNeighborFunction(SOM,sigma,i0); + +% Repeat the colum of neighborhood function to avoid the loop +neigh=repmat(neigh,1,dim); +% the learning rate should not be less than 0.02 +if eta<0.02 + eta=0.02; +end +% each ray of neighborhood function * the learning rate +neigh=neigh.*eta; + +deltaW=(inVecMat-w).*neigh; + + +newW=w+deltaW; + + diff --git a/PatRec/SOM/VectorDistance.m b/PatRec/SOM/VectorDistance.m index f028dd1..5cde4be 100644 --- a/PatRec/SOM/VectorDistance.m +++ b/PatRec/SOM/VectorDistance.m @@ -1,27 +1,27 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function to find the euclidean distance -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function dist=VectorDistance(x,y) +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function to find the euclidean distance +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function dist=VectorDistance(x,y) dist=sqrt(sum((x-y).^2, 2)); \ No newline at end of file diff --git a/PatRec/SOM/Visiulaize U-matrix/CreateUDMat.m b/PatRec/SOM/Visiulaize U-matrix/CreateUDMat.m index e7c67d6..1f916d1 100644 --- a/PatRec/SOM/Visiulaize U-matrix/CreateUDMat.m +++ b/PatRec/SOM/Visiulaize U-matrix/CreateUDMat.m @@ -1,92 +1,92 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function returns the distance between each two neighbors neuron -% in the map which is represente the intermediate spot between thos two -% neighbors in U-matrix. -% Note: The implementation of this function done corresponds to SOM Toolbox -% Team: -% Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-08-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function UDMat=CreateUDMat(w,mSize,shape) - - -dim=size(w,2); - -y = mSize(2); -x = mSize(1); -w = reshape(w,[y x dim]); - -UDMat = zeros(2 * x - 1, 2 * y - 1); - - -if strcmp(shape, 'rect'), % rectangular grid - - for i=1:y - for j=1:x, - % horizontal - if j1, - disZ = (w(i,j,:) - w(i+1,j-1,:)).^2; - UDMat(2*i,2*j-2) = sqrt(sum(disZ(:))); - end - end - end - end - -end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function returns the distance between each two neighbors neuron +% in the map which is represente the intermediate spot between thos two +% neighbors in U-matrix. +% Note: The implementation of this function done corresponds to SOM Toolbox +% Team: +% Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-08-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function UDMat=CreateUDMat(w,mSize,shape) + + +dim=size(w,2); + +y = mSize(2); +x = mSize(1); +w = reshape(w,[y x dim]); + +UDMat = zeros(2 * x - 1, 2 * y - 1); + + +if strcmp(shape, 'rect'), % rectangular grid + + for i=1:y + for j=1:x, + % horizontal + if j1, + disZ = (w(i,j,:) - w(i+1,j-1,:)).^2; + UDMat(2*i,2*j-2) = sqrt(sum(disZ(:))); + end + end + end + end + +end + diff --git a/PatRec/SOM/Visiulaize U-matrix/GetColor.m b/PatRec/SOM/Visiulaize U-matrix/GetColor.m index c7e6243..eef0a03 100644 --- a/PatRec/SOM/Visiulaize U-matrix/GetColor.m +++ b/PatRec/SOM/Visiulaize U-matrix/GetColor.m @@ -1,100 +1,100 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to generate different color and it return if there is more than -% mov. contribute in one and more neurons also it will return the spot -% size of the neuron if that more than mov. contribute in one and more neurons. -% -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-08-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function [color indx sizeSpotMat mixColorMat]=GetColor(SOM) - -nMov=size(SOM.neuroLabel,2); - -indx=[]; -% the size of the neuron spot the default size is .5 -sizeNeur=ones(SOM.mUnits,1).*.5; - -% in case of Mix Mov (mixcolor). -mixColor=zeros(SOM.mUnits,size(SOM.neuroLabel,2)); -for i=1:size(SOM.neuroLabel,1) - - neuron=SOM.neuroLabel(i,:); - neuron=neuron(neuron~=0); - neuron=unique(neuron); - % check the neuron i belong to which mov.! - [~, c]=find(SOM.neuroLabel==neuron); - c=unique(c'); - - ind=unique(SOM.neuroLabel(i,c)); - ind=ind(ind~=0); - % set the mov. index of neuron i equal to 1. - mixColor(ind,c)=1; - % set the spot size of neuron i equal to .5 * number of mov. - sizeNeur(ind)=size(c,2)*.5; - -end - - -% generating RGB colors -color(1,:)=[255,0,0]; -color(2,:)=[0,0,255]; -color(3,:)=[0,255,0]; -color(4,:)=[255,0,255]; -color(5,:)=[0,255,255]; -color(6,:)=[255,255,0]; -color(7,:)=[168,102,255]; -color(8,:)=[122,15,226]; -color(9,:)=[192,0,192]; -color(10,:)=[0,128,0]; -color(11,:)=[192,192,0]; -color(12,:)=[203,117,25]; -color(13,:)=[20,43,140]; -color(14,:)=[255,176,99]; -color(15,:)=[43,130,87]; -color(16,:)=[179,255,0]; -color(17,:)=[153,51,0]; -color(18,:)=[148,99,99]; -color(19,:)=[0,169,155]; -color=color./255; -% number of minxing mov -nMixColor=max(sizeNeur)/.5; -mixColorMat=zeros(size(mixColor,1),nMixColor); -sizeSpotMat=zeros(size(mixColor,1),nMixColor); - -for k=1:size(mixColor,1) - [~, col]=find(mixColor(k,:)==1); - if ~isempty(col) - mixColorMat(k,1:size(col,2))=col; - end - nMixColor=sizeNeur(k,1)/.5; - sizeSpotMat(k,1: nMixColor)=linspace(sizeNeur(k,1),.5,nMixColor); -end -for j=1:nMov - - i0=SOM.neuroLabel(:,j); - i0=i0(i0~=0); - % save the indx of the wining neuron - indx=[indx - i0]; - -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to generate different color and it return if there is more than +% mov. contribute in one and more neurons also it will return the spot +% size of the neuron if that more than mov. contribute in one and more neurons. +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-08-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function [color indx sizeSpotMat mixColorMat]=GetColor(SOM) + +nMov=size(SOM.neuroLabel,2); + +indx=[]; +% the size of the neuron spot the default size is .5 +sizeNeur=ones(SOM.mUnits,1).*.5; + +% in case of Mix Mov (mixcolor). +mixColor=zeros(SOM.mUnits,size(SOM.neuroLabel,2)); +for i=1:size(SOM.neuroLabel,1) + + neuron=SOM.neuroLabel(i,:); + neuron=neuron(neuron~=0); + neuron=unique(neuron); + % check the neuron i belong to which mov.! + [~, c]=find(SOM.neuroLabel==neuron); + c=unique(c'); + + ind=unique(SOM.neuroLabel(i,c)); + ind=ind(ind~=0); + % set the mov. index of neuron i equal to 1. + mixColor(ind,c)=1; + % set the spot size of neuron i equal to .5 * number of mov. + sizeNeur(ind)=size(c,2)*.5; + +end + + +% generating RGB colors +color(1,:)=[255,0,0]; +color(2,:)=[0,0,255]; +color(3,:)=[0,255,0]; +color(4,:)=[255,0,255]; +color(5,:)=[0,255,255]; +color(6,:)=[255,255,0]; +color(7,:)=[168,102,255]; +color(8,:)=[122,15,226]; +color(9,:)=[192,0,192]; +color(10,:)=[0,128,0]; +color(11,:)=[192,192,0]; +color(12,:)=[203,117,25]; +color(13,:)=[20,43,140]; +color(14,:)=[255,176,99]; +color(15,:)=[43,130,87]; +color(16,:)=[179,255,0]; +color(17,:)=[153,51,0]; +color(18,:)=[148,99,99]; +color(19,:)=[0,169,155]; +color=color./255; +% number of minxing mov +nMixColor=max(sizeNeur)/.5; +mixColorMat=zeros(size(mixColor,1),nMixColor); +sizeSpotMat=zeros(size(mixColor,1),nMixColor); + +for k=1:size(mixColor,1) + [~, col]=find(mixColor(k,:)==1); + if ~isempty(col) + mixColorMat(k,1:size(col,2))=col; + end + nMixColor=sizeNeur(k,1)/.5; + sizeSpotMat(k,1: nMixColor)=linspace(sizeNeur(k,1),.5,nMixColor); +end +for j=1:nMov + + i0=SOM.neuroLabel(:,j); + i0=i0(i0~=0); + % save the indx of the wining neuron + indx=[indx + i0]; + +end diff --git a/PatRec/SOM/Visiulaize U-matrix/PlotUDMatrix.m b/PatRec/SOM/Visiulaize U-matrix/PlotUDMatrix.m index 49bf815..78abe98 100644 --- a/PatRec/SOM/Visiulaize U-matrix/PlotUDMatrix.m +++ b/PatRec/SOM/Visiulaize U-matrix/PlotUDMatrix.m @@ -1,51 +1,51 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to plot the U-matrix. -% -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-08-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function PlotUDMatrix(SOM,color,mode,coords,UDMatNeuron,xSpotSize) - -%the spot vertices -neurSyntax=SyntaxNeuron(SOM.shape); -if size(xSpotSize,1)>1 - xSpotSize=repmat(xSpotSize',size(neurSyntax,1),1); - ySpotSize=xSpotSize; -else - ySpotSize=xSpotSize; -end -x=repmat(coords(:,1)',size(neurSyntax,1),1); -y=repmat(coords(:,2)',size(neurSyntax,1),1); - -xNeurSyntax=repmat(neurSyntax(:,1),1,UDMatNeuron); -yNeurSyntax=repmat(neurSyntax(:,2),1,UDMatNeuron); - -xNeurSyntax=(x./xSpotSize+xNeurSyntax).*xSpotSize; -yNeurSyntax=(y./ySpotSize+yNeurSyntax).*ySpotSize; - -map=patch(xNeurSyntax,yNeurSyntax,color); -if strcmp(mode,'Umat') - - set(map,'EdgeColor','none'); -end -colorbar('vert') +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to plot the U-matrix. +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-08-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function PlotUDMatrix(SOM,color,mode,coords,UDMatNeuron,xSpotSize) + +%the spot vertices +neurSyntax=SyntaxNeuron(SOM.shape); +if size(xSpotSize,1)>1 + xSpotSize=repmat(xSpotSize',size(neurSyntax,1),1); + ySpotSize=xSpotSize; +else + ySpotSize=xSpotSize; +end +x=repmat(coords(:,1)',size(neurSyntax,1),1); +y=repmat(coords(:,2)',size(neurSyntax,1),1); + +xNeurSyntax=repmat(neurSyntax(:,1),1,UDMatNeuron); +yNeurSyntax=repmat(neurSyntax(:,2),1,UDMatNeuron); + +xNeurSyntax=(x./xSpotSize+xNeurSyntax).*xSpotSize; +yNeurSyntax=(y./ySpotSize+yNeurSyntax).*ySpotSize; + +map=patch(xNeurSyntax,yNeurSyntax,color); +if strcmp(mode,'Umat') + + set(map,'EdgeColor','none'); +end +colorbar('vert') diff --git a/PatRec/SOM/Visiulaize U-matrix/ShowUDMatrix.m b/PatRec/SOM/Visiulaize U-matrix/ShowUDMatrix.m index 11c8464..751669a 100644 --- a/PatRec/SOM/Visiulaize U-matrix/ShowUDMatrix.m +++ b/PatRec/SOM/Visiulaize U-matrix/ShowUDMatrix.m @@ -1,93 +1,93 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to shwo Unified Distance Matrix (U-matrix).The implementation of -% visulising U-matrix is done according to Juha Vesanto, Johan Himberg, -% Esa Alhoniemi and Parhankangs,''SOM Toolbox'',Helsinki University of Technology -% ,ISBN 951-22-4951-0. -% -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-08-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function ShowUDMatrix(SOM) - -figure -colormap(1-gray) -ax=newplot; -set(ax,'Visible','off'); -set(get(ax,'Title'),'Visible','on'); -set(ax,'XaxisLocation','Top'); -% get the Unified Distance Matrix that is the distance between each two neighbors neuron -% in the map which is represente the intermediate spot between thos two neighbors in UDMat. -colorUDMat=CreateUDMat(SOM.w,SOM.mSize,SOM.shape); - -% the size of the Unified Distance Matrix (UDMat). -UDMatSize=[2*SOM.mSize(1)-1 2*SOM.mSize(2)-1]; -UDMatNeur=UDMatSize(1)*UDMatSize(2); -colorUDMat=colorUDMat(:); - -% % the neuron of the original map will have white color (empty not relate to any mov.) -% [indxNeurMap ,~]=find(colorUDMat==0); - -% the coordinats of each spot in UDMat. -coords=UDMatCoords(UDMatSize,SOM.shape,'Umat'); - -% show the spot of the UDMat without edges in different gray level. -PlotUDMatrix(SOM,colorUDMat','Umat',coords,UDMatNeur,.5) - -% get different color for the winning neuron (active neuron); -[colorMat indx sizeNeurSpot mixColor]=GetColor(SOM); - -% the coordinats of the only neurons in the map not in UDMat. -coords=UDMatCoords(SOM.mSize,SOM.shape,'Hit'); - -% % show the neuron spot of the original map with edges with white color. -% plotUDMatrix(SOM,colorUDMat(indxNeurMap)','All',Coords,size(Coords,1),.5) - -% the size of the neuron spot. -sizeNeurSpot=sizeNeurSpot(indx,:); - -% the coordinats of only the winning neuron (active) in the map. -coords=coords(indx,:); - -% the color of only the winning neuron (active) in the map. -mixColor=mixColor(indx,:); - -for i=1:size(mixColor,2) -% the index of the wining neurons of mixColor i -[ind,~]=find(sizeNeurSpot(:,i)~=0); - -% the coordinate of the wining neurons of mixColor i -coordsHit=coords(ind,:); - -% the spot size of the wining neurons of mixColor i -spotSize=sizeNeurSpot(ind,i); - -% number of winning neurons -hitNeurons=size(coordsHit,1); - -% the color of the mixcolor i. -color=colorMat(mixColor(ind,i),:); -color=reshape(color,[1 hitNeurons 3]); - -% show the wining neuron spot of the map with edges with different color mov. -PlotUDMatrix(SOM,color,'Hit',coordsHit,hitNeurons,spotSize) -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to shwo Unified Distance Matrix (U-matrix).The implementation of +% visulising U-matrix is done according to Juha Vesanto, Johan Himberg, +% Esa Alhoniemi and Parhankangs,''SOM Toolbox'',Helsinki University of Technology +% ,ISBN 951-22-4951-0. +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-08-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function ShowUDMatrix(SOM) + +figure +colormap(1-gray) +ax=newplot; +set(ax,'Visible','off'); +set(get(ax,'Title'),'Visible','on'); +set(ax,'XaxisLocation','Top'); +% get the Unified Distance Matrix that is the distance between each two neighbors neuron +% in the map which is represente the intermediate spot between thos two neighbors in UDMat. +colorUDMat=CreateUDMat(SOM.w,SOM.mSize,SOM.shape); + +% the size of the Unified Distance Matrix (UDMat). +UDMatSize=[2*SOM.mSize(1)-1 2*SOM.mSize(2)-1]; +UDMatNeur=UDMatSize(1)*UDMatSize(2); +colorUDMat=colorUDMat(:); + +% % the neuron of the original map will have white color (empty not relate to any mov.) +% [indxNeurMap ,~]=find(colorUDMat==0); + +% the coordinats of each spot in UDMat. +coords=UDMatCoords(UDMatSize,SOM.shape,'Umat'); + +% show the spot of the UDMat without edges in different gray level. +PlotUDMatrix(SOM,colorUDMat','Umat',coords,UDMatNeur,.5) + +% get different color for the winning neuron (active neuron); +[colorMat indx sizeNeurSpot mixColor]=GetColor(SOM); + +% the coordinats of the only neurons in the map not in UDMat. +coords=UDMatCoords(SOM.mSize,SOM.shape,'Hit'); + +% % show the neuron spot of the original map with edges with white color. +% plotUDMatrix(SOM,colorUDMat(indxNeurMap)','All',Coords,size(Coords,1),.5) + +% the size of the neuron spot. +sizeNeurSpot=sizeNeurSpot(indx,:); + +% the coordinats of only the winning neuron (active) in the map. +coords=coords(indx,:); + +% the color of only the winning neuron (active) in the map. +mixColor=mixColor(indx,:); + +for i=1:size(mixColor,2) +% the index of the wining neurons of mixColor i +[ind,~]=find(sizeNeurSpot(:,i)~=0); + +% the coordinate of the wining neurons of mixColor i +coordsHit=coords(ind,:); + +% the spot size of the wining neurons of mixColor i +spotSize=sizeNeurSpot(ind,i); + +% number of winning neurons +hitNeurons=size(coordsHit,1); + +% the color of the mixcolor i. +color=colorMat(mixColor(ind,i),:); +color=reshape(color,[1 hitNeurons 3]); + +% show the wining neuron spot of the map with edges with different color mov. +PlotUDMatrix(SOM,color,'Hit',coordsHit,hitNeurons,spotSize) +end diff --git a/PatRec/SOM/Visiulaize U-matrix/SyntaxNeuron.m b/PatRec/SOM/Visiulaize U-matrix/SyntaxNeuron.m index 53b7740..da9de87 100644 --- a/PatRec/SOM/Visiulaize U-matrix/SyntaxNeuron.m +++ b/PatRec/SOM/Visiulaize U-matrix/SyntaxNeuron.m @@ -1,47 +1,47 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to define the spot vertices. -% -% Note: This function implemented in the same way of SOM toolbox Team[1] -% -% [1]-Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs -% SOM Toolbox Team ,Helsinki University of Technology. -% http://www.cis.hut.fi/somtoolbox/package/papers/techrep.pdf -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-08-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function neuron=SyntaxNeuron(shape) - - -switch shape - case 'rect' - neuron=[[-.5 -.5]; ... - [-.5 .5];... - [.5 .5];... - [.5 -.5]]; - case 'hexa' - neuron=[[0 0.6667];... - [0.5 0.3333];... - [0.5 -0.3333];... - [0 -0.6667];... - [-0.5 -0.3333];... - [-0.5 0.3333]]; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to define the spot vertices. +% +% Note: This function implemented in the same way of SOM toolbox Team[1] +% +% [1]-Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs +% SOM Toolbox Team ,Helsinki University of Technology. +% http://www.cis.hut.fi/somtoolbox/package/papers/techrep.pdf +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-08-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function neuron=SyntaxNeuron(shape) + + +switch shape + case 'rect' + neuron=[[-.5 -.5]; ... + [-.5 .5];... + [.5 .5];... + [.5 -.5]]; + case 'hexa' + neuron=[[0 0.6667];... + [0.5 0.3333];... + [0.5 -0.3333];... + [0 -0.6667];... + [-0.5 -0.3333];... + [-0.5 0.3333]]; end \ No newline at end of file diff --git a/PatRec/SOM/Visiulaize U-matrix/UDMatCoords.m b/PatRec/SOM/Visiulaize U-matrix/UDMatCoords.m index 1b3979f..4a18bfe 100644 --- a/PatRec/SOM/Visiulaize U-matrix/UDMatCoords.m +++ b/PatRec/SOM/Visiulaize U-matrix/UDMatCoords.m @@ -1,56 +1,56 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to create neurons coordinates -% -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-08-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function coords=UDMatCoords(mSize,shape,mode) -nNurons = prod(mSize); -xUnits=mSize(1); -yUnits=mSize(2); - -switch mode - case'Hit'% wining neuron (active neuron). - % rectangular shape - [coords(:,1) coords(:,2)]=ind2sub([xUnits yUnits], 1:nNurons); - % hexa shape - if strcmp(shape,'hexa') - coords(:,[1 2]) = fliplr(coords(:,[1 2])); - location=rem(coords(:,2),2) == 0; - coords(location,1)=coords(location,1)+.5; - end - case'Umat' % Unified Distance Matrix - % rectangular shape - [coords(:,1) coords(:,2)]=ind2sub([xUnits yUnits], 1:nNurons); - % hexa shape - if strcmp(shape,'hexa') - coords(:,[1 2]) = fliplr(coords(:,[1 2])); - location=rem(coords(:,2),2) == 0; - coords(location,1)=coords(location,1)+.5; - location=rem(coords(:,2)+1,4) == 0; - coords(location,1)=coords(location,1)+1; - end - coords=coords/2+.5; - -end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to create neurons coordinates +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-08-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function coords=UDMatCoords(mSize,shape,mode) +nNurons = prod(mSize); +xUnits=mSize(1); +yUnits=mSize(2); + +switch mode + case'Hit'% wining neuron (active neuron). + % rectangular shape + [coords(:,1) coords(:,2)]=ind2sub([xUnits yUnits], 1:nNurons); + % hexa shape + if strcmp(shape,'hexa') + coords(:,[1 2]) = fliplr(coords(:,[1 2])); + location=rem(coords(:,2),2) == 0; + coords(location,1)=coords(location,1)+.5; + end + case'Umat' % Unified Distance Matrix + % rectangular shape + [coords(:,1) coords(:,2)]=ind2sub([xUnits yUnits], 1:nNurons); + % hexa shape + if strcmp(shape,'hexa') + coords(:,[1 2]) = fliplr(coords(:,[1 2])); + location=rem(coords(:,2),2) == 0; + coords(location,1)=coords(location,1)+.5; + location=rem(coords(:,2)+1,4) == 0; + coords(location,1)=coords(location,1)+1; + end + coords=coords/2+.5; + +end + diff --git a/PatRec/SSOM/EvaluateSSOM.m b/PatRec/SSOM/EvaluateSSOM.m index 385c096..ef0ac3a 100644 --- a/PatRec/SSOM/EvaluateSSOM.m +++ b/PatRec/SSOM/EvaluateSSOM.m @@ -1,126 +1,135 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function runs the selected training method then it's avoiding the overfitting -% learning by cross validation technique based on evaluation of RMSE. -% -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-09-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function [tempSSOM accV]=EvaluateSSOM(trSet, trOut, vSet, vOut,tType,algConf) -graph2d = 1; % -%graphUdMat = 1; -maxSim=50; %max simulation -passV = 0; % Pass test of the validation test -sim=0; -SSOM=InitSSOM(trSet,trOut,algConf); % - -tempSSOM=SSOM; - - -if graph2d % 2D Graph - hfig = figure; - hold on - set(hfig, 'DoubleBuffer','on'); - axis([1 maxSim 0 .3]); - hbestplot = plot(1:maxSim+1,zeros(1,maxSim+1)); - htext = text(20,0.2,sprintf('Best RMSE: %4.4f',0.0)); - - xlabel('simulations'); - ylabel('rmse'); - hold off - drawnow; -end - - -while sim <= maxSim - % Training - sim = sim + 1; - %Stochastic Training - if strcmp(tType,'Stochastic') - SSOM=SSOMStochasticTraining(trSet,SSOM,trOut); - % Batch Trainig - elseif strcmp(tType,'Batch') - SSOM=SSOMBatchTrainig(SSOM,trSet,maxSim,sim); - % error - else - disp('Select Training Method.'); - errordlg('Select Training Method.'); - error('Select Training Method.'); - - end - % test of the validation set - [apV,rmse(sim) ]=FastTestSSOM(SSOM,vOut,vSet); - SSOM.apV = apV; - SSOM.fV = rmse(sim); - - - % Save the best so far - if SSOM.apV >= tempSSOM.apV && SSOM.fV <= tempSSOM.fV - tempSSOM = SSOM; - end - - - - if graph2d % 2D Graph - plotvector = get(hbestplot,'YData'); - plotvector(sim) = rmse(sim); - set(hbestplot,'YData',plotvector); - set(htext,'String',sprintf('Best RMSE: %4.4f',tempSSOM.fV)); - - drawnow; - end - -end - -if tempSSOM.fV < 0.1 || tempSSOM.apV > 95 - passV = 1; -end - -[apV fV]=FullTestSSOM(tempSSOM,vOut,vSet); -accV=apV; -tempSSOM.apV=apV; -tempSSOM.fV=fV; - -% Visualize Som -if algConf.visualizeSOM - tempSSOM.neuroLabel=GetNeuronLabel(trSet,tempSSOM); - ShowUDMatrix(tempSSOM) - -end - -disp(['Simulations: ' num2str(sim)]); -disp(['General rmse: ' num2str(rmse(sim))]); -disp('RMES V: '); -disp(tempSSOM.fV'); -disp('Acc V: '); -disp(tempSSOM.apV'); - -if passV - disp('%%%%%%%%%%% Supervised SOM training completed %%%%%%%%%%%%%'); - -else - - disp('%%%%%%%%%%% Supervised SOM training failed %%%%%%%%%%%%%'); -end - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function runs the selected training method then it's avoiding the overfitting +% learning by cross validation technique based on evaluation of RMSE. +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-09-01 / Ali Fouad / Creation +% 2014-01-14 / Ali Fouad / Addition of "if statment" to reduce the training time +% 20xx-xx-xx / Author / Comment on update + +function [tempSSOM accV]=EvaluateSSOM(trSet, trOut, vSet, vOut,tType,algConf) +graph2d = 1; % +%graphUdMat = 1; +maxSim=50; %max simulation +passV = 0; % Pass test of the validation test +sim=0; +SSOM=InitSSOM(trSet,trOut,algConf); % + +tempSSOM=SSOM; + + +if graph2d % 2D Graph + hfig = figure; + hold on + set(hfig, 'DoubleBuffer','on'); + axis([1 maxSim 0 .3]); + hbestplot = plot(1:maxSim+1,zeros(1,maxSim+1)); + htext = text(20,0.2,sprintf('Best RMSE: %4.4f',0.0)); + + xlabel('simulations'); + ylabel('rmse'); + hold off + drawnow; +end + + +while sim <= maxSim && passV == 0 + % Training + sim = sim + 1; + %Stochastic Training + if strcmp(tType,'Stochastic') + SSOM=SSOMStochasticTraining(trSet,SSOM,trOut); + % Batch Trainig + elseif strcmp(tType,'Batch') + SSOM=SSOMBatchTrainig(SSOM,trSet,maxSim,sim); + % error + else + disp('Select Training Method.'); + errordlg('Select Training Method.'); + error('Select Training Method.'); + + end + % test of the validation set + [apV,rmse(sim) ]=FastTestSSOM(SSOM,vOut,vSet); + SSOM.apV = apV; + SSOM.fV = rmse(sim); + + + % Save the best so far + if SSOM.apV >= tempSSOM.apV && SSOM.fV <= tempSSOM.fV + tempSSOM = SSOM; + end + % terminate the training if there is no further convergence in the RMSE + if SSOM.fV < 0.1 + if sim > 10 + if mean(rmse(sim-fix(sim/3):sim)) < mean(rmse(sim-fix(sim/5):sim)) + + passV = 1; + end + end + end + + + if graph2d % 2D Graph + plotvector = get(hbestplot,'YData'); + plotvector(sim) = rmse(sim); + set(hbestplot,'YData',plotvector); + set(htext,'String',sprintf('Best RMSE: %4.4f',tempSSOM.fV)); + + drawnow; + end + +end + +if tempSSOM.fV < 0.1 || tempSSOM.apV > 95 + passV = 1; +end + +[apV fV]=FullTestSSOM(tempSSOM,vOut,vSet); +accV=apV; +tempSSOM.apV=apV; +tempSSOM.fV=fV; + +% Visualize Som +if algConf.visualizeSOM + tempSSOM.neuroLabel=GetNeuronLabel(trSet,tempSSOM); + ShowUDMatrix(tempSSOM) + +end + +disp(['Simulations: ' num2str(sim)]); +disp(['General rmse: ' num2str(rmse(sim))]); +disp('RMES V: '); +disp(tempSSOM.fV'); +disp('Acc V: '); +disp(tempSSOM.apV'); + +if passV + disp('%%%%%%%%%%% Supervised SOM training completed %%%%%%%%%%%%%'); + +else + + disp('%%%%%%%%%%% Supervised SOM training failed %%%%%%%%%%%%%'); +end + + close(hfig); \ No newline at end of file diff --git a/PatRec/SSOM/FastTestSSOM.m b/PatRec/SSOM/FastTestSSOM.m index 87c7dfc..b460d35 100644 --- a/PatRec/SSOM/FastTestSSOM.m +++ b/PatRec/SSOM/FastTestSSOM.m @@ -1,60 +1,60 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function classify the validation sets. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-09-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function [accP rmse]=FastTestSSOM(SSOM,vOut,x) - -% w represent only the first columns i.e(without the output) -w=SSOM.w(:,1:size(SSOM.w,2)-SSOM.sizeSSOMOut); - - -nSets=size(x,1); -% square Error. -sqErr=0; -% Number of movements. -nM=size(SSOM.trOut,2); -SSOMOut=SSOM.SSOMOut; -% loop over all validation data. -for i=1:nSets - - - % finding the winning neuron according to movement i. - [i0 ~]=FindClosest(w,x(i,:)); - - out(i,:)=SSOMOut(i0,:); - - % sum of square Error. - sqErr = sqErr + sum((out(i,:)-vOut(i,:)).^2); -end -% error. -er = find(sum(abs(out-vOut),2) >= 1); -% accuracy. -accP = (1 - (length(er)/nSets)) * 100; -% root mean square error. -rmse = sqrt(sqErr/(nSets*nM)); - - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function classify the validation sets. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-09-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function [accP rmse]=FastTestSSOM(SSOM,vOut,x) + +% w represent only the first columns i.e(without the output) +w=SSOM.w(:,1:size(SSOM.w,2)-SSOM.sizeSSOMOut); + + +nSets=size(x,1); +% square Error. +sqErr=0; +% Number of movements. +nM=size(SSOM.trOut,2); +SSOMOut=SSOM.SSOMOut; +% loop over all validation data. +for i=1:nSets + + + % finding the winning neuron according to movement i. + [i0 ~]=FindClosest(w,x(i,:)); + + out(i,:)=SSOMOut(i0,:); + + % sum of square Error. + sqErr = sqErr + sum((out(i,:)-vOut(i,:)).^2); +end +% error. +er = find(sum(abs(out-vOut),2) >= 1); +% accuracy. +accP = (1 - (length(er)/nSets)) * 100; +% root mean square error. +rmse = sqrt(sqErr/(nSets*nM)); + + + + + diff --git a/PatRec/SSOM/FullTestSSOM.m b/PatRec/SSOM/FullTestSSOM.m index aa7671a..98325f9 100644 --- a/PatRec/SSOM/FullTestSSOM.m +++ b/PatRec/SSOM/FullTestSSOM.m @@ -1,78 +1,78 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function classify the validation sets. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-09-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function [accP rmse]=FullTestSSOM(SSOM,vOut,x) - -% w represent only the first columns i.e(without the output) -w=SSOM.w(:,1:size(SSOM.w,2)-SSOM.sizeSSOMOut); -SSOMOut=SSOM.SSOMOut; - -% Number of data sets. -nSets=size(x,1); - -% Number of movements. -nM=size(SSOM.trOut,2); -nSetPerMov = nSets/nM ; % Number of sets per movement. -setIdx=1; -% loop over all validation data. -for i=1:nM - - % square Error. - sqErr=0; - cP=0; - - for j = 1 : nSetPerMov - - % finding the location of winning neuron i0(x) on the map. - [i0 ,~]=FindClosest(w,x(setIdx,:)); - - % finding the winning movement - prediVec=round(SSOMOut(i0,:)); - - %correct Movement - - cM= length(find(sum(abs(prediVec-vOut(setIdx,:)),2) >= 1)); - if cM==0 - correct=1; - else - correct=0; - end - cP = cP + correct; - % sum of square Error. - sqErr = sqErr + sum((prediVec-vOut(setIdx,:)).^2); - setIdx=setIdx+1; - end - accP(i) = cP / nSetPerMov; - rmse(i) = sqrt(sqErr/(nSetPerMov*nM)); - -end -accP(end + 1) = mean(accP); -rmse(end + 1) = mean(rmse); - - - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function classify the validation sets. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-09-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function [accP rmse]=FullTestSSOM(SSOM,vOut,x) + +% w represent only the first columns i.e(without the output) +w=SSOM.w(:,1:size(SSOM.w,2)-SSOM.sizeSSOMOut); +SSOMOut=SSOM.SSOMOut; + +% Number of data sets. +nSets=size(x,1); + +% Number of movements. +nM=size(SSOM.trOut,2); +nSetPerMov = nSets/nM ; % Number of sets per movement. +setIdx=1; +% loop over all validation data. +for i=1:nM + + % square Error. + sqErr=0; + cP=0; + + for j = 1 : nSetPerMov + + % finding the location of winning neuron i0(x) on the map. + [i0 ,~]=FindClosest(w,x(setIdx,:)); + + % finding the winning movement + prediVec=round(SSOMOut(i0,:)); + + %correct Movement + + cM= length(find(sum(abs(prediVec-vOut(setIdx,:)),2) >= 1)); + if cM==0 + correct=1; + else + correct=0; + end + cP = cP + correct; + % sum of square Error. + sqErr = sqErr + sum((prediVec-vOut(setIdx,:)).^2); + setIdx=setIdx+1; + end + accP(i) = cP / nSetPerMov; + rmse(i) = sqrt(sqErr/(nSetPerMov*nM)); + +end +accP(end + 1) = mean(accP); +rmse(end + 1) = mean(rmse); + + + + + + diff --git a/PatRec/SSOM/InitSSOM.m b/PatRec/SSOM/InitSSOM.m index 5a3400b..6badb4d 100644 --- a/PatRec/SSOM/InitSSOM.m +++ b/PatRec/SSOM/InitSSOM.m @@ -1,120 +1,120 @@ - -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to initialize the Artificial Neural Network (Supervised SOM). -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-09-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function SSOM=InitSSOM(trSet,trOut,algConf) - - -switch algConf.shape - case 'Hexagonal Grid' - shape= 'hexa'; - case 'Rectangular Grid' - shape='rect'; - case 'Select Shape.' - - disp('%%%%%%%%%%Select Shape.%%%%%%%%%%%'); - errordlg('Select Shape.'); - error('Select Shape.'); -end -switch algConf.neighFunc - case 'Bubble' - neigh='bubb'; - case 'Gaussian' - neigh='gauss'; - case 'Cutgaussian' - neigh='cutGauss'; - case 'Triangular' - neigh='trai'; - case 'Butterworth' - neigh='butter'; - case 'Select Neighbor Function.' - disp('%%%%%%%%%%%%%%% Select Neighbor Function.%%%%%%%%%%%%%%%%%'); - errordlg('Select Neighbor Function.'); - error('%%%%%%%%%%%%%%%% Select Neighbor Function.%%%%%%%%%%%%%%%%'); - -end - - - -sizeSSOMOut=size(trOut,2); -% the grid (map) width -xUnits=ceil( (size(trSet,1)^0.5)*1.5); -% the grid (map) length -yUnits=ceil( (size(trSet,1)^0.5)*1.5); -%initial learning rate parameter -etaIni=.5; -% the initial width of the topologic neighborhood function -sigmaIni=ceil(xUnits/2); -% time constant's decay of the learning parameter -tow2=3000; -% time constant's decay of the initial width of the topologic neighborhood function -tow1=tow2/log(sigmaIni); -% the grid (map) size -mSize=[xUnits yUnits]; -% the total number of neuron -mUnits=prod(mSize); -SSOMOut=zeros(mUnits,size(trSet,2)); -% the coordinates of the neuron in the grid and that depends on the grid's shape here i used the hexa shape -mapCoordinates=NeuronCoordinates(mSize,shape); -% number of data variables -dDim=size(trSet,2); -% initial weigth for each neuron in the grid where each neuron has the same number of data variable -w=RandomWeights(xUnits*yUnits,dDim,trSet); -%% - -% those parameters are for the batch training - - -% Unit Distance -neuronDist = NeuronDistances(mSize,mapCoordinates); -% the initial width of the topologic neighborhood function -radiusIni =max(1,ceil(max(mSize)/2)); -% the final width of the topologic neighborhood function -radiusFin=.01; - -%% -SSOM.t=1; -SSOM.sigmaIni=sigmaIni; -SSOM.tow1=tow1; -SSOM.etaIni=etaIni; -SSOM.tow2=tow2; -SSOM.w=w; -SSOM.mapCoordinates=mapCoordinates; -SSOM.apV=0; -SSOM.fV=Inf; -SSOM.mSize=mSize; -SSOM.trOut=trOut; -SSOM.mUnits=mUnits; -SSOM.radiusIni=radiusIni; -SSOM.radiusFin=radiusFin; -SSOM.neuronDist=neuronDist; -SSOM.SSOMOut=SSOMOut; -SSOM.sizeSSOMOut=sizeSSOMOut; - -SSOM.shape=shape; -SSOM.neighFunc=neigh; - - - + +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to initialize the Artificial Neural Network (Supervised SOM). +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-09-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function SSOM=InitSSOM(trSet,trOut,algConf) + + +switch algConf.shape + case 'Hexagonal Grid' + shape= 'hexa'; + case 'Rectangular Grid' + shape='rect'; + case 'Select Shape.' + + disp('%%%%%%%%%%Select Shape.%%%%%%%%%%%'); + errordlg('Select Shape.'); + error('Select Shape.'); +end +switch algConf.neighFunc + case 'Bubble' + neigh='bubb'; + case 'Gaussian' + neigh='gauss'; + case 'Cutgaussian' + neigh='cutGauss'; + case 'Triangular' + neigh='trai'; + case 'Butterworth' + neigh='butter'; + case 'Select Neighbor Function.' + disp('%%%%%%%%%%%%%%% Select Neighbor Function.%%%%%%%%%%%%%%%%%'); + errordlg('Select Neighbor Function.'); + error('%%%%%%%%%%%%%%%% Select Neighbor Function.%%%%%%%%%%%%%%%%'); + +end + + + +sizeSSOMOut=size(trOut,2); +% the grid (map) width +xUnits=ceil( (size(trSet,1)^0.5)*1.5); +% the grid (map) length +yUnits=ceil( (size(trSet,1)^0.5)*1.5); +%initial learning rate parameter +etaIni=.5; +% the initial width of the topologic neighborhood function +sigmaIni=ceil(xUnits/2); +% time constant's decay of the learning parameter +tow2=3000; +% time constant's decay of the initial width of the topologic neighborhood function +tow1=tow2/log(sigmaIni); +% the grid (map) size +mSize=[xUnits yUnits]; +% the total number of neuron +mUnits=prod(mSize); +SSOMOut=zeros(mUnits,size(trSet,2)); +% the coordinates of the neuron in the grid and that depends on the grid's shape here i used the hexa shape +mapCoordinates=NeuronCoordinates(mSize,shape); +% number of data variables +dDim=size(trSet,2); +% initial weigth for each neuron in the grid where each neuron has the same number of data variable +w=RandomWeights(xUnits*yUnits,dDim,trSet); +%% + +% those parameters are for the batch training + + +% Unit Distance +neuronDist = NeuronDistances(mSize,mapCoordinates); +% the initial width of the topologic neighborhood function +radiusIni =max(1,ceil(max(mSize)/2)); +% the final width of the topologic neighborhood function +radiusFin=.01; + +%% +SSOM.t=1; +SSOM.sigmaIni=sigmaIni; +SSOM.tow1=tow1; +SSOM.etaIni=etaIni; +SSOM.tow2=tow2; +SSOM.w=w; +SSOM.mapCoordinates=mapCoordinates; +SSOM.apV=0; +SSOM.fV=Inf; +SSOM.mSize=mSize; +SSOM.trOut=trOut; +SSOM.mUnits=mUnits; +SSOM.radiusIni=radiusIni; +SSOM.radiusFin=radiusFin; +SSOM.neuronDist=neuronDist; +SSOM.SSOMOut=SSOMOut; +SSOM.sizeSSOMOut=sizeSSOMOut; + +SSOM.shape=shape; +SSOM.neighFunc=neigh; + + + diff --git a/PatRec/SSOM/SSOM Batch Training/SSOMBatchTrainig.m b/PatRec/SSOM/SSOM Batch Training/SSOMBatchTrainig.m index 4826691..a998caa 100644 --- a/PatRec/SSOM/SSOM Batch Training/SSOMBatchTrainig.m +++ b/PatRec/SSOM/SSOM Batch Training/SSOMBatchTrainig.m @@ -1,85 +1,85 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to run the batch training.The principle of updating weight vector -% step goes by replacing each map unit by the average of the data vectors that -% were in its neighborhood. The contribution, or activation, of data vectors -% in the mean can be varied with the neighborhood function. This activation -% is given by matrix neigh. So, for each map unit the new weight vector is -% w(t+1) = sum(neigh(t)*S(t)) ./ sum(neigh(t)*A), -% wherw S sum of input vectors corresponding to wining neuron i0 , -% A is the number of sample corresponding to that wining neuron i0 -% and neigh is the neighborhood function.[1] -% -% Note: This function implemented in the same way of SOM toolbox Team[1] -% References -% [1]-Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs -% SOM Toolbox Team ,Helsinki University of Technology. -% http://www.cis.hut.fi/somtoolbox/package/papers/techrep.pdf -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-09-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - - -function SSOM=SSOMBatchTrainig(SSOM,trSet,maxSim,sim) -radiusFin=SSOM.radiusFin; -radiusIni=SSOM.radiusIni; -w=SSOM.w; -sizeSSOMOut=SSOM.sizeSSOMOut; -% sample weights: each sample is weighted -weights=1; -% number of sets -nSet=size(trSet,1); -% the width of the neighbour function. -radius = radiusFin + ((maxSim-(sim-1))/maxSim) * (radiusIni - radiusFin); -radius(radius==0) = eps; - - -knownData = ones(size(trSet,1),size(trSet,2)); -% winning neurons vector -i0 = zeros(1,nSet); -% loop over all data sets to find the winning neurons by measuring the min -% euclidean disttance -for i=1:nSet - inVec=repmat(trSet(i,:),size(w,1),1); - dist=VectorDistance(inVec,w); - [~, i0(i)]=min(dist); -end - - -% neighborhood function - -neigh = BatchNeighborFunction(SSOM,radius); -% a partition matrix p with elements p_ij=1 if the i0(winning neuron) of data vector j is i. -p = sparse(i0,[1:nSet],weights,SSOM.mUnits,nSet); -% neigh*sum of vectors corresponding to wining neuron i0 (in each Voronoi -% set) -s = neigh*(p*trSet); -% neigh*the number of vectors corresponding to wining neuron i0 (in Voronoi -% set) -a = neigh*(p*knownData); - -nonZero = find(a > 0); -w(nonZero)=s(nonZero)./ a(nonZero); -% the classifier outpu represent by last columns of the w matrix which we -% added previously in SSOM_Mapping. -SSOMOut=w(:,end-sizeSSOMOut+1:end); -SSOM.w=w; -SSOM.SSOMOut=SSOMOut; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to run the batch training.The principle of updating weight vector +% step goes by replacing each map unit by the average of the data vectors that +% were in its neighborhood. The contribution, or activation, of data vectors +% in the mean can be varied with the neighborhood function. This activation +% is given by matrix neigh. So, for each map unit the new weight vector is +% w(t+1) = sum(neigh(t)*S(t)) ./ sum(neigh(t)*A), +% wherw S sum of input vectors corresponding to wining neuron i0 , +% A is the number of sample corresponding to that wining neuron i0 +% and neigh is the neighborhood function.[1] +% +% Note: This function implemented in the same way of SOM toolbox Team[1] +% References +% [1]-Juha Vesanto, Johan Himberg, Esa Alhoniemi, and Parhankangs +% SOM Toolbox Team ,Helsinki University of Technology. +% http://www.cis.hut.fi/somtoolbox/package/papers/techrep.pdf +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-09-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + + +function SSOM=SSOMBatchTrainig(SSOM,trSet,maxSim,sim) +radiusFin=SSOM.radiusFin; +radiusIni=SSOM.radiusIni; +w=SSOM.w; +sizeSSOMOut=SSOM.sizeSSOMOut; +% sample weights: each sample is weighted +weights=1; +% number of sets +nSet=size(trSet,1); +% the width of the neighbour function. +radius = radiusFin + ((maxSim-(sim-1))/maxSim) * (radiusIni - radiusFin); +radius(radius==0) = eps; + + +knownData = ones(size(trSet,1),size(trSet,2)); +% winning neurons vector +i0 = zeros(1,nSet); +% loop over all data sets to find the winning neurons by measuring the min +% euclidean disttance +for i=1:nSet + inVec=repmat(trSet(i,:),size(w,1),1); + dist=VectorDistance(inVec,w); + [~, i0(i)]=min(dist); +end + + +% neighborhood function + +neigh = BatchNeighborFunction(SSOM,radius); +% a partition matrix p with elements p_ij=1 if the i0(winning neuron) of data vector j is i. +p = sparse(i0,[1:nSet],weights,SSOM.mUnits,nSet); +% neigh*sum of vectors corresponding to wining neuron i0 (in each Voronoi +% set) +s = neigh*(p*trSet); +% neigh*the number of vectors corresponding to wining neuron i0 (in Voronoi +% set) +a = neigh*(p*knownData); + +nonZero = find(a > 0); +w(nonZero)=s(nonZero)./ a(nonZero); +% the classifier outpu represent by last columns of the w matrix which we +% added previously in SSOM_Mapping. +SSOMOut=w(:,end-sizeSSOMOut+1:end); +SSOM.w=w; +SSOM.SSOMOut=SSOMOut; diff --git a/PatRec/SSOM/SSOM Stochastic Training/SSOMStochasticTraining.m b/PatRec/SSOM/SSOM Stochastic Training/SSOMStochasticTraining.m index bdda69d..060913d 100644 --- a/PatRec/SSOM/SSOM Stochastic Training/SSOMStochasticTraining.m +++ b/PatRec/SSOM/SSOM Stochastic Training/SSOMStochasticTraining.m @@ -1,60 +1,62 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to run the Stochastic Training -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-09-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function SSOM=SSOMStochasticTraining(trSet,SSOM,trOut) -t=SSOM.t; -% width of neighbour function. -sigmaIni=SSOM.sigmaIni; -% constant's decay of sigma -tow1=SSOM.tow1; -% learning rate parameter -etaIni=SSOM.etaIni; -% constant's decay of eta -tow2=SSOM.tow2; -%the weight matrix -w=SSOM.w; -sizeSSOMOut=SSOM.sizeSSOMOut; - -% Draw random samples -p=randperm(length(trOut(:,1))); -for i=1:size(trSet,1) - % width of neighbour function. - sigmaT=Sigma(t,sigmaIni,tow1); - sigmaT(sigmaT==0)=eps; - % learning rate parameter - eta= Eta(t,etaIni,tow2); - %Finding the winning neuron (min euclidean distance weights matrix and input vector ) - [i0 mindist]=FindClosest(w,trSet(p(i),:)); - %Updating the weights - w=UpdateWeights(i0,trSet(p(i),:),w,eta,sigmaT,SSOM); - % increase the iteration - t=t+1; - SSOM.t=t; - -end - -SSOMOut=w(:,end-sizeSSOMOut+1:end); -SSOM.w=w; -SSOM.SSOMOut=SSOMOut; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to run the Stochastic Training +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-09-01 / Ali Fouad / Creation +% 2014-01-14 / Ali Fouad / Updating of sampling step +% 20xx-xx-xx / Author / Comment on update + +function SSOM=SSOMStochasticTraining(trSet,SSOM,trOut) +t=SSOM.t; +% width of neighbour function. +sigmaIni=SSOM.sigmaIni; +% constant's decay of sigma +tow1=SSOM.tow1; +% learning rate parameter +etaIni=SSOM.etaIni; +% constant's decay of eta +tow2=SSOM.tow2; +%the weight matrix +w=SSOM.w; +sizeSSOMOut=SSOM.sizeSSOMOut; + +% Draw random samples +nTrPattern=fix(.7*length(trSet(:,1))); +p=randperm(length(trSet(:,1)),nTrPattern); +for i=1:nTrPattern + % width of neighbour function. + sigmaT=Sigma(t,sigmaIni,tow1); + sigmaT(sigmaT==0)=eps; + % learning rate parameter + eta= Eta(t,etaIni,tow2); + %Finding the winning neuron (min euclidean distance weights matrix and input vector ) + [i0 mindist]=FindClosest(w,trSet(p(i),:)); + %Updating the weights + w=UpdateWeights(i0,trSet(p(i),:),w,eta,sigmaT,SSOM); + % increase the iteration + t=t+1; + SSOM.t=t; + +end + +SSOMOut=w(:,end-sizeSSOMOut+1:end); +SSOM.w=w; +SSOM.SSOMOut=SSOMOut; diff --git a/PatRec/SSOM/SSOMTest.m b/PatRec/SSOM/SSOMTest.m index f71eb2d..2fe3883 100644 --- a/PatRec/SSOM/SSOMTest.m +++ b/PatRec/SSOM/SSOMTest.m @@ -1,64 +1,64 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to classifies the testing sets. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-09-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - -function [outMov predictVec]=SSOMTest(patRecTrained,x) - - -% Check if there is any NaN value -tKnownNaN = ~isnan(x); -if ~isempty(find(tKnownNaN==0, 1)) - disp('You have NaN value in Testing sets'); - errordlg('You have NaN value in Testing sets'); - error('You have Complex number in Testing sets'); -end -% Check if there is any Inf value -tKnownInf = ~isinf(x); -if ~isempty(find(tKnownInf==0, 1)) - disp('You have Inf value in Testing sets'); - errordlg('You have Inf value in Testing sets'); - error('You have Complex number in Testing sets'); -end -% Check if there is any Complex number in Testing sets -tKnownComplex = ~isreal(x); -if ~isempty(find(tKnownComplex==1, 1)) - disp('You have Complex number in Testing sets'); - errordlg('You have Complex number in Testing sets'); - error('You have Complex number in Testing sets'); -end - - -% w represent only the first columns i.e(without the output) -w=patRecTrained.SSOM.w(:,1:size(patRecTrained.SSOM.w,2)-patRecTrained.SSOM.sizeSSOMOut); -SSOMOut=patRecTrained.SSOM.SSOMOut; - -% finding the winning neuron. -[i0,~ ]=FindClosest(w,x); - - -predictVec=SSOMOut(i0,:); -% finding the winning movement -outMov=find(round(predictVec)); -end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to classifies the testing sets. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-09-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + +function [outMov predictVec]=SSOMTest(patRecTrained,x) + + +% Check if there is any NaN value +tKnownNaN = ~isnan(x); +if ~isempty(find(tKnownNaN==0, 1)) + disp('You have NaN value in Testing sets'); + errordlg('You have NaN value in Testing sets'); + error('You have Complex number in Testing sets'); +end +% Check if there is any Inf value +tKnownInf = ~isinf(x); +if ~isempty(find(tKnownInf==0, 1)) + disp('You have Inf value in Testing sets'); + errordlg('You have Inf value in Testing sets'); + error('You have Complex number in Testing sets'); +end +% Check if there is any Complex number in Testing sets +tKnownComplex = ~isreal(x); +if ~isempty(find(tKnownComplex==1, 1)) + disp('You have Complex number in Testing sets'); + errordlg('You have Complex number in Testing sets'); + error('You have Complex number in Testing sets'); +end + + +% w represent only the first columns i.e(without the output) +w=patRecTrained.SSOM.w(:,1:size(patRecTrained.SSOM.w,2)-patRecTrained.SSOM.sizeSSOMOut); +SSOMOut=patRecTrained.SSOM.SSOMOut; + +% finding the winning neuron. +[i0,~ ]=FindClosest(w,x); + + +predictVec=SSOMOut(i0,:); +% finding the winning movement +outMov=find(round(predictVec)); +end + diff --git a/PatRec/SSOM/SSOM_Mapping.m b/PatRec/SSOM/SSOM_Mapping.m index c98676e..60da344 100644 --- a/PatRec/SSOM/SSOM_Mapping.m +++ b/PatRec/SSOM/SSOM_Mapping.m @@ -1,82 +1,82 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function check if there is any NaN or Inf value in training and -% validation sets then it run the training method of SOM. -% -% -% -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-09-01 / Ali Fouad / Creation -% 20xx-xx-xx / Author / Comment on update - - -function [out accV]=SSOM_Mapping(trSets, trOuts, vSets, vOuts, tType,algConf) -% Check if there is any NaN value in training sets -trKnownNaN = ~isnan(trSets); -if ~isempty(find(trKnownNaN==0, 1)) - disp('You have NaN value in Training sets'); - errordlg('You have NaN value in Training sets'); - error('You have NaN value in Training sets'); - -end -% Check if there is any Inf value in training sets -trKnownInf = ~isinf(trSets); -if ~isempty(find(trKnownInf==0, 1)) - disp('You have Inf value in Training sets'); - errordlg('You have Inf value in Training sets'); - error('You have Inf value in Training sets'); -end -% Check if there is any Complex number in training sets -trKnownComplex = ~isreal(trSets); -if ~isempty(find(trKnownComplex==1, 1)) - disp('You have Complex number in Training sets'); - errordlg('You have Complex number in Training sets'); - error('You have Complex number in Training sets'); -end -% Check if there is any NaN value in validation sets -vKnownNaN = ~isnan(vSets); -if ~isempty(find(vKnownNaN==0, 1)) - disp('You have NaN value in Validation sets'); - errordlg('You have NaN value in Validation sets'); - error('You have NaN value in Validation sets'); -end -% Check if there is any Inf value in validation sets -vKnownInf = ~isinf(vSets); -if ~isempty(find(vKnownInf==0, 1)) - disp('You have Inf value in Validation sets'); - errordlg('You have Inf value in Validation sets'); - error('You have Inf value in Validation sets'); -end -% Check if there is any Complex number in Validation sets -vKnownComplex = ~isreal(vSets); -if ~isempty(find(vKnownComplex==1, 1)) - disp('You have Complex number in Validation sets'); - errordlg('You have Complex number in Validation sets'); - error('You have Complex number in Validation sets'); -end -% adding the lalel of training sets last columns to make it Supervised. -trSets=[trSets trOuts]; - -[out accV]=EvaluateSSOM(trSets, trOuts, vSets, vOuts,tType,algConf); - -end - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function check if there is any NaN or Inf value in training and +% validation sets then it run the training method of SOM. +% +% +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-09-01 / Ali Fouad / Creation +% 20xx-xx-xx / Author / Comment on update + + +function [out accV]=SSOM_Mapping(trSets, trOuts, vSets, vOuts, tType,algConf) +% Check if there is any NaN value in training sets +trKnownNaN = ~isnan(trSets); +if ~isempty(find(trKnownNaN==0, 1)) + disp('You have NaN value in Training sets'); + errordlg('You have NaN value in Training sets'); + error('You have NaN value in Training sets'); + +end +% Check if there is any Inf value in training sets +trKnownInf = ~isinf(trSets); +if ~isempty(find(trKnownInf==0, 1)) + disp('You have Inf value in Training sets'); + errordlg('You have Inf value in Training sets'); + error('You have Inf value in Training sets'); +end +% Check if there is any Complex number in training sets +trKnownComplex = ~isreal(trSets); +if ~isempty(find(trKnownComplex==1, 1)) + disp('You have Complex number in Training sets'); + errordlg('You have Complex number in Training sets'); + error('You have Complex number in Training sets'); +end +% Check if there is any NaN value in validation sets +vKnownNaN = ~isnan(vSets); +if ~isempty(find(vKnownNaN==0, 1)) + disp('You have NaN value in Validation sets'); + errordlg('You have NaN value in Validation sets'); + error('You have NaN value in Validation sets'); +end +% Check if there is any Inf value in validation sets +vKnownInf = ~isinf(vSets); +if ~isempty(find(vKnownInf==0, 1)) + disp('You have Inf value in Validation sets'); + errordlg('You have Inf value in Validation sets'); + error('You have Inf value in Validation sets'); +end +% Check if there is any Complex number in Validation sets +vKnownComplex = ~isreal(vSets); +if ~isempty(find(vKnownComplex==1, 1)) + disp('You have Complex number in Validation sets'); + errordlg('You have Complex number in Validation sets'); + error('You have Complex number in Validation sets'); +end +% adding the lalel of training sets last columns to make it Supervised. +trSets=[trSets trOuts]; + +[out accV]=EvaluateSSOM(trSets, trOuts, vSets, vOuts,tType,algConf); + +end + + diff --git a/PatRec/SignalProcessing_RealtimePatRec.m b/PatRec/SignalProcessing_RealtimePatRec.m index 3b1c017..9f9275c 100644 --- a/PatRec/SignalProcessing_RealtimePatRec.m +++ b/PatRec/SignalProcessing_RealtimePatRec.m @@ -1,46 +1,55 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -%------------------------- Function Description ------------------------- -% Function to execute the general steps of signal processing required in -% real-time PatRec. -% -% ------------------------------- Updates --------------------------------- -% 2011-08-02 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function tSet = SignalProcessing_RealtimePatRec(data, patRec) - - % Apply fliters - data = ApplyFilters(patRec, data); - - % Get signal features - tFeatures = GetSigFeatures(data,patRec.sF,patRec.selFeatures); - - % Create a vector with the signal features - tSet = []; - for i = 1 : size(patRec.selFeatures,1) - tSet = [tSet tFeatures.(patRec.selFeatures{i})]; - end - - % Normalize if required - tSet = NormalizeSet(tSet,patRec); - %if patRec.norm - % tSet = ((tSet - patRec.nMean) ./ patRec.nVar); - %end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +%------------------------- Function Description ------------------------- +% Function to execute the general steps of signal processing required in +% real-time PatRec. +% +% ------------------------------- Updates --------------------------------- +% 2011-08-02 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function tSet = SignalProcessing_RealtimePatRec(data, patRec) + + % Apply fliters + data = ApplyFilters(patRec, data); + + % Apply Signal Separation algorithms if required + if isfield(patRec,'sigSeparation') + data=SignalSeparationRealtime(patRec,data); + end + + % Get signal features + tFeatures = GetSigFeatures(data,patRec.sF,patRec.selFeatures); + + % Create a vector with the signal features + tSet = []; + for i = 1 : size(patRec.selFeatures,1) + tSet = [tSet tFeatures.(patRec.selFeatures{i})]; + end + + % Normalize if required + tSet = NormalizeSet(tSet,patRec); + + % Apply Feature Reduction (PCA) if required + tSet = ApplyFeatureReduction(tSet, patRec); + + %if patRec.norm + % tSet = ((tSet - patRec.nMean) ./ patRec.nVar); + %end + end \ No newline at end of file diff --git a/PatRec/SoftMax.m b/PatRec/SoftMax.m index 6e1cd77..c50a749 100644 --- a/PatRec/SoftMax.m +++ b/PatRec/SoftMax.m @@ -1,47 +1,47 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------- Function Description ------------- -% Apply SoftMax activation function -% -% ------------- Updates ------------- -% 2011-10-09 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update -function vector = SoftMax(vector) - -%% Manual - %vector = vector - min(vector); % Offset negative numbers - vector(vector < 0) = 0; % Remove negative numbers - vector = vector ./ sum(vector); - - - -%% Automatic -% vector = softmax(vector); - - -%% Validations - - % The softmax function can returns NaN - if find(isnan(vector)) - vector(isnan(vector)) = 1; - end - - % Replace 0 for small values - vector(vector == 0) = 0.0000000001; - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------- Function Description ------------- +% Apply SoftMax activation function +% +% ------------- Updates ------------- +% 2011-10-09 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update +function vector = SoftMax(vector) + +%% Manual + %vector = vector - min(vector); % Offset negative numbers + vector(vector < 0) = 0; % Remove negative numbers + vector = vector ./ sum(vector); + + + +%% Automatic +% vector = softmax(vector); + + +%% Validations + + % The softmax function can returns NaN + if find(isnan(vector)) + vector(isnan(vector)) = 1; + end + + % Replace 0 for small values + vector(vector == 0) = 0.0000000001; + + diff --git a/PatRec/TacTestResults.m b/PatRec/TacTestResults.m index 8ea15a3..06cf494 100644 --- a/PatRec/TacTestResults.m +++ b/PatRec/TacTestResults.m @@ -1,155 +1,156 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to print the results of the TAC test -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-07-17 / Nichlas Sander / Created the file from MotionTest and -% changed accordingly - -function tacTest = TacTestResults(tacTest) - -nSucct = zeros(tacTest.trials,tacTest.combinations); - -% General matrix -for t = 1 : tacTest.trials - for r = 1 : tacTest.nR - for m = 1 : tacTest.combinations - selectionTime(r+(tacTest.nR*(t-1)),m) = tacTest.trialResult(t,r,m).selectionTime; - compTime(r+(tacTest.nR*(t-1)),m) = tacTest.trialResult(t,r,m).completionTime; - movements{t,m} = tacTest.trialResult(t,r,m).name; -% selTimeTW(r+(tacTest.nR*(t-1)),m) = tacTest.trialResult(t,r,m).selTimeTW; -% compTimeTW(r+(tacTest.nR*(t-1)),m) = tacTest.trialResult(t,r,m).compTimeTW; - pathEfficiency(r+(tacTest.nR*(t-1)),m) = tacTest.trialResult(t,r,m).pathEfficiency; - % Check the numer of succesful trails - if ~tacTest.trialResult(t,r,m).fail - nSucct(t,m) = nSucct(t,m) + 1; % Number of succesful trials - end - end - end -end - -%Save the first repetition of selectionTime and compTime. -selectionTime1 = zeros(size(selectionTime)); -selectionTime1 = selectionTime(1:tacTest.nR,1:tacTest.combinations); -comptime1 = zeros(size(compTime)); -compTime1 = compTime(1:tacTest.nR,1:tacTest.combinations); - - -%Add all movements in first trial to corresponding in later ones. -if tacTest.trials > 1 - %Set the success-rate to the first row. - for i = 1:tacTest.combinations - name = movements{1,i}; - for j = 2:tacTest.trials - for k = 1:tacTest.combinations - if strcmp(name,movements{j,k}) - nSucct(1,i) = nSucct(1,i) + nSucct(j,k); - end - end - end - end - nSucct(2:end,:) = []; - - for i = 1:tacTest.combinations - name = movements{1,i}; - for j = 2:tacTest.trials - for k = 1:tacTest.combinations - if strcmp(name,movements{j,k}) - selectionTime1(((j-1)*tacTest.nR)+1 : ((j-1)*tacTest.nR)+tacTest.nR , i) = selectionTime(((j-1)*tacTest.nR)+1 : ((j-1)*tacTest.nR)+tacTest.nR , k); - compTime1(((j-1)*tacTest.nR)+1 : ((j-1)*tacTest.nR)+tacTest.nR , i) = compTime(((j-1)*tacTest.nR)+1 : ((j-1)*tacTest.nR)+tacTest.nR , k); - end - end - end - end - %Remove the names of all other rows than the first row. - movements(2:end,:) = []; -end -compRate = nSucct ./ (tacTest.nR*tacTest.trials); -selectionTime = selectionTime1; -compTime = compTime1; - -%% Add a mean -compRate(end+1) = mean(compRate); -selectionTime(:,end+1) = nanmean(selectionTime,2); -compTime(:,end+1) = nanmean(compTime,2); -pathEfficiency(:,end+1) = nanmean(pathEfficiency,2); - -%% Save results - -tacTest.compRate = compRate; -tacTest.selectionTime = selectionTime; -tacTest.compTime = compTime; -% tacTest.selTimeTW = selTimeTW; -% tacTest.compTimeTW = compTimeTW; -tacTest.pathEfficiency = pathEfficiency; - -%% plot and save results -% Completion rate -figure(); -plot(compRate,'r-*'); -title('Completion rate'); -xlabel('Movements'); -set(gca,'XTick',1:length(movements)+1,'XtickLabel',[movements 'Mean']); -ylabel('% of completion'); - -% Completion Time -figure(); -hold on; -if(size(compTime,1) > 1) - plot(nanmean(compTime),'r*'); - boxplot(compTime,'plotstyle','compact'); -else - plot(compTime,'o'); -end -hold off; -title('Completion Time'); -xlabel('Movements'); -set(gca,'XTick',1:length(movements)+1,'XtickLabel',[movements 'Mean']); -ylabel('Seconds'); - -% Selection Time -figure(); -hold on; -if(size(selectionTime,1) > 1) - plot(nanmean(selectionTime),'r*'); - boxplot(selectionTime,'plotstyle','compact'); -else - plot(selectionTime,'o'); -end -hold off; -title('Selection Time'); -xlabel('Movements'); -set(gca,'XTick',1:length(movements)+1,'XtickLabel',[movements 'Mean']); -ylabel('Seconds'); - -% % Accuracy -figure(); -hold on; -if(size(pathEfficiency,1) > 1) - plot(nanmean(pathEfficiency),'r*'); - boxplot(pathEfficiency,'plotstyle','compact'); -else - plot(pathEfficiency,'o'); -end -hold off; -title('Path efficiency'); -xlabel('Movements'); -set(gca,'XTick',1:length(movements)+1,'XtickLabel',[movements 'Mean']); -ylabel('Accuracy'); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to print the results of the TAC test +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-07-17 / Nichlas Sander / Created the file from MotionTest and +% changed accordingly +% 2013-04-27 / Max Ortiz / Addition of movements to tacTest for further +% identification + +function tacTest = TacTestResults(tacTest) + +nSucct = zeros(tacTest.trials,tacTest.combinations); + +% General matrix +for t = 1 : tacTest.trials + for r = 1 : tacTest.nR + for m = 1 : tacTest.combinations + selectionTime(r+(tacTest.nR*(t-1)),m) = tacTest.trialResult(t,r,m).selectionTime; + compTime(r+(tacTest.nR*(t-1)),m) = tacTest.trialResult(t,r,m).completionTime; + movements{t,m} = tacTest.trialResult(t,r,m).name; +% selTimeTW(r+(tacTest.nR*(t-1)),m) = tacTest.trialResult(t,r,m).selTimeTW; +% compTimeTW(r+(tacTest.nR*(t-1)),m) = tacTest.trialResult(t,r,m).compTimeTW; + pathEfficiency(r+(tacTest.nR*(t-1)),m) = tacTest.trialResult(t,r,m).pathEfficiency; + % Check the numer of succesful trails + if ~tacTest.trialResult(t,r,m).fail + nSucct(t,m) = nSucct(t,m) + 1; % Number of succesful trials + end + end + end +end + +%Save the first repetition of selectionTime and compTime. +selectionTime1 = zeros(size(selectionTime)); +selectionTime1 = selectionTime(1:tacTest.nR,1:tacTest.combinations); +comptime1 = zeros(size(compTime)); +compTime1 = compTime(1:tacTest.nR,1:tacTest.combinations); + + +%Add all movements in first trial to corresponding in later ones. +if tacTest.trials > 1 + %Set the success-rate to the first row. + for i = 1:tacTest.combinations + name = movements{1,i}; + for j = 2:tacTest.trials + for k = 1:tacTest.combinations + if strcmp(name,movements{j,k}) + nSucct(1,i) = nSucct(1,i) + nSucct(j,k); + end + end + end + end + nSucct(2:end,:) = []; + + for i = 1:tacTest.combinations + name = movements{1,i}; + for j = 2:tacTest.trials + for k = 1:tacTest.combinations + if strcmp(name,movements{j,k}) + selectionTime1(((j-1)*tacTest.nR)+1 : ((j-1)*tacTest.nR)+tacTest.nR , i) = selectionTime(((j-1)*tacTest.nR)+1 : ((j-1)*tacTest.nR)+tacTest.nR , k); + compTime1(((j-1)*tacTest.nR)+1 : ((j-1)*tacTest.nR)+tacTest.nR , i) = compTime(((j-1)*tacTest.nR)+1 : ((j-1)*tacTest.nR)+tacTest.nR , k); + end + end + end + end + %Remove the names of all other rows than the first row. + movements(2:end,:) = []; +end +compRate = nSucct ./ (tacTest.nR*tacTest.trials); +selectionTime = selectionTime1; +compTime = compTime1; + +%% Add a mean +compRate(end+1) = mean(compRate); +selectionTime(:,end+1) = nanmean(selectionTime,2); +compTime(:,end+1) = nanmean(compTime,2); +pathEfficiency(:,end+1) = nanmean(pathEfficiency,2); + +%% Save results + +tacTest.compRate = compRate; +tacTest.selectionTime = selectionTime; +tacTest.compTime = compTime; +tacTest.pathEfficiency = pathEfficiency; +tacTest.movements = movements; + +%% plot and save results +% Completion rate +figure(); +plot(compRate,'r-*'); +title('Completion rate'); +xlabel('Movements'); +set(gca,'XTick',1:length(movements)+1,'XtickLabel',[movements 'Mean']); +ylabel('% of completion'); + +% Completion Time +figure(); +hold on; +if(size(compTime,1) > 1) + plot(nanmean(compTime),'r*'); + boxplot(compTime,'plotstyle','compact'); +else + plot(compTime,'o'); +end +hold off; +title('Completion Time'); +xlabel('Movements'); +set(gca,'XTick',1:length(movements)+1,'XtickLabel',[movements 'Mean']); +ylabel('Seconds'); + +% Selection Time +figure(); +hold on; +if(size(selectionTime,1) > 1) + plot(nanmean(selectionTime),'r*'); + boxplot(selectionTime,'plotstyle','compact'); +else + plot(selectionTime,'o'); +end +hold off; +title('Selection Time'); +xlabel('Movements'); +set(gca,'XTick',1:length(movements)+1,'XtickLabel',[movements 'Mean']); +ylabel('Seconds'); + +% % Accuracy +figure(); +hold on; +if(size(pathEfficiency,1) > 1) + plot(nanmean(pathEfficiency),'r*'); + boxplot(pathEfficiency,'plotstyle','compact'); +else + plot(pathEfficiency,'o'); +end +hold off; +title('Path efficiency'); +xlabel('Movements'); +set(gca,'XTick',1:length(movements)+1,'XtickLabel',[movements 'Mean']); +ylabel('Accuracy'); + diff --git a/PatRec/Topologies/PatRec_AgoAntagonist.m b/PatRec/Topologies/PatRec_AgoAntagonist.m index 595273f..d603552 100644 --- a/PatRec/Topologies/PatRec_AgoAntagonist.m +++ b/PatRec/Topologies/PatRec_AgoAntagonist.m @@ -1,89 +1,89 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to compute the classification output of the Ago-antagonist -% scheme. -% -% outMov : Index of the selected patterns -% outVector : Vector the raw classification output -% -% ------------------------- Updates & Contributors ------------------------ -% 2012-03-06 / Max Ortiz / Creation from PatRec_AgoAntagonistAndMixed to do -% not considered the Mixed - -function [outMov outVector] = PatRec_AgoAntagonist(patRec, x) - - if strcmp(patRec.mov(end),'Rest') - restFlag = 1; - tempRest = []; - else - restFlag = 0; - end - outVector = []; - outMov = []; - movIdx = 1; - - for j = 1 : size(patRec.patRecTrained,2) - [outMovTemp outVectorTemp] = OneShotPatRec(patRec.patRecTrained(j),x); - % Create outVector - if restFlag - tempRest = [tempRest ; outVectorTemp(end)]; - end - outVector = [outVector ; outVectorTemp(1:2)]; - - - % How many patterns were predicted? - nPredMov = length(outMovTemp); - if nPredMov > 0 - if restFlag - if outVectorTemp(1) > outVectorTemp(2) - outMov(end+1) = movIdx; - elseif outVectorTemp(2) > outVectorTemp(3) - outMov(end+1) = movIdx+1; - end - - % The following line doesn't worn since it does't consider - % that some classifier can predict several outputs. - %if outMovTemp ~= 3 - % outMov(end+1) = outMovTemp + (2*(j-1)); - %end - else - if outVectorTemp(1) > outVectorTemp(2) - outMov(end+1) = movIdx; - else - outMov(end+1) = movIdx+1; - end - % The following line doesn't worn since it does't consider - % that some classifier can predict several outputs. - %outMov(end+1) = outMovTemp + (2*(j-1)); - end - - end - movIdx = movIdx + 2; - end - - % Add the average of rest to the outVector - if restFlag - outVector(end+1) = mean(tempRest); - % Select rest if nothing else is selected - if isempty(outMov) - outMov = size(outVector,1); - end - end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to compute the classification output of the Ago-antagonist +% scheme. +% +% outMov : Index of the selected patterns +% outVector : Vector the raw classification output +% +% ------------------------- Updates & Contributors ------------------------ +% 2012-03-06 / Max Ortiz / Creation from PatRec_AgoAntagonistAndMixed to do +% not considered the Mixed + +function [outMov outVector] = PatRec_AgoAntagonist(patRec, x) + + if strcmp(patRec.mov(end),'Rest') + restFlag = 1; + tempRest = []; + else + restFlag = 0; + end + outVector = []; + outMov = []; + movIdx = 1; + + for j = 1 : size(patRec.patRecTrained,2) + [outMovTemp outVectorTemp] = OneShotPatRec(patRec.patRecTrained(j),x); + % Create outVector + if restFlag + tempRest = [tempRest ; outVectorTemp(end)]; + end + outVector = [outVector ; outVectorTemp(1:2)]; + + + % How many patterns were predicted? + nPredMov = length(outMovTemp); + if nPredMov > 0 + if restFlag + if outVectorTemp(1) > outVectorTemp(2) + outMov(end+1) = movIdx; + elseif outVectorTemp(2) > outVectorTemp(3) + outMov(end+1) = movIdx+1; + end + + % The following line doesn't worn since it does't consider + % that some classifier can predict several outputs. + %if outMovTemp ~= 3 + % outMov(end+1) = outMovTemp + (2*(j-1)); + %end + else + if outVectorTemp(1) > outVectorTemp(2) + outMov(end+1) = movIdx; + else + outMov(end+1) = movIdx+1; + end + % The following line doesn't worn since it does't consider + % that some classifier can predict several outputs. + %outMov(end+1) = outMovTemp + (2*(j-1)); + end + + end + movIdx = movIdx + 2; + end + + % Add the average of rest to the outVector + if restFlag + outVector(end+1) = mean(tempRest); + % Select rest if nothing else is selected + if isempty(outMov) + outMov = size(outVector,1); + end + end + end \ No newline at end of file diff --git a/PatRec/Topologies/PatRec_AgoAntagonistAndMixed.m b/PatRec/Topologies/PatRec_AgoAntagonistAndMixed.m index fd134e0..6c1ed94 100644 --- a/PatRec/Topologies/PatRec_AgoAntagonistAndMixed.m +++ b/PatRec/Topologies/PatRec_AgoAntagonistAndMixed.m @@ -1,80 +1,107 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to compute the classification output of the Ago-antagonist -% scheme. -% -% outMov : Index of the selected patterns -% outVector : Vector the raw classification output -% -% ------------------------- Updates & Contributors ------------------------ -% 2011-12-04 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update -function [outMov outVector] = PatRec_AgoAntagonistAndMixed(patRec, x) - - % Variables init - if strcmp(patRec.mov(end),'Rest') - restFlag = 1; - tempRest = []; - else - restFlag = 0; - end - outVector = []; - outMov = []; - movIdx = 1; - - % Go through all DoF - for j = 1 : size(patRec.patRecTrained,2) - % Run PatRec - [outMovTemp outMatrix(:,j)] = OneShotPatRec(patRec.patRecTrained(j),x); - - % Find the winner looking at the outVector - % If rest or the mixed class are the winners, outMov is not - % modified - [mMax mIdx] = max(outMatrix(:,j)); - nPredMov = length(outMovTemp); - if nPredMov > 0 & outMovTemp ~= 0 - if mIdx == 1 - outMov(end+1) = movIdx; - elseif mIdx == 2; - outMov(end+1) = movIdx +1; - end - end - movIdx = movIdx + 2; - - % Create outVector - outVector = [outVector ; outMatrix(1:2,j)]; - if restFlag - tempRest = [tempRest ; outMatrix(end,j)]; - end - - - end - % If no movement was predicted and rest exist, then rest it is. - % Add the average of rest to the outVector - if restFlag - outVector(end+1) = mean(tempRest); - % Select rest if nothing else is selected - if isempty(outMov) - outMov = size(outVector,1); - end - end - - % To comply with standard outMov format - outMov = outMov'; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to compute the classification output of the Ago-antagonist +% scheme. +% +% outMov : Index of the selected patterns +% outVector : Vector the raw classification output +% +% ------------------------- Updates & Contributors ------------------------ +% 2011-12-04 / Max Ortiz / Creation +% 2012-01-26 / Max Ortiz / Fixed bug on predictions outMovTemp = 0 +% 2013-03-03 / Max Ortiz / Revision of the whole prediction chain and +% simplification of the outMov computation +% 20xx-xx-xx / Author / Comment on update + +function [outMov outVector] = PatRec_AgoAntagonistAndMixed(patRec, x) + + % Variables init + if strcmp(patRec.mov(end),'Rest') + restFlag = 1; + tempRest = []; + else + restFlag = 0; + end + outVector = []; + outMov = []; + movIdx = 1; + + % Go through all DoF + for j = 1 : size(patRec.patRecTrained,2) + % Run PatRec + [outMovTemp outMatrix(:,j)] = OneShotPatRec(patRec.patRecTrained(j),x); + + % Create the outMov vector using the winners from each classifier + if length(outMovTemp) == 1 + if outMovTemp == 1 + outMov(end+1) = movIdx; + elseif outMovTemp == 2; + outMov(end+1) = movIdx +1; + end + + % If there is more than 1 movement predicted, take the highest + elseif length(outMovTemp) >= 1 + [mMax mIdx] = max(outMatrix(:,j)); + if mIdx == 1 + outMov(end+1) = movIdx; + elseif mIdx == 2; + outMov(end+1) = movIdx +1; + end + end + +% Code prior 2013-03-03 +% % Find the winner looking at the outVector +% % This is done to handle cases where outMovTemp has more than 1 value +% % If rest or the mixed class are the winners, outMov is not +% % modified +% [mMax mIdx] = max(outMatrix(:,j)); +% nPredMov = length(outMovTemp); +% if nPredMov > 0 & outMovTemp ~= 0 +% if mIdx == 1 +% outMov(end+1) = movIdx; +% elseif mIdx == 2; +% outMov(end+1) = movIdx +1; +% end +% end + + % Increase the Index number for the movements + movIdx = movIdx + 2; + + % Create outVector + outVector = [outVector ; outMatrix(1:2,j)]; + if restFlag + tempRest = [tempRest ; outMatrix(end,j)]; + end + + + end + % If no movement was predicted and rest exist, then rest it is. + % Add the average of rest to the outVector (this is not the best + % computation of the strenght of the rest prediction) + if restFlag + outVector(end+1) = mean(tempRest); + % Select rest if nothing else is selected + if isempty(outMov) + outMov = size(outVector,1); + end + end + + % To comply with standard outMov format + outMov = outMov'; end \ No newline at end of file diff --git a/PatRec/Topologies/PatRec_AllAndOne.m b/PatRec/Topologies/PatRec_AllAndOne.m index 0106c59..5fc0116 100644 --- a/PatRec/Topologies/PatRec_AllAndOne.m +++ b/PatRec/Topologies/PatRec_AllAndOne.m @@ -1,60 +1,60 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to compute the classification output of the All-and-One scheme -% which is a combination between One-Vs-All and One-Vs-One where ... -% -% outMov : Index of the selected patterns -% outVector : Vector the raw classification output -% -% ---------------------------------- Updates ------------------------------ -% 2011-12-06 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update -function [outMov outVector] = PatRec_AllAndOne(patRec, x) - - outVector = []; - - % One-Vs-All Computation - for j = 1 : size(patRec.patRecTrained,2) - [outMovTemp outMatrix(:,j)] = OneShotPatRec(patRec.patRecTrained(j),x); - % Create the out Vector by gathering the probability of each - % movement to happen - outVector = [outVector ; outMatrix(1,j)]; - end - - % Sort the best patterns - [Y, I] = sort(outVector,'descend'); - bestMov = I(1:2)'; - - % Order the winners for later comparasion OVO - if bestMov(1) > bestMov(2) - m(1) = bestMov(2); - m(2) = bestMov(1); - else - m = bestMov; - end - - % One-Vs-One Computation - [outMovTemp outVectorTemp] = OneShotPatRec(patRec.patRecTrainedAux(m(1),m(2)),x); - if outMovTemp == 0 - outMov = bestMov(1); - else - outMov = m(outMovTemp); - end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to compute the classification output of the All-and-One scheme +% which is a combination between One-Vs-All and One-Vs-One where ... +% +% outMov : Index of the selected patterns +% outVector : Vector the raw classification output +% +% ---------------------------------- Updates ------------------------------ +% 2011-12-06 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update +function [outMov outVector] = PatRec_AllAndOne(patRec, x) + + outVector = []; + + % One-Vs-All Computation + for j = 1 : size(patRec.patRecTrained,2) + [outMovTemp outMatrix(:,j)] = OneShotPatRec(patRec.patRecTrained(j),x); + % Create the out Vector by gathering the probability of each + % movement to happen + outVector = [outVector ; outMatrix(1,j)]; + end + + % Sort the best patterns + [Y, I] = sort(outVector,'descend'); + bestMov = I(1:2)'; + + % Order the winners for later comparasion OVO + if bestMov(1) > bestMov(2) + m(1) = bestMov(2); + m(2) = bestMov(1); + else + m = bestMov; + end + + % One-Vs-One Computation + [outMovTemp outVectorTemp] = OneShotPatRec(patRec.patRecTrainedAux(m(1),m(2)),x); + if outMovTemp == 0 + outMov = bestMov(1); + else + outMov = m(outMovTemp); + end + end \ No newline at end of file diff --git a/PatRec/Topologies/PatRec_OneVsAll.m b/PatRec/Topologies/PatRec_OneVsAll.m index b71259b..467a9b1 100644 --- a/PatRec/Topologies/PatRec_OneVsAll.m +++ b/PatRec/Topologies/PatRec_OneVsAll.m @@ -1,49 +1,49 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to compute the classification output of the binary classifier -% One-Versus-All -% -% outMov : Index of the selected patterns -% outVector : Vector the raw classification output -% -% ------------------------- Updates & Contributors ------------------------ -% 2011-12-04 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update -function [outMov outVector] = PatRec_OneVsAll(patRec, x) - - outVector = []; - outMov = []; - for j = 1 : size(patRec.patRecTrained,2) - [outMovTemp outMatrix] = OneShotPatRec(patRec.patRecTrained(j),x); - - % Is one or all the winner? - nPredMov = length(outMovTemp); - if nPredMov > 0 - if outMatrix(1) > outMatrix(2) - outMov(end+1) = j; - end - end - % Create the out Vector by gathering the probability of each - % movement to happen - outVector = [outVector ; outMatrix(1)]; - end - - % To comply with standard outMov format - outMov = outMov'; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to compute the classification output of the binary classifier +% One-Versus-All +% +% outMov : Index of the selected patterns +% outVector : Vector the raw classification output +% +% ------------------------- Updates & Contributors ------------------------ +% 2011-12-04 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update +function [outMov outVector] = PatRec_OneVsAll(patRec, x) + + outVector = []; + outMov = []; + for j = 1 : size(patRec.patRecTrained,2) + [outMovTemp outMatrix] = OneShotPatRec(patRec.patRecTrained(j),x); + + % Is one or all the winner? + nPredMov = length(outMovTemp); + if nPredMov > 0 + if outMatrix(1) > outMatrix(2) + outMov(end+1) = j; + end + end + % Create the out Vector by gathering the probability of each + % movement to happen + outVector = [outVector ; outMatrix(1)]; + end + + % To comply with standard outMov format + outMov = outMov'; end \ No newline at end of file diff --git a/PatRec/Topologies/PatRec_OneVsAllT.m b/PatRec/Topologies/PatRec_OneVsAllT.m new file mode 100644 index 0000000..f585802 --- /dev/null +++ b/PatRec/Topologies/PatRec_OneVsAllT.m @@ -0,0 +1,48 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to compute the classification output of the Ago-antagonist +% scheme. +% +% outMov : Index of the selected patterns +% outVector : Vector the raw classification output +% +% ------------------------- Updates & Contributors ------------------------ +% 2011-12-04 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update +function [outMov outVector] = PatRec_OneVsAllT(patRec, x) + + outVector = []; + outMov = []; + outThrs = 0.5; + + for j = 1 : size(patRec.patRecTrained,2) + [outMovTemp outMatrix(:,j)] = OneShotPatRec(patRec.patRecTrained(j),x); + % Create the out Vector by gathering the probability of each + % movement to happen + outVector = [outVector ; outMatrix(1,j)]; + end + + % Use of a threshold + for i = 1 : size(outVector,1) + if outVector(i) >= outThrs + outMov(end+1) = i; + end + end + +end \ No newline at end of file diff --git a/PatRec/Topologies/PatRec_OneVsOne.m b/PatRec/Topologies/PatRec_OneVsOne.m index a50a30c..d017600 100644 --- a/PatRec/Topologies/PatRec_OneVsOne.m +++ b/PatRec/Topologies/PatRec_OneVsOne.m @@ -1,73 +1,73 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to compute the classification output of One Vs One topology -% -% outMov : Index of the selected patterns -% outVector : Vector the raw classification output -% -% ------------------------- Updates & Contributors ------------------------ -% 2011-12-04 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update -function [outMov outVector] = PatRec_OneVsOne(patRec, x) - - % Initialize matrices - outMovMatrix = zeros(size(patRec.patRecTrained,2),size(patRec.patRecTrained,2)); - outMatrix = outMovMatrix; - nOut = size(patRec.patRecTrained,1); - - % Compute the output of each classification - for j = 1 : size(patRec.patRecTrained,1) - for k = j+1 : size(patRec.patRecTrained,2) - [outMovTemp outVectorTemp] = OneShotPatRec(patRec.patRecTrained(j,k),x); - - % How many patterns were predicted? - nPredMov = length(outMovTemp); - if nPredMov > 0 - % Count the victories of each pattern - if outVectorTemp(1) > outVectorTemp(2) - outMovMatrix(j,k) = 1; - outMovMatrix(k,j) = 0; - else - outMovMatrix(j,k) = 0; - outMovMatrix(k,j) = 1; - end - % Non-of them was predicted - else - outMovMatrix(j,k) = 0; - outMovMatrix(k,j) = 0; - end - outMatrix(j,k) = outVectorTemp(1); - outMatrix(k,j) = outVectorTemp(2); - end - end - - % Ouput selection by probability -% outVector = sum(outMovMatrix,2)./ (size(outMovMatrix,1) - 1); -% outMov = find(outVector > 0.80); - - % Output selection by threshold -% outVector = sum(outMovMatrix,2); -% outMov = find(outVector > patRec.nOut-1); - - - % Output selection by majority voting - outVector = sum(outMovMatrix,2); - [outMax outMov] = max(outVector); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to compute the classification output of One Vs One topology +% +% outMov : Index of the selected patterns +% outVector : Vector the raw classification output +% +% ------------------------- Updates & Contributors ------------------------ +% 2011-12-04 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update +function [outMov outVector] = PatRec_OneVsOne(patRec, x) + + % Initialize matrices + outMovMatrix = zeros(size(patRec.patRecTrained,2),size(patRec.patRecTrained,2)); + outMatrix = outMovMatrix; + nOut = size(patRec.patRecTrained,1); + + % Compute the output of each classification + for j = 1 : size(patRec.patRecTrained,1) + for k = j+1 : size(patRec.patRecTrained,2) + [outMovTemp outVectorTemp] = OneShotPatRec(patRec.patRecTrained(j,k),x); + + % How many patterns were predicted? + nPredMov = length(outMovTemp); + if nPredMov > 0 + % Count the victories of each pattern + if outVectorTemp(1) > outVectorTemp(2) + outMovMatrix(j,k) = 1; + outMovMatrix(k,j) = 0; + else + outMovMatrix(j,k) = 0; + outMovMatrix(k,j) = 1; + end + % Non-of them was predicted + else + outMovMatrix(j,k) = 0; + outMovMatrix(k,j) = 0; + end + outMatrix(j,k) = outVectorTemp(1); + outMatrix(k,j) = outVectorTemp(2); + end + end + + % Ouput selection by probability +% outVector = sum(outMovMatrix,2)./ (size(outMovMatrix,1) - 1); +% outMov = find(outVector > 0.80); + + % Output selection by threshold +% outVector = sum(outMovMatrix,2); +% outMov = find(outVector > patRec.nOut-1); + + + % Output selection by majority voting + outVector = sum(outMovMatrix,2); + [outMax outMov] = max(outVector); + end \ No newline at end of file diff --git a/PatRec/Topologies/PatRec_OneVsOneDoF.m b/PatRec/Topologies/PatRec_OneVsOneDoF.m new file mode 100644 index 0000000..184c71d --- /dev/null +++ b/PatRec/Topologies/PatRec_OneVsOneDoF.m @@ -0,0 +1,114 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to compute the classification output of One Vs One topology but +% it adds the modification of removing the losing movement of each DoF +% before the final computation +% +% outMov : Index of the selected patterns +% outVector : Vector the raw classification output +% +% ------------------------- Updates & Contributors ------------------------ +% 2011-12-04 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update +function [outMov outVector] = PatRec_OneVsOneDoF(patRec, x) + + % Initialize matrices + outMovMatrix = zeros(size(patRec.patRecTrained,2),size(patRec.patRecTrained,2)); + outMatrix = outMovMatrix; + + % Compute the output of each classification + for j = 1 : size(patRec.patRecTrained,1) + for k = j+1 : size(patRec.patRecTrained,2) + [outMovTemp outVectorTemp] = OneShotPatRec(patRec.patRecTrained(j,k),x); + % How many patterns were predicted? + nPredMov = length(outMovTemp); + if nPredMov > 0 + % Count the victories of each pattern + if outVectorTemp(1) > outVectorTemp(2) + outMovMatrix(j,k) = 1; + outMovMatrix(k,j) = 0; + else + outMovMatrix(j,k) = 0; + outMovMatrix(k,j) = 1; + end + end + outMatrix(j,k) = outVectorTemp(1); + outMatrix(k,j) = outVectorTemp(2); + end + end + + % Remove rest from the outMatrix +% if strcmp(patRec.mov(end),'Rest') +% outMatrix = outMatrix(1:end-1,1:end-1); +% end + + % Make 0 the lossing moving in the same DoF + for j = 1 : 2 : size(patRec.patRecTrained,1) + if outMovMatrix(j,j+1) == 1 + outMovMatrix(j+1,:) = 0; + outMatrix(j+1,:) = 0; + else + outMovMatrix(j,:) = 0; + outMatrix(j,:) = 0; + end + % Try by adding an extra point to the winner + %outVector(j) = outVector(j) + outMovMatrix(j,j+1); + %outVector(j+1) = outVector(j+1) + outMovMatrix(j+1,j); + end + + % Get the outVector (average) +% outVector = sum(outMatrix,2)./ (size(outMatrix,1) - 1); + outVector = sum(outMovMatrix,2)./ (size(outMovMatrix,1) - 1); + outMov = find(sum(outMovMatrix,2) >= (size(outMovMatrix,1) - 2)); + + +% % ANN delivers simultaneous prediction directly +% if strcmp(patRec.patRecTrained(1,2).algorithm,'ANN') +% % Giving good results: +% %outVector = sum(outMatrix,2)./(size(patRec.patRecTrained,1)/2); +% % Considering the mean +% %outVector = sum(outMatrix,2)./((size(outMatrix,1) - 2)/2); +% % Mean with reduction by threshold +% outVector = sum(outMatrix,2)./ (((size(outMatrix,1) - 2)/2)+.5); +% outMov = find(round(outVector)); +% % NOTE: Threshold needs to be implemented in outMov not in +% % outVector +% else +% outVector = sum(outMovMatrix,2); +% end + + + % Check if "rest" exist and select it if nothing else is fired + if strcmp(patRec.mov(end),'Rest') && isempty(find(round(outVector), 1)) + outVector(end+1) = 1; + end + + % Check againt rest +% if strcmp(patRec.mov(end),'Rest') +% %Get the winner without rest +% [outMax outMov] = max(outVector(1:end-1)); +% if outMovMatrix(outMov,end) == 1 +% outVector(end) = 0; +% end +% end + + +% [outMax outMov] = max(outVector); + +end \ No newline at end of file diff --git a/PatRec/Topologies/PatRec_SingleClassifier.m b/PatRec/Topologies/PatRec_SingleClassifier.m index 92a6da8..b831cc3 100644 --- a/PatRec/Topologies/PatRec_SingleClassifier.m +++ b/PatRec/Topologies/PatRec_SingleClassifier.m @@ -1,34 +1,34 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to compute the classification output of a single classifier -% Simple function to keep standarizeed the call for different classifiers -% topologies -% -% outMov : Index of the selected patterns -% outVector : Vector the raw classification output -% -% ------------------------- Updates & Contributors ------------------------ -% 2011-12-08 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function [outMov outVector] = PatRec_SingleClassifier(patRec, x) - - [outMov outVector] = OneShotPatRec(patRec.patRecTrained,x); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to compute the classification output of a single classifier +% Simple function to keep standarizeed the call for different classifiers +% topologies +% +% outMov : Index of the selected patterns +% outVector : Vector the raw classification output +% +% ------------------------- Updates & Contributors ------------------------ +% 2011-12-08 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function [outMov outVector] = PatRec_SingleClassifier(patRec, x) + + [outMov outVector] = OneShotPatRec(patRec.patRecTrained,x); + end \ No newline at end of file diff --git a/PatRec/UseDefaults.m b/PatRec/UseDefaults.m new file mode 100644 index 0000000..b191ea6 --- /dev/null +++ b/PatRec/UseDefaults.m @@ -0,0 +1,168 @@ +%BioPatRec Data Treatment Process: +function UseDefaults(button, simultaneous) +[file, path] = uigetfile({'*.mat';'*.csv'}); +load([path,file]); +set(button, 'String', 'Wait'); +progress = waitbar(0,'PreProcessing'); +drawnow; +handles = NaN; + + if isfield(recSession,'trdata') + recSession = rmfield(recSession,'trdata'); + end + if isfield(recSession,'cTp') + recSession = rmfield(recSession,'cTp'); + end + + if isfield(recSession,'nCh') + nCh = recSession.nCh; + if length(recSession.nCh) == 1 + recSession.nCh = 1:recSession.nCh; + nCh = recSession.nCh; + end + else + nCh = 1:length(recSession.tdata(1,:,1)); + recSession.nCh = nCh; + end + +%Signal Treatment +sigTreated = RemoveTransient_cTp(recSession, 0.7); +sigTreated = AddRestAsMovement(sigTreated, recSession); +waitbar(0.2,progress); +nw = fix(sigTreated.cT * sigTreated.cTp * sigTreated.nR / 0.2); +trP = 0.4; +vP = 0.2; +tP = 0.4; + +sigTreated.eCt = sigTreated.cT*sigTreated.cTp; +overlap = 0.05; +tT = sigTreated.cT * sigTreated.cTp * sigTreated.nR; +tw = 0.2; +offset = ceil(tw/overlap); +nw = fix(tT / overlap) - offset; +trN = fix(trP * nw); +vN = fix( vP* nw); +tN = fix(tP * nw); + while trN+vN+tN < nw + tN = tN + 1; + end +%Treat +sigTreated.trSets = trN; +sigTreated.vSets = vN; +sigTreated.tSets = tN; +sigTreated.nW = nw; +sigTreated.tW = tw; +sigTreated.wOverlap = overlap; +sigTreated.fFilter = 'None'; +sigTreated.sFilter = 'None'; + +sigTreated.twSegMethod = 'Overlapped Cons'; +waitbar(0.4,progress,'Treating'); + +%% Split the data into the time windows +[trData, vData, tData] = GetData(sigTreated); +%Remove raw trated data +sigTreated = rmfield(sigTreated,'trData'); +% add the new sets of tw for tr, v and t +sigTreated.trData = trData; +sigTreated.vData = vData; +sigTreated.tData = tData; + +%sigFeatures = GetAllSigFeatures(handles, sigTreated); +sigFeatures.sF = sigTreated.sF; +sigFeatures.tW = sigTreated.tW; +sigFeatures.nCh = sigTreated.nCh; +sigFeatures.mov = sigTreated.mov; + +% temporal conditional to keep compatibility with olrder rec session +if isfield(sigTreated,'dev') + sigFeatures.dev = sigTreated.dev; +else + sigFeatures.dev = 'Unknow'; +end + +if isfield(sigTreated,'comm') + sigFeatures.comm = sigTreated.comm; + if strcmp(sigFeatures.comm, 'COM') + if isfield(sigTreated,'comn') + sigFeatures.comn = sigTreated.comn; + end + end +else + sigFeatures.comm = 'N/A'; +end + +sigFeatures.fFilter = sigTreated.fFilter; +sigFeatures.sFilter = sigTreated.sFilter; +sigFeatures.trSets = sigTreated.trSets; +sigFeatures.vSets = sigTreated.vSets; +sigFeatures.tSets = sigTreated.tSets; +waitbar(0.5,progress,'Treating'); +nM = sigTreated.nM; % Number of exercises + +for m = 1: nM + for i = 1 : sigTreated.trSets + trFeatures(i,m) = GetSigFeatures(sigTreated.trData(:,:,m,i),sigTreated.sF); + end +end +waitbar(0.6,progress,'Treating'); +for m = 1: nM + for i = 1 : sigTreated.vSets + vFeatures(i,m) = GetSigFeatures(sigTreated.vData(:,:,m,i),sigTreated.sF); + end +end +waitbar(0.7,progress,'Treating'); +for m = 1: nM + for i = 1 : sigTreated.tSets + %tFeatures(i,m) = analyze_signal(sigTreated.tData(:,:,m,i),sigTreated.sF); + tFeatures(i,m) = GetSigFeatures(sigTreated.tData(:,:,m,i),sigTreated.sF); + end +end +waitbar(0.75,progress,'Treating'); +sigFeatures.trFeatures = trFeatures; +sigFeatures.vFeatures = vFeatures; +sigFeatures.tFeatures = tFeatures; +%Offline PatRec (Top4+tcard, LDA, OvsO) +%%sigFeatures = get(handles.t_sigFeatures,'UserData'); +sigFeatures.eTrSets = trN; +sigFeatures.eVSets = vN; +sigFeatures.eTSets = tN; +%?No-norm set? +%?No-feature reduction? +waitbar(0.85,progress,'Training'); +if (strcmp(simultaneous,'false')) +movMix = 'Individual Mov'; +randFeatures = true; +confMatFlag = false; +alg = 'Discriminant A.'; +tType = 'linear'; +topology = 'One-vs-One'; +selFeatures = {'tmabs';'twl';'tzc';'tslpch2';'tcard'}; +normSets = 'Select Normalization'; +algConf = []; +featReducAlg = 'Select Reduc./Selec.'; +patRec = OfflinePatRec(sigFeatures, selFeatures, randFeatures, normSets, alg, tType, algConf, movMix, topology, confMatFlag, featReducAlg); +handles.patRec = patRec; +Load_patRec(handles.patRec, 'GUI_TestPatRec_Mov2Mov',1); +set(button, 'String', 'Train Individual Mov)'); +waitbar(1,progress,'Training'); +close(progress); +else +movMix = 'Mixed Output'; +randFeatures = true; +confMatFlag = false; +alg = 'MLP'; +tType = 'Backpropagation'; +topology = 'Ago-antagonistAndMixed'; +selFeatures = {'tmabs';'twl';'tzc';'tslpch2';'tcard'}; +normSets = 'Midrange0Range2'; +algConf = []; +featReducAlg = 'Select Reduc./Selec.'; +patRec = OfflinePatRec(sigFeatures, selFeatures, randFeatures, normSets, alg, tType, algConf, movMix, topology, confMatFlag, featReducAlg); +handles.patRec = patRec; +Load_patRec(handles.patRec, 'GUI_TestPatRec_Mov2Mov',1); +set(button, 'String', 'Train Simultaneous Mov'); +waitbar(1,progress,'Training'); +close(progress); +end +end \ No newline at end of file diff --git a/SigFeatures/ExtractSets_Stack.m b/SigFeatures/ExtractSets_Stack.m index 8dc9813..99d8e79 100644 --- a/SigFeatures/ExtractSets_Stack.m +++ b/SigFeatures/ExtractSets_Stack.m @@ -1,78 +1,78 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function extract the corresponding set of selected movements. -% It keeps the format of xSets and xOuts but only with the rows -% corresponding to the selected movements (selSets) -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-11-29 / Max Ortiz / Creation - - -function [trSetsX, trOutsX, vSetsX, vOutsX] = ExtractSets_Stack(trSets, trOuts, vSets, vOuts, selSets) - - % Init the matrices - trSetsX = []; - trOutsX = []; - vSetsX = []; - vOutsX = []; - - % Extract the training set - for i = 1 : size(selSets,2) - % Training - setsIdx = find(trOuts(:,selSets(i))); - trSetsX = [trSetsX ; trSets(setsIdx,:)]; - nS = size(setsIdx,1); - % This would only work with equal number of sets per class - % trOutsX(1+(nS*(i-1)):nS*i,i) = 1; - - % This works for different set sizes - if isempty(trOutsX) - trOutsX(1:nS,i) = 1; - else - trOutsX(end+1:end+nS,i) = 1; - end - - % Validation - setsIdx = find(vOuts(:,selSets(i))); - vSetsX = [vSetsX ; vSets(setsIdx,:)]; - nS = size(setsIdx,1); - % This would only work with equal number of sets per class - % vOutsX(1+(nS*(i-1)):nS*i,i) = 1; - - % This works for different set sizes - if isempty(vOutsX) - vOutsX(1:nS,i) = 1; - else - vOutsX(end+1:end+nS,i) = 1; - end - - end - - - % Bad (slow) way -% for i = 1 : size(trOuts,1) -% if find(selSets == find(trOuts(i,:),1)) -% trSetsX = [trSetsX ; trSets(i,:)]; -% trOutsX = [trOutsX ; trOuts(i,:)]; -% end -% end - - -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function extract the corresponding set of selected movements. +% It keeps the format of xSets and xOuts but only with the rows +% corresponding to the selected movements (selSets) +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-11-29 / Max Ortiz / Creation + + +function [trSetsX, trOutsX, vSetsX, vOutsX] = ExtractSets_Stack(trSets, trOuts, vSets, vOuts, selSets) + + % Init the matrices + trSetsX = []; + trOutsX = []; + vSetsX = []; + vOutsX = []; + + % Extract the training set + for i = 1 : size(selSets,2) + % Training + setsIdx = find(trOuts(:,selSets(i))); + trSetsX = [trSetsX ; trSets(setsIdx,:)]; + nS = size(setsIdx,1); + % This would only work with equal number of sets per class + % trOutsX(1+(nS*(i-1)):nS*i,i) = 1; + + % This works for different set sizes + if isempty(trOutsX) + trOutsX(1:nS,i) = 1; + else + trOutsX(end+1:end+nS,i) = 1; + end + + % Validation + setsIdx = find(vOuts(:,selSets(i))); + vSetsX = [vSetsX ; vSets(setsIdx,:)]; + nS = size(setsIdx,1); + % This would only work with equal number of sets per class + % vOutsX(1+(nS*(i-1)):nS*i,i) = 1; + + % This works for different set sizes + if isempty(vOutsX) + vOutsX(1:nS,i) = 1; + else + vOutsX(end+1:end+nS,i) = 1; + end + + end + + + % Bad (slow) way +% for i = 1 : size(trOuts,1) +% if find(selSets == find(trOuts(i,:),1)) +% trSetsX = [trSetsX ; trSets(i,:)]; +% trOutsX = [trOutsX ; trOuts(i,:)]; +% end +% end + + +end diff --git a/SigFeatures/ExtractSets_Stack_MixRest.m b/SigFeatures/ExtractSets_Stack_MixRest.m index d98c391..93367a7 100644 --- a/SigFeatures/ExtractSets_Stack_MixRest.m +++ b/SigFeatures/ExtractSets_Stack_MixRest.m @@ -1,115 +1,115 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec, Copyright © 2009 Max J. Ortiz C. -% BioPatRec is free software under the GNU GPL v3, or any later version. -% See the file "LICENSE" for the full license governing this code. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Chalmers -% University of Technology. Valuable authors’ contributions are mentioned -% below and in the acknowledgements in the project web page. -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project, or send your comments to: -% maxo@chalmers.se. -% -% This copyright notice must be kept in this or any new source file linked -% to BioPatRec -% -% -------------------------- Function Description ------------------------- -% This function extract the corresponding set of selected movements. -% It keeps the format of xSets and xOuts but only with the rows -% corresponding to the selected movements (selSets) -% -% It differs from ExtractSets_Stack by also including a mix of ALL the -% remaing movements in sets with a single output. -% -% ------------------------- Updates & Contributors ------------------------ -% 2012-03-04 / Max Ortiz / Creation -% 2012-04-08 / Max Ortiz / Modified to handle mixed-classes - - -function [trSetsX, trOutsX, vSetsX, vOutsX, selSets, movLables] = ExtractSets_Stack_MixRest(trSets, trOuts, vSets, vOuts, selSets, movLables) - - %% Init the matrices - trSetsX = []; - trOutsX = []; - vSetsX = []; - vOutsX = []; - - %% Extract the training set - for i = 1 : size(selSets,2) - % Training - trSetsIdx = find(trOuts(:,selSets(i))); - trSetsX = [trSetsX ; trSets(trSetsIdx,:)]; - nTrS = size(trSetsIdx,1); - % This would only work with equal number of sets per class - % trOutsX(1+(nTrS*(i-1)):nTrS*i,i) = 1; - - % This works for different set sizes - if isempty(trOutsX) - trOutsX(1:nTrS,i) = 1; - else - trOutsX(end+1:end+nTrS,i) = 1; - end - - - % Validation - vSetsIdx = find(vOuts(:,selSets(i))); - vSetsX = [vSetsX ; vSets(vSetsIdx,:)]; - nVs = size(vSetsIdx,1); - % This would only work with equal number of sets per class - % vOutsX(1+(nVs*(i-1)):nVs*i,i) = 1; - - % This works for different set sizes - if isempty(vOutsX) - vOutsX(1:nVs,i) = 1; - else - vOutsX(end+1:end+nVs,i) = 1; - end - end - - - %% Get mixed sets - mixSet = []; - % Get index of non selected sets - restSets = zeros(1,size(trOuts,2)); - restSets(selSets) = 1; - - for i = 1 : size(trSets,1) - mask = and(trOuts(i,:), restSets); - if ~mask - mixSet = [mixSet ; trSets(i,:)]; - end - end - - trSetsX = [trSetsX ; mixSet]; - trOutsX(end+1:size(trSetsX,1),end+1) = 1; - - %% Get mixed sets for validation - mixSet = []; - - for i = 1 : size(vSets,1) - mask = and(vOuts(i,:), restSets); - if ~mask - mixSet = [mixSet ; vSets(i,:)]; - end - end - - vSetsX = [vSetsX ; mixSet]; - vOutsX(end+1:size(vSetsX,1),end+1) = 1; - - %% Modify lables and index - % A new lable needs to be created for the mixed sets - if ~strcmp(movLables(end),'Mixed') - movLables(end+1) = {'Mixed'}; - end - selSets(end+1) = size(movLables,1); - - - if size(trSetsX,1) ~= size(trOutsX,1) || size(vSetsX,1) ~= size(vOutsX,1) - disp('Error obtaining the lables in ExtractSets_Stack_AllButSome') - patRec = []; - return; - end - - -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec, Copyright © 2009 Max J. Ortiz C. +% BioPatRec is free software under the GNU GPL v3, or any later version. +% See the file "LICENSE" for the full license governing this code. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Chalmers +% University of Technology. Valuable authors’ contributions are mentioned +% below and in the acknowledgements in the project web page. +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project, or send your comments to: +% maxo@chalmers.se. +% +% This copyright notice must be kept in this or any new source file linked +% to BioPatRec +% +% -------------------------- Function Description ------------------------- +% This function extract the corresponding set of selected movements. +% It keeps the format of xSets and xOuts but only with the rows +% corresponding to the selected movements (selSets) +% +% It differs from ExtractSets_Stack by also including a mix of ALL the +% remaing movements in sets with a single output. +% +% ------------------------- Updates & Contributors ------------------------ +% 2012-03-04 / Max Ortiz / Creation +% 2012-04-08 / Max Ortiz / Modified to handle mixed-classes + + +function [trSetsX, trOutsX, vSetsX, vOutsX, selSets, movLables] = ExtractSets_Stack_MixRest(trSets, trOuts, vSets, vOuts, selSets, movLables) + + %% Init the matrices + trSetsX = []; + trOutsX = []; + vSetsX = []; + vOutsX = []; + + %% Extract the training set + for i = 1 : size(selSets,2) + % Training + trSetsIdx = find(trOuts(:,selSets(i))); + trSetsX = [trSetsX ; trSets(trSetsIdx,:)]; + nTrS = size(trSetsIdx,1); + % This would only work with equal number of sets per class + % trOutsX(1+(nTrS*(i-1)):nTrS*i,i) = 1; + + % This works for different set sizes + if isempty(trOutsX) + trOutsX(1:nTrS,i) = 1; + else + trOutsX(end+1:end+nTrS,i) = 1; + end + + + % Validation + vSetsIdx = find(vOuts(:,selSets(i))); + vSetsX = [vSetsX ; vSets(vSetsIdx,:)]; + nVs = size(vSetsIdx,1); + % This would only work with equal number of sets per class + % vOutsX(1+(nVs*(i-1)):nVs*i,i) = 1; + + % This works for different set sizes + if isempty(vOutsX) + vOutsX(1:nVs,i) = 1; + else + vOutsX(end+1:end+nVs,i) = 1; + end + end + + + %% Get mixed sets + mixSet = []; + % Get index of non selected sets + restSets = zeros(1,size(trOuts,2)); + restSets(selSets) = 1; + + for i = 1 : size(trSets,1) + mask = and(trOuts(i,:), restSets); + if ~mask + mixSet = [mixSet ; trSets(i,:)]; + end + end + + trSetsX = [trSetsX ; mixSet]; + trOutsX(end+1:size(trSetsX,1),end+1) = 1; + + %% Get mixed sets for validation + mixSet = []; + + for i = 1 : size(vSets,1) + mask = and(vOuts(i,:), restSets); + if ~mask + mixSet = [mixSet ; vSets(i,:)]; + end + end + + vSetsX = [vSetsX ; mixSet]; + vOutsX(end+1:size(vSetsX,1),end+1) = 1; + + %% Modify lables and index + % A new lable needs to be created for the mixed sets + if ~strcmp(movLables(end),'Mixed') + movLables(end+1) = {'Mixed'}; + end + selSets(end+1) = size(movLables,1); + + + if size(trSetsX,1) ~= size(trOutsX,1) || size(vSetsX,1) ~= size(vOutsX,1) + disp('Error obtaining the lables in ExtractSets_Stack_AllButSome') + patRec = []; + return; + end + + +end diff --git a/SigFeatures/ExtractSets_Stack_MixRestEqual.m b/SigFeatures/ExtractSets_Stack_MixRestEqual.m index 932473a..7420a9b 100644 --- a/SigFeatures/ExtractSets_Stack_MixRestEqual.m +++ b/SigFeatures/ExtractSets_Stack_MixRestEqual.m @@ -1,175 +1,175 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function extract the corresponding set of selected movements. -% It keeps the format of xSets and xOuts but only with the rows -% corresponding to the selected movements (selSets) -% -% It differes from ExtractSets_Stack by also including a mix of the remaing -% movements in sets with a single output. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-12-02 / Max Ortiz / Creation - - -function [trSetsX, trOutsX, vSetsX, vOutsX, selSets, movLables] = ExtractSets_Stack_MixRestEqual(trSets, trOuts, vSets, vOuts, selSets, movLables) - - %% Init the matrices - trSetsX = []; - trOutsX = []; - vSetsX = []; - vOutsX = []; - - %% Extract the training set - for i = 1 : size(selSets,2) - % Training - trSetsIdx = find(trOuts(:,selSets(i))); - trSetsX = [trSetsX ; trSets(trSetsIdx,:)]; - nTrS = size(trSetsIdx,1); - % This would only work with equal number of sets per class - % trOutsX(1+(nTrS*(i-1)):nTrS*i,i) = 1; - - % This works for different set sizes - if isempty(trOutsX) - trOutsX(1:nTrS,i) = 1; - else - trOutsX(end+1:end+nTrS,i) = 1; - end - - % Validation - vSetsIdx = find(vOuts(:,selSets(i))); - vSetsX = [vSetsX ; vSets(vSetsIdx,:)]; - nVs = size(vSetsIdx,1); - % This would only work with equal number of sets per class - % vOutsX(1+(nVs*(i-1)):nVs*i,i) = 1; - - % This works for different set sizes - if isempty(vOutsX) - vOutsX(1:nVs,i) = 1; - else - vOutsX(end+1:end+nVs,i) = 1; - end - - end - - %% Get mixed sets - mixSet = []; - % Get index of non selected sets - restSets = zeros(1,size(trOuts,2)); - restSets(selSets) = 1; - % Setep - movStep = find(~trOuts(:,1)); - movStep = movStep(1)-1; - - % Get sets orderly one per set until the data set is completed - % Doing this sequencially assures that at least 1 set per movemnts is - % included - stopFlag = 0; - for i = 1 : nTrS - for j = i : movStep :size(trSets,1) - mask = and(trOuts(j,:), restSets); - if ~mask - mixSet = [mixSet ; trSets(j,:)]; - end - if size(mixSet,1) == nTrS - stopFlag = 1; - break; - end - end - if stopFlag - break; - end - end - -% %% Get randomly data sets from the training set -% %Randomize Idx -% setIdx = randperm(size(trOuts,1)); -% %Get data sets -% for i = 1 : size(trSets,1) -% mask = and(trOuts(setIdx(i),:), restSets); -% if ~mask -% mixSet = [mixSet ; trSets(setIdx(i),:)]; -% end -% %Stop if the number of mix sets is reached -% if size(mixSet,1) == nTrS -% break; -% end -% end - - trSetsX = [trSetsX ; mixSet]; - trOutsX(end+1:end+nTrS,end+1) = 1; - - %% Get mixed sets for validation - mixSet = []; - % Setep - movStep = find(~vOuts(:,1)); - movStep = movStep(1)-1; - - % Get sets orderly one per set until the data set is completed - stopFlag = 0; - for i = 1 : nVs - for j = i : movStep :size(vSets,1) - mask = and(vOuts(j,:), restSets); - if ~mask - mixSet = [mixSet ; vSets(j,:)]; - end - if size(mixSet,1) == nVs - stopFlag = 1; - break; - end - end - if stopFlag - break; - end - end - - % Get randomly data sets from the validation set -% %Randomize Idx -% setIdx = randperm(size(vOuts,1)); -% %Get data sets -% for i = 1 : size(vSets,1) -% mask = and(vOuts(setIdx(i),:), restSets); -% if ~mask -% mixSet = [mixSet ; vSets(setIdx(i),:)]; -% end -% %Stop if the number of mix sets is reached -% if size(mixSet,1) == nVs -% break; -% end -% end - - vSetsX = [vSetsX ; mixSet]; - vOutsX(end+1:end+nVs,end+1) = 1; - - %% Modify lables and index - % A new lable needs to be created for the mixed sets - if ~strcmp(movLables(end),'Mixed') - movLables(end+1) = {'Mixed'}; - end - selSets(end+1) = size(movLables,1); - - - if size(trSetsX,1) ~= size(trOutsX,1) || size(vSetsX,1) ~= size(vOutsX,1) - disp('Error obtaining the lables in ExtractSets_Stack_AllButSome') - patRec = []; - return; - end - - -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function extract the corresponding set of selected movements. +% It keeps the format of xSets and xOuts but only with the rows +% corresponding to the selected movements (selSets) +% +% It differes from ExtractSets_Stack by also including a mix of the remaing +% movements in sets with a single output. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-12-02 / Max Ortiz / Creation + + +function [trSetsX, trOutsX, vSetsX, vOutsX, selSets, movLables] = ExtractSets_Stack_MixRestEqual(trSets, trOuts, vSets, vOuts, selSets, movLables) + + %% Init the matrices + trSetsX = []; + trOutsX = []; + vSetsX = []; + vOutsX = []; + + %% Extract the training set + for i = 1 : size(selSets,2) + % Training + trSetsIdx = find(trOuts(:,selSets(i))); + trSetsX = [trSetsX ; trSets(trSetsIdx,:)]; + nTrS = size(trSetsIdx,1); + % This would only work with equal number of sets per class + % trOutsX(1+(nTrS*(i-1)):nTrS*i,i) = 1; + + % This works for different set sizes + if isempty(trOutsX) + trOutsX(1:nTrS,i) = 1; + else + trOutsX(end+1:end+nTrS,i) = 1; + end + + % Validation + vSetsIdx = find(vOuts(:,selSets(i))); + vSetsX = [vSetsX ; vSets(vSetsIdx,:)]; + nVs = size(vSetsIdx,1); + % This would only work with equal number of sets per class + % vOutsX(1+(nVs*(i-1)):nVs*i,i) = 1; + + % This works for different set sizes + if isempty(vOutsX) + vOutsX(1:nVs,i) = 1; + else + vOutsX(end+1:end+nVs,i) = 1; + end + + end + + %% Get mixed sets + mixSet = []; + % Get index of non selected sets + restSets = zeros(1,size(trOuts,2)); + restSets(selSets) = 1; + % Setep + movStep = find(~trOuts(:,1)); + movStep = movStep(1)-1; + + % Get sets orderly one per set until the data set is completed + % Doing this sequencially assures that at least 1 set per movemnts is + % included + stopFlag = 0; + for i = 1 : nTrS + for j = i : movStep :size(trSets,1) + mask = and(trOuts(j,:), restSets); + if ~mask + mixSet = [mixSet ; trSets(j,:)]; + end + if size(mixSet,1) == nTrS + stopFlag = 1; + break; + end + end + if stopFlag + break; + end + end + +% %% Get randomly data sets from the training set +% %Randomize Idx +% setIdx = randperm(size(trOuts,1)); +% %Get data sets +% for i = 1 : size(trSets,1) +% mask = and(trOuts(setIdx(i),:), restSets); +% if ~mask +% mixSet = [mixSet ; trSets(setIdx(i),:)]; +% end +% %Stop if the number of mix sets is reached +% if size(mixSet,1) == nTrS +% break; +% end +% end + + trSetsX = [trSetsX ; mixSet]; + trOutsX(end+1:end+nTrS,end+1) = 1; + + %% Get mixed sets for validation + mixSet = []; + % Setep + movStep = find(~vOuts(:,1)); + movStep = movStep(1)-1; + + % Get sets orderly one per set until the data set is completed + stopFlag = 0; + for i = 1 : nVs + for j = i : movStep :size(vSets,1) + mask = and(vOuts(j,:), restSets); + if ~mask + mixSet = [mixSet ; vSets(j,:)]; + end + if size(mixSet,1) == nVs + stopFlag = 1; + break; + end + end + if stopFlag + break; + end + end + + % Get randomly data sets from the validation set +% %Randomize Idx +% setIdx = randperm(size(vOuts,1)); +% %Get data sets +% for i = 1 : size(vSets,1) +% mask = and(vOuts(setIdx(i),:), restSets); +% if ~mask +% mixSet = [mixSet ; vSets(setIdx(i),:)]; +% end +% %Stop if the number of mix sets is reached +% if size(mixSet,1) == nVs +% break; +% end +% end + + vSetsX = [vSetsX ; mixSet]; + vOutsX(end+1:end+nVs,end+1) = 1; + + %% Modify lables and index + % A new lable needs to be created for the mixed sets + if ~strcmp(movLables(end),'Mixed') + movLables(end+1) = {'Mixed'}; + end + selSets(end+1) = size(movLables,1); + + + if size(trSetsX,1) ~= size(trOutsX,1) || size(vSetsX,1) ~= size(vOutsX,1) + disp('Error obtaining the lables in ExtractSets_Stack_AllButSome') + patRec = []; + return; + end + + +end diff --git a/SigFeatures/ExtractSigFeature.m b/SigFeatures/ExtractSigFeature.m index f8570ee..4bebffe 100644 --- a/SigFeatures/ExtractSigFeature.m +++ b/SigFeatures/ExtractSigFeature.m @@ -1,40 +1,40 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% The different with GetSigFeatures is that this routines extracts a single -% feature from a complete time series. GetSigFeatures can extract several -% features for a single time window. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-06-03 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function fData = ExtractSigFeature(data,sF,fID) - -tS = size(data,1); % Total samples -twS = 0.2 * sF; % Time window samples, considering tw of 200 ms -overlapS = 0.02*sF; % Overlap samples considering 20 ms -fData = []; - -for i = 1 : overlapS : tS-twS - feature = GetSigFeatures(data(i:i+twS,:), sF, {fID}); - fData = [fData ; feature.(fID)]; -end - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% The difference with GetSigFeatures is that this routines extracts a single +% feature from a complete time series. GetSigFeatures can extract several +% features for a single time window. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-03 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function fData = ExtractSigFeature(data,sF,fID) + +tS = size(data,1); % Total samples +twS = 0.2 * sF; % Time window samples, considering tw of 200 ms +overlapS = 0.02*sF; % Overlap samples considering 20 ms +fData = []; + +for i = 1 : overlapS : tS-twS + feature = GetSigFeatures(data(i:i+twS,:), sF, {fID}); + fData = [fData ; feature.(fID)]; +end + + diff --git a/SigFeatures/ExtractSigFeatureVar.m b/SigFeatures/ExtractSigFeatureVar.m new file mode 100644 index 0000000..0405126 --- /dev/null +++ b/SigFeatures/ExtractSigFeatureVar.m @@ -0,0 +1,41 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% The difference with GetSigFeatures is that this routines extracts a single +% feature from a complete time series. GetSigFeatures can extract several +% features for a single time window. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-06-03 / Max Ortiz / Creation +% 2014-12-25 / Max Ortiz / Made twS and overlapS input variables +% 20xx-xx-xx / Author / Comment on update + +function fData = ExtractSigFeatureVar(data,sF,fID,twS,overlapS) + +tS = size(data,1); % Total samples +%twS = 0.2 * sF; % Time window samples, considering tw of 200 ms +%overlapS = 0.02*sF; % Overlap samples considering 20 ms +fData = []; + +for i = 1 : overlapS : tS-twS + feature = GetSigFeatures(data(i:i+twS,:), sF, {fID}); + fData = [fData ; feature.(fID)]; +end + + diff --git a/SigFeatures/GetAllSigFeatures.m b/SigFeatures/GetAllSigFeatures.m index f0d26cf..dc33855 100644 --- a/SigFeatures/GetAllSigFeatures.m +++ b/SigFeatures/GetAllSigFeatures.m @@ -1,96 +1,109 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function recieves sigTreated and returns sigFeatures which is a -% matrix of structs of (sets x movements) where each struct contains the -% signals features identfied by their name code (ID) -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-07-27 / Max Ortiz / Creation from EMG_AQ routines -% 20xx-xx-xx / Author / Comment on update - -function sigFeatures = GetAllSigFeatures(handles, sigTreated) - - sigFeatures.sF = sigTreated.sF; - sigFeatures.tW = sigTreated.tW; - sigFeatures.nCh = sigTreated.nCh; - sigFeatures.mov = sigTreated.mov; - - % temporal conditional to keep compatibility with olrder rec session - if isfield(sigTreated,'dev') - sigFeatures.dev = sigTreated.dev; - else - sigFeatures.dev = 'Unknow'; - end - - sigFeatures.fFilter = sigTreated.fFilter; - sigFeatures.sFilter = sigTreated.sFilter; - sigFeatures.trSets = sigTreated.trSets; - sigFeatures.vSets = sigTreated.vSets; - sigFeatures.tSets = sigTreated.tSets; - - nM = sigTreated.nM; % Number of exercises - - set(handles.t_msg,'String','Extracting ALL features for training...'); - drawnow; - for m = 1: nM - for i = 1 : sigTreated.trSets - trFeatures(i,m) = GetSigFeatures(sigTreated.trData(:,:,m,i),sigTreated.sF); - end - end - - set(handles.t_msg,'String','Extracting ALL features for validation...'); - drawnow; - for m = 1: nM - for i = 1 : sigTreated.vSets - vFeatures(i,m) = GetSigFeatures(sigTreated.vData(:,:,m,i),sigTreated.sF); - end - end - - set(handles.t_msg,'String','Extracting ALL features for testing...'); - drawnow; - for m = 1: nM - for i = 1 : sigTreated.tSets - %tFeatures(i,m) = analyze_signal(sigTreated.tData(:,:,m,i),sigTreated.sF); - tFeatures(i,m) = GetSigFeatures(sigTreated.tData(:,:,m,i),sigTreated.sF); - end - end - - sigFeatures.trFeatures = trFeatures; - sigFeatures.vFeatures = vFeatures; - sigFeatures.tFeatures = tFeatures; - -end -% for e = 1: Ne -% for i = 1 : sigTreated.nw -% iidx = 1 + (assize*(i-1)); -% eidx = assize+(assize*(i-1)); -% tempdata(i,e) = analyze_signal(data(iidx:eidx,:,e),sigTreated.sF); -% end -% end -% -% %trPr = 0.6; % Percentage to be used for training -% %Ntr = round(nR * nTw * trPr); % Compute Number of trainning for the whole contraction time -% %Nv = round(nR * nTw * (1-trPr)/2); % Compute Number of validation -% trSets = sigTreated.trSets; -% vSets = sigTreated.vSets; -% tSets = sigTreated.tSets; -% trdata = tempdata(1:trSets,:); -% vdata = tempdata(trSets+1:trSets+vSets,:); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function recieves sigTreated and returns sigFeatures which is a +% matrix of structs of (sets x movements) where each struct contains the +% signals features identfied by their name code (ID) +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-27 / Max Ortiz / Creation from EMG_AQ routines +% 2014-12-01 / Enzo Mastinu / Added the handling part for the COM port number + % information into the parameters +% 20xx-xx-xx / Author / Comment on update + +function sigFeatures = GetAllSigFeatures(handles, sigTreated) + + sigFeatures.sF = sigTreated.sF; + sigFeatures.tW = sigTreated.tW; + sigFeatures.nCh = sigTreated.nCh; + sigFeatures.mov = sigTreated.mov; + sigFeatures.scaled = sigTreated.scaled; + sigFeatures.noise = sigTreated.noise; + + % temporal conditional to keep compatibility with olrder rec session + if isfield(sigTreated,'dev') + sigFeatures.dev = sigTreated.dev; + else + sigFeatures.dev = 'Unknow'; + end + + if isfield(sigTreated,'comm') + sigFeatures.comm = sigTreated.comm; + if isfield(sigTreated,'comn') + sigFeatures.comn = sigTreated.comn; + end + else + sigFeatures.comm = 'N/A'; + end + + sigFeatures.fFilter = sigTreated.fFilter; + sigFeatures.sFilter = sigTreated.sFilter; + sigFeatures.trSets = sigTreated.trSets; + sigFeatures.vSets = sigTreated.vSets; + sigFeatures.tSets = sigTreated.tSets; + + nM = sigTreated.nM; % Number of exercises + + set(handles.t_msg,'String','Extracting ALL features for training...'); + drawnow; + for m = 1: nM + for i = 1 : sigTreated.trSets + trFeatures(i,m) = GetSigFeatures(sigTreated.trData(:,:,m,i),sigTreated.sF); + end + end + + set(handles.t_msg,'String','Extracting ALL features for validation...'); + drawnow; + for m = 1: nM + for i = 1 : sigTreated.vSets + vFeatures(i,m) = GetSigFeatures(sigTreated.vData(:,:,m,i),sigTreated.sF); + end + end + + set(handles.t_msg,'String','Extracting ALL features for testing...'); + drawnow; + for m = 1: nM + for i = 1 : sigTreated.tSets + %tFeatures(i,m) = analyze_signal(sigTreated.tData(:,:,m,i),sigTreated.sF); + tFeatures(i,m) = GetSigFeatures(sigTreated.tData(:,:,m,i),sigTreated.sF); + end + end + + sigFeatures.trFeatures = trFeatures; + sigFeatures.vFeatures = vFeatures; + sigFeatures.tFeatures = tFeatures; + +end +% for e = 1: Ne +% for i = 1 : sigTreated.nw +% iidx = 1 + (assize*(i-1)); +% eidx = assize+(assize*(i-1)); +% tempdata(i,e) = analyze_signal(data(iidx:eidx,:,e),sigTreated.sF); +% end +% end +% +% %trPr = 0.6; % Percentage to be used for training +% %Ntr = round(nR * nTw * trPr); % Compute Number of trainning for the whole contraction time +% %Nv = round(nR * nTw * (1-trPr)/2); % Compute Number of validation +% trSets = sigTreated.trSets; +% vSets = sigTreated.vSets; +% tSets = sigTreated.tSets; +% trdata = tempdata(1:trSets,:); +% vdata = tempdata(trSets+1:trSets+vSets,:); % tdata = tempdata(trSets+vSets+1:trSets+vSets+tSets,:); \ No newline at end of file diff --git a/SigFeatures/GetFFT.m b/SigFeatures/GetFFT.m index 4b362dc..062b8fe 100644 --- a/SigFeatures/GetFFT.m +++ b/SigFeatures/GetFFT.m @@ -1,50 +1,61 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Routine based in matlab examples, further optimization is probabily -% possible -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% Max Ortiz 2009-04-29 Creation -% Max Ortiz 2011-07-28 Adapted to BioPatRec - - -function pF = GetFFT(pF) - - cF = 1000; - - % Fast Fourier Transform - NFFT = 2^nextpow2(pF.sp); % Next power of 2 from number of samples - dataf = fft(pF.data,NFFT)/pF.sp; % Gets the fast fourier transform - pF.fftData = 2*abs(dataf((1:NFFT/2+1),:)); % Get the half of the data considering abs values, - % since it is simetric and we - % only look at the half, it is multiply for 2 - pF.fftData(1,:) = 0; % The first element is made 0 to reduce artifats of low fqs. - - pF.fftDataT = sum(pF.fftData); % Sum of all frequency contributions - - pF.fV = pF.sF/2*linspace(0,1,NFFT/2+1); % Creates the frequency vector - - % Cuff the matrix to Fc to analysis - cS = round(cF * length(pF.fftData(:,1)) / (pF.sF/2)); - pF.fftData = pF.fftData(1:cS,:); - pF.fV = pF.fV(1:cS); - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Routine based in matlab examples, further optimization is probabily +% possible +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% Max Ortiz 2009-04-29 Creation for EMG_AQ (old BioPatRec) +% Max Ortiz 2011-07-28 Adapted to BioPatRec +% Max Ortiz 2013-10-09 Fixed identation and add commented code for ploting + + +function pF = GetFFT(pF) + + cF = pF.sF/2; + + % Fast Fourier Transform + NFFT = 2^nextpow2(pF.sp); % Next power of 2 from number of samples + dataf = fft(pF.data,NFFT)/pF.sp; % Gets the fast fourier transform + pF.fftData = 2*abs(dataf((1:NFFT/2+1),:)); % Get the half of the data considering abs values, + % since it is simetric and we + % only look at the half, it is multiply for 2 + + pF.fftData(1,:) = 0; % The first element is made 0 to reduce artifats of low fqs. + + pF.fftDataT = sum(pF.fftData); % Sum of all frequency contributions + + pF.fV = pF.sF/2*linspace(0,1,NFFT/2+1); % Creates the frequency vector + + % Plot for visualization if required + % figure(); + % plot(pF.fV,pF.fftData(:,1)) + + % Cuff the matrix to cF for analysis + cF = 1000; % Cut frequency for features stimation + % If sF > 1kH, the estimation of frequency related features + % will be within 0 to 1kH + if pF.fV(end) > cF + cS = round(cF * length(pF.fftData(:,1)) / (pF.sF/2)); + pF.fftData = pF.fftData(1:cS,:); + pF.fV = pF.fV(1:cS); + end + + + diff --git a/SigFeatures/GetSetsLables_Stack.m b/SigFeatures/GetSetsLables_Stack.m index d69dc18..0edb2a4 100644 --- a/SigFeatures/GetSetsLables_Stack.m +++ b/SigFeatures/GetSetsLables_Stack.m @@ -1,58 +1,58 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------- Function Description ------------- -% Function to obtain the corresponding movements from each raw of data in -% the sets -% -% ------------- Updates ------------- -% 11-06-24 / Max Ortiz / Creation -% 11-11-30 / Max Ortiz / Removed tLables which are never used. - -function [trLables, vLables] = GetSetsLables_Stack(mov, trOut, vOut, movIdxIndv) -%function [trLables, vLables, tLables] = GetSetsLables_Stack(mov, trOut, -%vOut, tOut, movIdxIndv) - - movIndv = mov(movIdxIndv); - - - trLables = []; - vLables = []; - tLables = []; - - for i = 1 : size(trOut,1) - trLables = [trLables;movIndv(find(trOut(i,:)))]; - end - - for i = 1 : size(vOut,1) - vLables = [vLables;movIndv(find(vOut(i,:)))]; - end - -% for i = 1 : length(tOut) -% idxMov = find(tOut(i,:)); -% % check the number of labels involved -% if size(idxMov,2) == 1 -% tLables = [tLables; movIndv(find(tOut(i,:)))]; -% else -% strLabel = []; -% for j = 1 : size(idxMov,2) -% strLabel = [strLabel char(movIndv(idxMov(j))) ' + ']; -% end -% strLabel = strLabel(1:end-3); -% tLables = [tLables; strLabel]; -% end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------- Function Description ------------- +% Function to obtain the corresponding movements from each raw of data in +% the sets +% +% ------------- Updates ------------- +% 11-06-24 / Max Ortiz / Creation +% 11-11-30 / Max Ortiz / Removed tLables which are never used. + +function [trLables, vLables] = GetSetsLables_Stack(mov, trOut, vOut, movIdxIndv) +%function [trLables, vLables, tLables] = GetSetsLables_Stack(mov, trOut, +%vOut, tOut, movIdxIndv) + + movIndv = mov(movIdxIndv); + + + trLables = []; + vLables = []; + tLables = []; + + for i = 1 : size(trOut,1) + trLables = [trLables;movIndv(find(trOut(i,:)))]; + end + + for i = 1 : size(vOut,1) + vLables = [vLables;movIndv(find(vOut(i,:)))]; + end + +% for i = 1 : length(tOut) +% idxMov = find(tOut(i,:)); +% % check the number of labels involved +% if size(idxMov,2) == 1 +% tLables = [tLables; movIndv(find(tOut(i,:)))]; +% else +% strLabel = []; +% for j = 1 : size(idxMov,2) +% strLabel = [strLabel char(movIndv(idxMov(j))) ' + ']; +% end +% strLabel = strLabel(1:end-3); +% tLables = [tLables; strLabel]; +% end % end \ No newline at end of file diff --git a/SigFeatures/GetSets_Stack.m b/SigFeatures/GetSets_Stack.m index 9e206a9..c7c6616 100644 --- a/SigFeatures/GetSets_Stack.m +++ b/SigFeatures/GetSets_Stack.m @@ -1,99 +1,99 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% % This function will create matrices of training, validation and test sets -% All movements will be STACK one over each other -% -% input: trFeatures contains the splitted data, is a Nsplits x Nexercises structure matrix -% vFeatures is similar to trFeatures -% features contains the name of the charactaristics to be used -% output: trsets are the normalized training sets -% vsets are the normalized validation sets -% trOut contains the correspondet outputs -% vOut contains the correspondet outputs -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-04-26 / Max Ortiz / Creation -% 2009-07-21 / Max Ortiz / To hanlde "treated_data" struct and selection of the number of set -% 2011-06-24 / Max Ortiz / Update to new coding standard and name to stack - -function [trSet, trOut, vSet, vOut, tSet, tOut] = GetSets_Stack(sigFeatures, features) - -nM = length(sigFeatures.trFeatures(1,:)); % Number of movements -trSets = sigFeatures.eTrSets; % effective number of sets for trainning - -if isempty(sigFeatures.vFeatures) - vSets = 0; -else - vSets = sigFeatures.eVSets; % Number of sets for valdiation -end - -if isempty(sigFeatures.tFeatures) - tSets = 0; -else - tSets = sigFeatures.eTSets; % Number of sets for testing -end - -Ntrset = trSets * nM; -Nvset = vSets * nM; -Ntset = tSets * nM; -trSet = zeros(Ntrset, length(features)); -vSet = zeros(Nvset , length(features)); -tSet = zeros(Ntset , length(features)); -trOut = zeros(Ntrset, nM); -vOut = zeros(Nvset , nM); -tOut = zeros(Ntset , nM); - -for e = 1 : nM; - % Training - for r = 1 : trSets - sidx = r + (trSets*(e-1)); - li = 1; - for i = 1 : length(features) - le = li - 1 + length(sigFeatures.trFeatures(r,e).(features{i})); - trSet(sidx,li:le) = sigFeatures.trFeatures(r,e).(features{i}); - li = le + 1; - end - trOut(sidx,e) = 1; - end - % Validation - for r = 1 : vSets - sidx = r + (vSets*(e-1)); - li = 1; - for i = 1 : length(features) - le = li - 1 + length(sigFeatures.vFeatures(r,e).(features{i})); - vSet(sidx,li:le) = sigFeatures.vFeatures(r,e).(features{i}); - li = le + 1; - end - vOut(sidx,e) = 1; - end - % Test - for r = 1 : tSets - sidx = r + (tSets*(e-1)); - li = 1; - for i = 1 : length(features) - le = li - 1 + length(sigFeatures.tFeatures(r,e).(features{i})); - tSet(sidx,li:le) = sigFeatures.tFeatures(r,e).(features{i}); - li = le + 1; - end - tOut(sidx,e) = 1; - end -end - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% % This function will create matrices of training, validation and test sets +% All movements will be STACK one over each other +% +% input: trFeatures contains the splitted data, is a Nsplits x Nexercises structure matrix +% vFeatures is similar to trFeatures +% features contains the name of the charactaristics to be used +% output: trsets are the normalized training sets +% vsets are the normalized validation sets +% trOut contains the correspondet outputs +% vOut contains the correspondet outputs +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-04-26 / Max Ortiz / Creation +% 2009-07-21 / Max Ortiz / To hanlde "treated_data" struct and selection of the number of set +% 2011-06-24 / Max Ortiz / Update to new coding standard and name to stack + +function [trSet, trOut, vSet, vOut, tSet, tOut] = GetSets_Stack(sigFeatures, features) + +nM = length(sigFeatures.trFeatures(1,:)); % Number of movements +trSets = sigFeatures.eTrSets; % effective number of sets for trainning + +if isempty(sigFeatures.vFeatures) + vSets = 0; +else + vSets = sigFeatures.eVSets; % Number of sets for valdiation +end + +if isempty(sigFeatures.tFeatures) + tSets = 0; +else + tSets = sigFeatures.eTSets; % Number of sets for testing +end + +Ntrset = trSets * nM; +Nvset = vSets * nM; +Ntset = tSets * nM; +trSet = zeros(Ntrset, length(features)); +vSet = zeros(Nvset , length(features)); +tSet = zeros(Ntset , length(features)); +trOut = zeros(Ntrset, nM); +vOut = zeros(Nvset , nM); +tOut = zeros(Ntset , nM); + +for e = 1 : nM; + % Training + for r = 1 : trSets + sidx = r + (trSets*(e-1)); + li = 1; + for i = 1 : length(features) + le = li - 1 + length(sigFeatures.trFeatures(r,e).(features{i})); + trSet(sidx,li:le) = sigFeatures.trFeatures(r,e).(features{i}); + li = le + 1; + end + trOut(sidx,e) = 1; + end + % Validation + for r = 1 : vSets + sidx = r + (vSets*(e-1)); + li = 1; + for i = 1 : length(features) + le = li - 1 + length(sigFeatures.vFeatures(r,e).(features{i})); + vSet(sidx,li:le) = sigFeatures.vFeatures(r,e).(features{i}); + li = le + 1; + end + vOut(sidx,e) = 1; + end + % Test + for r = 1 : tSets + sidx = r + (tSets*(e-1)); + li = 1; + for i = 1 : length(features) + le = li - 1 + length(sigFeatures.tFeatures(r,e).(features{i})); + tSet(sidx,li:le) = sigFeatures.tFeatures(r,e).(features{i}); + li = le + 1; + end + tOut(sidx,e) = 1; + end +end + + diff --git a/SigFeatures/GetSets_Stack_IndvMov.m b/SigFeatures/GetSets_Stack_IndvMov.m index 8bfc7a9..2f9f9f4 100644 --- a/SigFeatures/GetSets_Stack_IndvMov.m +++ b/SigFeatures/GetSets_Stack_IndvMov.m @@ -1,161 +1,161 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------- Function Description ------------- -% This function will create matrices of training, validation and test sets -% - All movements will be STACK one over each other, this is, each set of -% movements will be the row and the colums are made of the features. -% - The number of columbs is given by the nuber of channels times the -% features -% - The value of the first features in each channel is then follow for the -% value of the second features in each channel and so on. -% -% NOTE: The main difference with GetSets_Stack is that the mixed movements -% are only considered in the testing set -% -% input: trFeatures contains the splitted data, is a Nsplits x Nexercises structure matrix -% vFeatures is similar to trFeatures -% features contains the name of the charactaristics to be used -% output: trsets are the normalized training sets -% vsets are the normalized validation sets -% trOut contains the correspondet outputs -% vOut contains the correspondet outputs -% -% ------------- Updates ------------- -% 2011-10-03 / Max Ortiz / Created -% 20xx-xx-xx / Author / Comment on update - -function [trSet, trOut, vSet, vOut, tSet, tOut, movIdx, movOutIdx] = GetSets_Stack_IndvMov(sigFeatures, features) - -%Variables -movIdx = []; -movIdxMix = []; - -% Find the mixed movements by looking for "+" -% use of a temporal index to match the output, this assumes that the order -% of the output is the same as the order of the movements -tempIdx = 1; -for i = 1 : size(sigFeatures.mov,1) - if isempty(strfind(sigFeatures.mov{i},'+')) - movIdx = [movIdx i]; - movOutIdx{i} = tempIdx; % Index for the output of each movement - tempIdx = tempIdx + 1; - else - movIdxMix = [movIdxMix i]; - end -end - -nMi = size(movIdx,2); % Number of movements individuals -nMm = size(movIdxMix,2); % Number of movements mixed - -trSets = sigFeatures.eTrSets; % effective number of sets for trainning - -if isempty(sigFeatures.vFeatures) - vSets = 0; -else - vSets = sigFeatures.eVSets; % Number of sets for valdiation -end - -if isempty(sigFeatures.tFeatures) - tSets = 0; -else - tSets = sigFeatures.eTSets; % Number of sets for testing -end - -Ntrset = trSets * nMi; -Nvset = vSets * nMi; -Ntset = tSets * nMi; - -trSet = zeros(Ntrset, length(features)); -vSet = zeros(Nvset , length(features)); -tSet = zeros(Ntset , length(features)); - -trOut = zeros(Ntrset, nMi); -vOut = zeros(Nvset , nMi); -tOut = zeros(Ntset , nMi); - -% Stack data sets for individual movements - -for j = 1 : nMi; - e = movIdx(j); - % Training - for r = 1 : trSets - sidx = r + (trSets*(j-1)); - li = 1; - for i = 1 : length(features) - le = li - 1 + length(sigFeatures.trFeatures(r,e).(features{i})); - trSet(sidx,li:le) = sigFeatures.trFeatures(r,e).(features{i}); % Get each feature per channel - li = le + 1; - end - trOut(sidx,j) = 1; - end - % Validation - for r = 1 : vSets - sidx = r + (vSets*(j-1)); - li = 1; - for i = 1 : length(features) - le = li - 1 + length(sigFeatures.vFeatures(r,e).(features{i})); - vSet(sidx,li:le) = sigFeatures.vFeatures(r,e).(features{i}); - li = le + 1; - end - vOut(sidx,j) = 1; - end - % Test - for r = 1 : tSets - sidx = r + (tSets*(e-1)); % Use e instead of j for test - li = 1; - for i = 1 : length(features) - le = li - 1 + length(sigFeatures.tFeatures(r,e).(features{i})); - tSet(sidx,li:le) = sigFeatures.tFeatures(r,e).(features{i}); - li = le + 1; - end - tOut(sidx,j) = 1; - end -end - -% Extract information for mixed patterns -for j = 1 : nMm; - e = movIdxMix(j); % index of the movement - %find mixed movements - for i = 1 : nMi - ii = movIdx(i); - if isempty(strfind(sigFeatures.mov{e},sigFeatures.mov{ii})) - idxMix(i) = 0; - else - idxMix(i) = 1; - end - end - - % Test - for r = 1 : tSets - sidx = r + (tSets*(e-1)); - li = 1; - for i = 1 : length(features) - le = li - 1 + length(sigFeatures.tFeatures(r,e).(features{i})); - tSet(sidx,li:le) = sigFeatures.tFeatures(r,e).(features{i}); - li = le + 1; - end - tOut(sidx,:) = idxMix; - end - - movOutIdx{e} = find(idxMix); - -end - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------- Function Description ------------- +% This function will create matrices of training, validation and test sets +% - All movements will be STACK one over each other, this is, each set of +% movements will be the row and the colums are made of the features. +% - The number of columbs is given by the nuber of channels times the +% features +% - The value of the first features in each channel is then follow for the +% value of the second features in each channel and so on. +% +% NOTE: The main difference with GetSets_Stack is that the mixed movements +% are only considered in the testing set +% +% input: trFeatures contains the splitted data, is a Nsplits x Nexercises structure matrix +% vFeatures is similar to trFeatures +% features contains the name of the charactaristics to be used +% output: trsets are the normalized training sets +% vsets are the normalized validation sets +% trOut contains the correspondet outputs +% vOut contains the correspondet outputs +% +% ------------- Updates ------------- +% 2011-10-03 / Max Ortiz / Created +% 20xx-xx-xx / Author / Comment on update + +function [trSet, trOut, vSet, vOut, tSet, tOut, movIdx, movOutIdx] = GetSets_Stack_IndvMov(sigFeatures, features) + +%Variables +movIdx = []; +movIdxMix = []; + +% Find the mixed movements by looking for "+" +% use of a temporal index to match the output, this assumes that the order +% of the output is the same as the order of the movements +tempIdx = 1; +for i = 1 : size(sigFeatures.mov,1) + if isempty(strfind(sigFeatures.mov{i},'+')) + movIdx = [movIdx i]; + movOutIdx{i} = tempIdx; % Index for the output of each movement + tempIdx = tempIdx + 1; + else + movIdxMix = [movIdxMix i]; + end +end + +nMi = size(movIdx,2); % Number of movements individuals +nMm = size(movIdxMix,2); % Number of movements mixed + +trSets = sigFeatures.eTrSets; % effective number of sets for trainning + +if isempty(sigFeatures.vFeatures) + vSets = 0; +else + vSets = sigFeatures.eVSets; % Number of sets for valdiation +end + +if isempty(sigFeatures.tFeatures) + tSets = 0; +else + tSets = sigFeatures.eTSets; % Number of sets for testing +end + +Ntrset = trSets * nMi; +Nvset = vSets * nMi; +Ntset = tSets * nMi; + +trSet = zeros(Ntrset, length(features)); +vSet = zeros(Nvset , length(features)); +tSet = zeros(Ntset , length(features)); + +trOut = zeros(Ntrset, nMi); +vOut = zeros(Nvset , nMi); +tOut = zeros(Ntset , nMi); + +% Stack data sets for individual movements + +for j = 1 : nMi; + e = movIdx(j); + % Training + for r = 1 : trSets + sidx = r + (trSets*(j-1)); + li = 1; + for i = 1 : length(features) + le = li - 1 + length(sigFeatures.trFeatures(r,e).(features{i})); + trSet(sidx,li:le) = sigFeatures.trFeatures(r,e).(features{i}); % Get each feature per channel + li = le + 1; + end + trOut(sidx,j) = 1; + end + % Validation + for r = 1 : vSets + sidx = r + (vSets*(j-1)); + li = 1; + for i = 1 : length(features) + le = li - 1 + length(sigFeatures.vFeatures(r,e).(features{i})); + vSet(sidx,li:le) = sigFeatures.vFeatures(r,e).(features{i}); + li = le + 1; + end + vOut(sidx,j) = 1; + end + % Test + for r = 1 : tSets + sidx = r + (tSets*(e-1)); % Use e instead of j for test + li = 1; + for i = 1 : length(features) + le = li - 1 + length(sigFeatures.tFeatures(r,e).(features{i})); + tSet(sidx,li:le) = sigFeatures.tFeatures(r,e).(features{i}); + li = le + 1; + end + tOut(sidx,j) = 1; + end +end + +% Extract information for mixed patterns +for j = 1 : nMm; + e = movIdxMix(j); % index of the movement + %find mixed movements + for i = 1 : nMi + ii = movIdx(i); + if isempty(strfind(sigFeatures.mov{e},sigFeatures.mov{ii})) + idxMix(i) = 0; + else + idxMix(i) = 1; + end + end + + % Test + for r = 1 : tSets + sidx = r + (tSets*(e-1)); + li = 1; + for i = 1 : length(features) + le = li - 1 + length(sigFeatures.tFeatures(r,e).(features{i})); + tSet(sidx,li:le) = sigFeatures.tFeatures(r,e).(features{i}); + li = le + 1; + end + tOut(sidx,:) = idxMix; + end + + movOutIdx{e} = find(idxMix); + +end + + + + diff --git a/SigFeatures/GetSets_Stack_MixedOut.m b/SigFeatures/GetSets_Stack_MixedOut.m index 4ce8bc2..3e8189c 100644 --- a/SigFeatures/GetSets_Stack_MixedOut.m +++ b/SigFeatures/GetSets_Stack_MixedOut.m @@ -1,180 +1,180 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------- Function Description ------------- -% This function creates matrices of training, validation and test sets, -% however, the number of outputs is reduced to the number of single -% movements. The mixed movementes are part of the training, validation and -% testing sets. -% -% - All movements will be STACK one over each other, this is, each set of -% movements (vector of features) will be a row. -% - The number of columbs is given by the nuber of channels times the features -% - The features in the first channel are follow by the features of the -% second channel and so on. -% -% ------------- Updates ------------- -% 2012-04-06 / Max Ortiz / Created -% 20xx-xx-xx / Author / Comment on update - -function [trSet, trOut, vSet, vOut, tSet, tOut, movIdx, movOutIdx] = GetSets_Stack_MixedOut(sigFeatures, features) - -%Variables -movIdx = []; % Index of individual movements -movIdxMix = []; % Index of mixed movements -nFeatures = size(features,1); - -% Find the mixed movements by looking for "+" -% use of a temporal index to match the output, this assumes that the order -% of the output is the same as the order of the movements -tempIdx = 1; -for i = 1 : size(sigFeatures.mov,1) - if isempty(strfind(sigFeatures.mov{i},'+')) - movIdx = [movIdx i]; - movOutIdx{i} = tempIdx; % Index for the output of each movement - tempIdx = tempIdx + 1; - else - movIdxMix = [movIdxMix i]; - end -end - -nMi = size(movIdx,2); % Number of movements individuals -nMm = size(movIdxMix,2); % Number of movements mixed - -trSets = sigFeatures.eTrSets; % effective number of sets for trainning - -if isempty(sigFeatures.vFeatures) - vSets = 0; -else - vSets = sigFeatures.eVSets; % Number of sets for valdiation -end - -if isempty(sigFeatures.tFeatures) - tSets = 0; -else - tSets = sigFeatures.eTSets; % Number of sets for testing -end - -Ntrset = trSets * nMi; -Nvset = vSets * nMi; -Ntset = tSets * nMi; - -trSet = zeros(Ntrset, nFeatures); -vSet = zeros(Nvset , nFeatures); -tSet = zeros(Ntset , nFeatures); - -trOut = zeros(Ntrset, nMi); -vOut = zeros(Nvset , nMi); -tOut = zeros(Ntset , nMi); - -% Stack data sets for individual movements - -for j = 1 : nMi; - e = movIdx(j); - % Training - for r = 1 : trSets - %sidx = r + (trSets*(j-1)); - sidx = r + (trSets*(e-1)); % Use e instead of j for test - li = 1; - for i = 1 : nFeatures - le = li - 1 + length(sigFeatures.trFeatures(r,e).(features{i})); - trSet(sidx,li:le) = sigFeatures.trFeatures(r,e).(features{i}); % Get each feature per channel - li = le + 1; - end - trOut(sidx,j) = 1; - end - % Validation - for r = 1 : vSets - %sidx = r + (vSets*(j-1)); - sidx = r + (vSets*(e-1)); % Use e instead of j for test - li = 1; - for i = 1 : nFeatures - le = li - 1 + length(sigFeatures.vFeatures(r,e).(features{i})); - vSet(sidx,li:le) = sigFeatures.vFeatures(r,e).(features{i}); - li = le + 1; - end - vOut(sidx,j) = 1; - end - % Test - for r = 1 : tSets - sidx = r + (tSets*(e-1)); % Use e instead of j for test - li = 1; - for i = 1 : nFeatures - le = li - 1 + length(sigFeatures.tFeatures(r,e).(features{i})); - tSet(sidx,li:le) = sigFeatures.tFeatures(r,e).(features{i}); - li = le + 1; - end - tOut(sidx,j) = 1; - end -end - -% Extract information for mixed patterns -for j = 1 : nMm; - e = movIdxMix(j); % index of the movement - %find mixed movements - for i = 1 : nMi - ii = movIdx(i); - if isempty(strfind(sigFeatures.mov{e},sigFeatures.mov{ii})) - idxMix(i) = 0; - else - idxMix(i) = 1; - end - end - - % Training - for r = 1 : trSets - sidx = r + (trSets*(e-1)); - li = 1; - for i = 1 : nFeatures - le = li - 1 + length(sigFeatures.trFeatures(r,e).(features{i})); - trSet(sidx,li:le) = sigFeatures.trFeatures(r,e).(features{i}); - li = le + 1; - end - trOut(sidx,:) = idxMix; - end - - % Validation - for r = 1 : vSets - sidx = r + (vSets*(e-1)); - li = 1; - for i = 1 : nFeatures - le = li - 1 + length(sigFeatures.vFeatures(r,e).(features{i})); - vSet(sidx,li:le) = sigFeatures.vFeatures(r,e).(features{i}); - li = le + 1; - end - vOut(sidx,:) = idxMix; - end - - % Test - for r = 1 : tSets - sidx = r + (tSets*(e-1)); - li = 1; - for i = 1 : nFeatures - le = li - 1 + length(sigFeatures.tFeatures(r,e).(features{i})); - tSet(sidx,li:le) = sigFeatures.tFeatures(r,e).(features{i}); - li = le + 1; - end - tOut(sidx,:) = idxMix; - end - - movOutIdx{e} = find(idxMix); - -end - - - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------- Function Description ------------- +% This function creates matrices of training, validation and test sets, +% however, the number of outputs is reduced to the number of single +% movements. The mixed movementes are part of the training, validation and +% testing sets. +% +% - All movements will be STACK one over each other, this is, each set of +% movements (vector of features) will be a row. +% - The number of columbs is given by the nuber of channels times the features +% - The features in the first channel are follow by the features of the +% second channel and so on. +% +% ------------- Updates ------------- +% 2012-04-06 / Max Ortiz / Created +% 20xx-xx-xx / Author / Comment on update + +function [trSet, trOut, vSet, vOut, tSet, tOut, movIdx, movOutIdx] = GetSets_Stack_MixedOut(sigFeatures, features) + +%Variables +movIdx = []; % Index of individual movements +movIdxMix = []; % Index of mixed movements +nFeatures = size(features,1); + +% Find the mixed movements by looking for "+" +% use of a temporal index to match the output, this assumes that the order +% of the output is the same as the order of the movements +tempIdx = 1; +for i = 1 : size(sigFeatures.mov,1) + if isempty(strfind(sigFeatures.mov{i},'+')) + movIdx = [movIdx i]; + movOutIdx{i} = tempIdx; % Index for the output of each movement + tempIdx = tempIdx + 1; + else + movIdxMix = [movIdxMix i]; + end +end + +nMi = size(movIdx,2); % Number of movements individuals +nMm = size(movIdxMix,2); % Number of movements mixed + +trSets = sigFeatures.eTrSets; % effective number of sets for trainning + +if isempty(sigFeatures.vFeatures) + vSets = 0; +else + vSets = sigFeatures.eVSets; % Number of sets for valdiation +end + +if isempty(sigFeatures.tFeatures) + tSets = 0; +else + tSets = sigFeatures.eTSets; % Number of sets for testing +end + +Ntrset = trSets * nMi; +Nvset = vSets * nMi; +Ntset = tSets * nMi; + +trSet = zeros(Ntrset, nFeatures); +vSet = zeros(Nvset , nFeatures); +tSet = zeros(Ntset , nFeatures); + +trOut = zeros(Ntrset, nMi); +vOut = zeros(Nvset , nMi); +tOut = zeros(Ntset , nMi); + +% Stack data sets for individual movements + +for j = 1 : nMi; + e = movIdx(j); + % Training + for r = 1 : trSets + %sidx = r + (trSets*(j-1)); + sidx = r + (trSets*(e-1)); % Use e instead of j for test + li = 1; + for i = 1 : nFeatures + le = li - 1 + length(sigFeatures.trFeatures(r,e).(features{i})); + trSet(sidx,li:le) = sigFeatures.trFeatures(r,e).(features{i}); % Get each feature per channel + li = le + 1; + end + trOut(sidx,j) = 1; + end + % Validation + for r = 1 : vSets + %sidx = r + (vSets*(j-1)); + sidx = r + (vSets*(e-1)); % Use e instead of j for test + li = 1; + for i = 1 : nFeatures + le = li - 1 + length(sigFeatures.vFeatures(r,e).(features{i})); + vSet(sidx,li:le) = sigFeatures.vFeatures(r,e).(features{i}); + li = le + 1; + end + vOut(sidx,j) = 1; + end + % Test + for r = 1 : tSets + sidx = r + (tSets*(e-1)); % Use e instead of j for test + li = 1; + for i = 1 : nFeatures + le = li - 1 + length(sigFeatures.tFeatures(r,e).(features{i})); + tSet(sidx,li:le) = sigFeatures.tFeatures(r,e).(features{i}); + li = le + 1; + end + tOut(sidx,j) = 1; + end +end + +% Extract information for mixed patterns +for j = 1 : nMm; + e = movIdxMix(j); % index of the movement + %find mixed movements + for i = 1 : nMi + ii = movIdx(i); + if isempty(strfind(sigFeatures.mov{e},sigFeatures.mov{ii})) + idxMix(i) = 0; + else + idxMix(i) = 1; + end + end + + % Training + for r = 1 : trSets + sidx = r + (trSets*(e-1)); + li = 1; + for i = 1 : nFeatures + le = li - 1 + length(sigFeatures.trFeatures(r,e).(features{i})); + trSet(sidx,li:le) = sigFeatures.trFeatures(r,e).(features{i}); + li = le + 1; + end + trOut(sidx,:) = idxMix; + end + + % Validation + for r = 1 : vSets + sidx = r + (vSets*(e-1)); + li = 1; + for i = 1 : nFeatures + le = li - 1 + length(sigFeatures.vFeatures(r,e).(features{i})); + vSet(sidx,li:le) = sigFeatures.vFeatures(r,e).(features{i}); + li = le + 1; + end + vOut(sidx,:) = idxMix; + end + + % Test + for r = 1 : tSets + sidx = r + (tSets*(e-1)); + li = 1; + for i = 1 : nFeatures + le = li - 1 + length(sigFeatures.tFeatures(r,e).(features{i})); + tSet(sidx,li:le) = sigFeatures.tFeatures(r,e).(features{i}); + li = le + 1; + end + tOut(sidx,:) = idxMix; + end + + movOutIdx{e} = find(idxMix); + +end + + + + diff --git a/SigFeatures/GetSigFeatures.m b/SigFeatures/GetSigFeatures.m index 058a455..cfd6459 100644 --- a/SigFeatures/GetSigFeatures.m +++ b/SigFeatures/GetSigFeatures.m @@ -1,493 +1,493 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to Breakdown the time and frecuency features -% Input : data Matrix of MxN where the rows are the channels -% : sF Sampling Frequency -% : fID features ID, if not fID given, it compute the hard-coded -% ones -% Output: features Signal features is an struct -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-07-27 / Max Ortiz / Creation from the old analyze_signal fuction in -% EMG_AQ in 2009 -% 2012-07-19 / Max Ortyiz / Add LoadFeaturesID funtion instead of -% hard-coded IDs -% 2012-11-23 / Max Ortyiz / Vectorized implementation of tzc and tslpch2 -% for speed improvement -% -% 20xx-xx-xx / Author / Comment on update - -function xFeatures = GetSigFeatures(data,sF,fID) - - % If not features ID were receved, then compute the following ones: - if ~exist('fID','var') - fID = LoadFeaturesIDs; - end - - % General information required to calculate different signal features - % other processing is added by specific futures in their functions - procFeatures.ch = length(data(1,:)); - procFeatures.sp = length(data(:,1)); - procFeatures.sF = sF; - procFeatures.data = data; - procFeatures.absdata = abs(data); - procFeatures.f = {}; - - % Add data of the fast fourier transform if a frequency feature is - % required - % This verification needs to be optimized - for i = 1 : size(fID,1) - temp = fID{i}; - if temp(1) == 'f' - procFeatures = GetFFT(procFeatures); - break; - end - end - - % Calculate signal features - for i = 1 : size(fID,1) - fName = ['GetSigFeatures_' fID{i}]; - procFeatures = feval(fName, procFeatures); - end - - xFeatures = procFeatures.f; - -end - -% ----------------------------------------------- -function pF = GetSigFeatures_tmn(pF) -% 2011-07-27 Max Ortiz / Creation - pF.f.tmn = mean(pF.data); -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tmabs(pF) -% 2011-07-27 Max Ortiz / Creation - pF.f.tmabs = mean(pF.absdata); -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tmd(pF) -% 2011-07-27 Max Ortiz / Creation - pF.f.tmd = median(pF.data); -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tstd(pF) -% 2011-07-27 Max Ortiz / Creation - pF.f.tstd = std(pF.data); -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tvar(pF) -% 2011-07-27 Max Ortiz / Creation - pF.f.tvar = var(pF.data); -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_twl(pF) -% Waveform Length (acumulative changes in the length) -% 2011-07-27 Max Ortiz / Creation - mdata = [zeros(1,pF.ch) ; pF.data(1:pF.sp-1,:)]; - pF.f.twl = sum(abs(pF.data - mdata)); -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_trms(pF) -% 2011-07-27 Max Ortiz / Creation - pF.f.trms = sqrt(sum(pF.absdata .^ 2)/pF.sp); -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tzc(pF) -% 2011-07-27 Max Ortiz / Creation - %check if tmabs is available - if ~isfield(pF.f,'tmabs') - pF = GetSigFeatures_tmabs(pF); - end - - tmp = repmat(pF.f.tmabs,[size(pF.data,1),1] ); - zc = ( pF.data >= tmp ) - (pF.data < tmp ); - pF.f.tzc = sum( ( zc(1:pF.sp-1,:) - zc(2:pF.sp,:) ) ~= 0); - -% % Zero Crossing / using the abs mean as threshold -% for i = 1 : pF.ch -% zc = (pF.data(:,i) >= pF.f.tmabs(i)) - (pF.data(:,i) < pF.f.tmabs(i)); -% pF.f.tzc(i) = sum((zc(1:pF.sp-1) - zc(2:pF.sp)) ~= 0); -% end - -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tpks(pF) -% 2011-07-27 Max Ortiz / Creation - % Peaks using rms value - - if ~isfield(pF.f,'trms') - pF = GetSigFeatures_trms(pF); - end - if ~isfield(pF,'pks') - for i = 1 : pF.ch - [pks, locs] = findpeaks(pF.absdata(:,i),'minpeakheight',pF.f.trms(i)); - pF.pks{i} = pks; - pF.locs{i} = locs; - end - end - - for i = 1 : pF.ch - pF.f.tpks(i) = size(pF.pks{i},2); - end -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tmpks(pF) -% 2011-07-27 Max Ortiz / Creation - % Mean of peaks using rms value - - if ~isfield(pF.f,'trms') - pF = GetSigFeatures_trms(pF); - end - if ~isfield(pF,'pks') - for i = 1 : pF.ch - [pks, locs] = findpeaks(pF.absdata(:,i),'minpeakheight',pF.f.trms(i)); - pF.pks{i} = pks; - pF.locs{i} = locs; - end - end - - for i = 1 : pF.ch - pF.f.tmpks(i) = mean(pF.pks{i}); - end -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tmvel(pF) -% 2011-07-27 Max Ortiz / Creation - % Mean firing velocity using the peaks - - if ~isfield(pF.f,'trms') - pF = GetSigFeatures_trms(pF); - end - if ~isfield(pF,'pks') - for i = 1 : pF.ch - [pks, locs] = findpeaks(pF.absdata(:,i),'minpeakheight',pF.f.trms(i)); - pF.pks{i} = pks; - pF.locs{i} = locs; - end - end - if ~isfield(pF,'pksData') - for i = 1 : pF.ch - pF.pksData{i} = pF.data(pF.locs{1}, i); % Only data from the peaks - pF.diffPksData{i} = diff(pF.pksData{i}); % Get the diff of the peaks or velocity of the peaks - end - end - - for i = 1 : pF.ch - if isempty(pF.diffPksData{i}) - pF.f.tmvel(i) = 0; - else - pF.f.tmvel(i) = mean(abs(pF.diffPksData{i})); % Get mean of the firing velocity - end - end -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tslpch1(pF) -% 2011-07-27 Max Ortiz / Creation - % Slope Changes using the rms pks - % needs to be reviewed - - if ~isfield(pF.f,'trms') - pF = GetSigFeatures_trms(pF); - end - if ~isfield(pF.f,'tmvel') - pF = GetSigFeatures_tmvel(pF); - end - if ~isfield(pF,'pks') - for i = 1 : pF.ch - [pks, locs] = findpeaks(pF.absdata(:,i),'minpeakheight',pF.f.trms(i)); - pF.pks{i} = pks; - pF.locs{i} = locs; - end - end - if ~isfield(pF,'pksData') - for i = 1 : pF.ch - pF.pksData{i} = pF.data(pF.locs{1}, i); % Only data from the peaks - pF.diffPksData{i} = diff(pF.pksData{i}); % Get the diff of the peaks or velocity of the peaks - end - end - - for i = 1 : pF.ch - if isempty(pF.diffPksData{i}) - pF.f.tslpch1(i) = 0; - else - tmax = max(pF.diffPksData{i}); - if tmax > pF.f.tmvel(i) - pF.f.tslpch1(i) = length(findpeaks(pF.diffPksData{i},'minpeakheight',pF.f.tmvel(i))); - else - pF.f.tslpch1(i) = 0; - end - end - end -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tslpch2(pF) -% 2011-07-27 Max Ortiz / Creation - % Slope Changes using the diff of the raw signal and the computing - % zero crossing - - if ~isfield(pF,'diffData') - pF.diffData = diff(pF.data); % Get the diff - end - - zc = (pF.diffData > 0 ) - (pF.diffData < 0); - pF.f.tslpch2 = sum( abs( zc(1:end-1,:) - zc(2:end,:) ) > 1 ); - -% for i = 1 : pF.ch -% zc = (pF.diffData(:,i) > 0) - (pF.diffData(:,i) < 0); -% pF.f.tslpch2(i) = sum(abs(zc(1:end-1) - zc(2:end)) > 1); -% end - -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tpwr(pF) -% 2011-07-27 Max Ortiz / Creation - pF.f.tpwr = sum(pF.absdata.^2)/pF.sp; -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tcr(pF) -% 2011-07-27 Max Ortiz / Creation - % Correlation - % Close to 1 means mutual increment - % Close to -1 means mutual decrement - % Close to 0 is no correlation or no-linear correlation - mcr = corrcoef(pF.data); - k=1; - for i = 1: pF.ch - for j = i+1 : pF.ch - pF.f.tcr(k) = mcr(i,j); - k=k+1; - end - end -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tcv(pF) -% 2011-07-27 Max Ortiz / Creation - % Covariance - % Note: It is possible that the covariance is not required because corr is - % computer already - mcr = cov(pF.data); - k=1; - for i = 1: pF.ch - for j = i+1 : pF.ch - pF.f.tcv(k) = mcr(i,j); - k=k+1; - end - end -end - -% ######################### Frequency Features ################### - -function pF = GetSigFeatures_fwl(pF) -% 2011-07-27 Max Ortiz / Creation - % Waveform Length (acumulative changes in the length) - tempdataf = [zeros(1,pF.ch) ; pF.fftData(1:end-1,:)]; - pF.f.fwl = sum(abs(pF.fftData - tempdataf)); -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_fmn(pF) -% 2011-07-27 Max Ortiz / Creation - % Mean Frequency - for ch = 1 : pF.ch - fmn = 0; - for i = 1 : length(pF.fftData(:,1)) - if fmn <= pF.fftDataT(ch)/2 - fmn = fmn + pF.fftData(i,ch); - else - break; - end - end - pF.f.fmn(ch) = pF.fV(i-1); - end -end -% ----------------------------------------------- - -function pF = GetSigFeatures_fmd(pF) -% 2011-07-27 Max Ortiz / Creation - % Median Frequency - if ~isfield(pF.f,'fwl') - pF = GetSigFeatures_fwl(pF); - end - tempdataf = [zeros(1,pF.ch) ; pF.fftData(1:end-1,:)]; - for ch = 1 : pF.ch - fmd = 0; - for i = 1 : length(pF.fftData(:,1)) - if fmd <= pF.f.fwl(ch)/2 - fmd = fmd + abs(pF.fftData(i,ch) - tempdataf(i,ch)); - else - break; - end - end - pF.f.fmd(ch) = pF.fV(i-1); - end -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_fpmn(pF) -% 2011-07-27 Max Ortiz / Creation - % Find the highest frequency peaks and gets its mean - if ~isfield(pF,'fPks') - nP = 5; % Number of peaks to be used - for i = 1 : pF.ch - [pks, locs] = findpeaks(pF.fftData(:,i),'SORTSTR','descend'); - pF.fPks(:,i) = pks(1:nP); - pF.fLocs(:,i) = locs(1:nP); - end - end - - for ch = 1 : pF.ch - pF.f.fpmn(ch) = mean(pF.fV(pF.fLocs(:,ch))); - end -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_fpmd(pF) -% 2011-07-27 Max Ortiz / Creation - % Find the highest frequency peaks and gets its mean - if ~isfield(pF,'fPks') - nP = 5; % Number of peaks to be used - for i = 1 : pF.ch - [pks, locs] = findpeaks(pF.fftData(:,i),'SORTSTR','descend'); - pF.fPks(:,i) = pks(1:nP); - pF.fLocs(:,i) = locs(1:nP); - end - end - - for ch = 1 : pF.ch - pF.f.fpmd(ch) = median(pF.fV(pF.fLocs(:,ch))); - end -end -% ----------------------------------------------- - -function pF = GetSigFeatures_fpstd(pF) -% 2011-07-27 Max Ortiz / Creation - % Find the highest frequency peaks and gets its mean - if ~isfield(pF,'fPks') - nP = 5; % Number of peaks to be used - for i = 1 : pF.ch - [pks, locs] = findpeaks(pF.fftData(:,i),'SORTSTR','descend'); - pF.fPks(:,i) = pks(1:nP); - pF.fLocs(:,i) = locs(1:nP); - end - end - - for ch = 1 : pF.ch - pF.f.fpstd(ch) = std(pF.fV(pF.fLocs(:,ch))); - end -end - -% ----------------------------------------------- - -function pF = GetSigFeatures_tdam(pF) -% 2012-05-22 Max Ortiz / Creation, found in FP10 - - temp = zeros(size(pF.data)); - temp(1:end-1,:) = pF.data(2:end,:); % Compute k+1 - diffAmp = abs(temp - pF.data); % compute the absulute difference - pF.f.tdam = sum(diffAmp(1:end-1,:)) ./ (pF.sp-1); - -end - -function pF = GetSigFeatures_tfd(pF) -% 2012-06-06 Max Ortiz / Creation, found in GS97 - - mdata = [zeros(1,pF.ch) ; pF.data(1:pF.sp-1,:)]; - absDiff = abs(pF.data - mdata); - L = sum(absDiff); - n = pF.sp; %Data points or samples - % This is not calculated properly, max of absDiff is only the max - % distance between adjacent points and not all the points in the set - d = max(absDiff); % Max distance between two points. - - pF.f.tfd = log(n) ./ (log(n) + (d./L)); - -end - -function pF = GetSigFeatures_tmfl(pF) -% 2012-06-06 Max Ortiz / Creation, found in AK10 -% Maximum Fractal Length - - N = pF.sp; %Total samples - m = 1; %Initial time - - for k = 1 : 9 : 10; - limTop = floor((N-m)/k); - for i = 1 : limTop - a = m+(i*k); - b = m+((i-1)*k); - tempL(i,:) = abs(pF.data(a,:) - pF.data(b,:)); - end - L(k,:) = sum(tempL) .* ((N-1)/(limTop*k)) ./ k; - clear tempL; - end - - pF.f.tmfl = L(1,:); - - pF.L = L; - -end - -function pF = GetSigFeatures_tfdh(pF) -% 2012-06-06 Max Ortiz / Creation, found in AK10 -% Fractal dimension using Higuchi algorithm - - if ~isfield(pF.f,'tmfl') - pF = GetSigFeatures_tmfl(pF); - end - - dX = log(pF.L(1,:))-log(pF.L(10,:)); - dY = log(10)-log(1); - pF.f.tfdh = dX./dY; - -end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to Breakdown the time and frecuency features +% Input : data Matrix of MxN where the rows are the channels +% : sF Sampling Frequency +% : fID features ID, if not fID given, it compute the hard-coded +% ones +% Output: features Signal features is an struct +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-27 / Max Ortiz / Creation from the old analyze_signal fuction in +% EMG_AQ in 2009 +% 2012-07-19 / Max Ortyiz / Add LoadFeaturesID funtion instead of +% hard-coded IDs +% 2012-11-23 / Max Ortyiz / Vectorized implementation of tzc and tslpch2 +% for speed improvement +% +% 20xx-xx-xx / Author / Comment on update + +function xFeatures = GetSigFeatures(data,sF,fID) + + % If not features ID were receved, then compute the following ones: + if ~exist('fID','var') + fID = LoadFeaturesIDs; + end + + % General information required to calculate different signal features + % other processing is added by specific futures in their functions + procFeatures.ch = length(data(1,:)); + procFeatures.sp = length(data(:,1)); + procFeatures.sF = sF; + procFeatures.data = data; + procFeatures.absdata = abs(data); + procFeatures.f = {}; + + % Add data of the fast fourier transform if a frequency feature is + % required + % This verification needs to be optimized + for i = 1 : size(fID,1) + temp = fID{i}; + if temp(1) == 'f' + procFeatures = GetFFT(procFeatures); + break; + end + end + + % Calculate signal features + for i = 1 : size(fID,1) + fName = ['GetSigFeatures_' fID{i}]; + procFeatures = feval(fName, procFeatures); + end + + xFeatures = procFeatures.f; + +end + +% ----------------------------------------------- +function pF = GetSigFeatures_tmn(pF) +% 2011-07-27 Max Ortiz / Creation + pF.f.tmn = mean(pF.data); +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tmabs(pF) +% 2011-07-27 Max Ortiz / Creation + pF.f.tmabs = mean(pF.absdata); +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tmd(pF) +% 2011-07-27 Max Ortiz / Creation + pF.f.tmd = median(pF.data); +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tstd(pF) +% 2011-07-27 Max Ortiz / Creation + pF.f.tstd = std(pF.data); +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tvar(pF) +% 2011-07-27 Max Ortiz / Creation + pF.f.tvar = var(pF.data); +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_twl(pF) +% Waveform Length (acumulative changes in the length) +% 2011-07-27 Max Ortiz / Creation + mdata = [zeros(1,pF.ch) ; pF.data(1:pF.sp-1,:)]; + pF.f.twl = sum(abs(pF.data - mdata)); +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_trms(pF) +% 2011-07-27 Max Ortiz / Creation + pF.f.trms = sqrt(sum(pF.absdata .^ 2)/pF.sp); +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tzc(pF) +% 2011-07-27 Max Ortiz / Creation + %check if tmabs is available + if ~isfield(pF.f,'tmabs') + pF = GetSigFeatures_tmabs(pF); + end + + tmp = repmat(pF.f.tmabs,[size(pF.data,1),1] ); + zc = ( pF.data >= tmp ) - (pF.data < tmp ); + pF.f.tzc = sum( ( zc(1:pF.sp-1,:) - zc(2:pF.sp,:) ) ~= 0); + +% % Zero Crossing / using the abs mean as threshold +% for i = 1 : pF.ch +% zc = (pF.data(:,i) >= pF.f.tmabs(i)) - (pF.data(:,i) < pF.f.tmabs(i)); +% pF.f.tzc(i) = sum((zc(1:pF.sp-1) - zc(2:pF.sp)) ~= 0); +% end + +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tpks(pF) +% 2011-07-27 Max Ortiz / Creation + % Peaks using rms value + + if ~isfield(pF.f,'trms') + pF = GetSigFeatures_trms(pF); + end + if ~isfield(pF,'pks') + for i = 1 : pF.ch + [pks, locs] = findpeaks(pF.absdata(:,i),'minpeakheight',pF.f.trms(i)); + pF.pks{i} = pks; + pF.locs{i} = locs; + end + end + + for i = 1 : pF.ch + pF.f.tpks(i) = size(pF.pks{i},2); + end +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tmpks(pF) +% 2011-07-27 Max Ortiz / Creation + % Mean of peaks using rms value + + if ~isfield(pF.f,'trms') + pF = GetSigFeatures_trms(pF); + end + if ~isfield(pF,'pks') + for i = 1 : pF.ch + [pks, locs] = findpeaks(pF.absdata(:,i),'minpeakheight',pF.f.trms(i)); + pF.pks{i} = pks; + pF.locs{i} = locs; + end + end + + for i = 1 : pF.ch + pF.f.tmpks(i) = mean(pF.pks{i}); + end +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tmvel(pF) +% 2011-07-27 Max Ortiz / Creation + % Mean firing velocity using the peaks + + if ~isfield(pF.f,'trms') + pF = GetSigFeatures_trms(pF); + end + if ~isfield(pF,'pks') + for i = 1 : pF.ch + [pks, locs] = findpeaks(pF.absdata(:,i),'minpeakheight',pF.f.trms(i)); + pF.pks{i} = pks; + pF.locs{i} = locs; + end + end + if ~isfield(pF,'pksData') + for i = 1 : pF.ch + pF.pksData{i} = pF.data(pF.locs{1}, i); % Only data from the peaks + pF.diffPksData{i} = diff(pF.pksData{i}); % Get the diff of the peaks or velocity of the peaks + end + end + + for i = 1 : pF.ch + if isempty(pF.diffPksData{i}) + pF.f.tmvel(i) = 0; + else + pF.f.tmvel(i) = mean(abs(pF.diffPksData{i})); % Get mean of the firing velocity + end + end +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tslpch1(pF) +% 2011-07-27 Max Ortiz / Creation + % Slope Changes using the rms pks + % needs to be reviewed + + if ~isfield(pF.f,'trms') + pF = GetSigFeatures_trms(pF); + end + if ~isfield(pF.f,'tmvel') + pF = GetSigFeatures_tmvel(pF); + end + if ~isfield(pF,'pks') + for i = 1 : pF.ch + [pks, locs] = findpeaks(pF.absdata(:,i),'minpeakheight',pF.f.trms(i)); + pF.pks{i} = pks; + pF.locs{i} = locs; + end + end + if ~isfield(pF,'pksData') + for i = 1 : pF.ch + pF.pksData{i} = pF.data(pF.locs{1}, i); % Only data from the peaks + pF.diffPksData{i} = diff(pF.pksData{i}); % Get the diff of the peaks or velocity of the peaks + end + end + + for i = 1 : pF.ch + if isempty(pF.diffPksData{i}) + pF.f.tslpch1(i) = 0; + else + tmax = max(pF.diffPksData{i}); + if tmax > pF.f.tmvel(i) + pF.f.tslpch1(i) = length(findpeaks(pF.diffPksData{i},'minpeakheight',pF.f.tmvel(i))); + else + pF.f.tslpch1(i) = 0; + end + end + end +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tslpch2(pF) +% 2011-07-27 Max Ortiz / Creation + % Slope Changes using the diff of the raw signal and the computing + % zero crossing + + if ~isfield(pF,'diffData') + pF.diffData = diff(pF.data); % Get the diff + end + + zc = (pF.diffData > 0 ) - (pF.diffData < 0); + pF.f.tslpch2 = sum( abs( zc(1:end-1,:) - zc(2:end,:) ) > 1 ); + +% for i = 1 : pF.ch +% zc = (pF.diffData(:,i) > 0) - (pF.diffData(:,i) < 0); +% pF.f.tslpch2(i) = sum(abs(zc(1:end-1) - zc(2:end)) > 1); +% end + +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tpwr(pF) +% 2011-07-27 Max Ortiz / Creation + pF.f.tpwr = sum(pF.absdata.^2)/pF.sp; +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tcr(pF) +% 2011-07-27 Max Ortiz / Creation + % Correlation + % Close to 1 means mutual increment + % Close to -1 means mutual decrement + % Close to 0 is no correlation or no-linear correlation + mcr = corrcoef(pF.data); + k=1; + for i = 1: pF.ch + for j = i+1 : pF.ch + pF.f.tcr(k) = mcr(i,j); + k=k+1; + end + end +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tcv(pF) +% 2011-07-27 Max Ortiz / Creation + % Covariance + % Note: It is possible that the covariance is not required because corr is + % computer already + mcr = cov(pF.data); + k=1; + for i = 1: pF.ch + for j = i+1 : pF.ch + pF.f.tcv(k) = mcr(i,j); + k=k+1; + end + end +end + +% ######################### Frequency Features ################### + +function pF = GetSigFeatures_fwl(pF) +% 2011-07-27 Max Ortiz / Creation + % Waveform Length (acumulative changes in the length) + tempdataf = [zeros(1,pF.ch) ; pF.fftData(1:end-1,:)]; + pF.f.fwl = sum(abs(pF.fftData - tempdataf)); +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_fmn(pF) +% 2011-07-27 Max Ortiz / Creation + % Mean Frequency + for ch = 1 : pF.ch + fmn = 0; + for i = 1 : length(pF.fftData(:,1)) + if fmn <= pF.fftDataT(ch)/2 + fmn = fmn + pF.fftData(i,ch); + else + break; + end + end + pF.f.fmn(ch) = pF.fV(i-1); + end +end +% ----------------------------------------------- + +function pF = GetSigFeatures_fmd(pF) +% 2011-07-27 Max Ortiz / Creation + % Median Frequency + if ~isfield(pF.f,'fwl') + pF = GetSigFeatures_fwl(pF); + end + tempdataf = [zeros(1,pF.ch) ; pF.fftData(1:end-1,:)]; + for ch = 1 : pF.ch + fmd = 0; + for i = 1 : length(pF.fftData(:,1)) + if fmd <= pF.f.fwl(ch)/2 + fmd = fmd + abs(pF.fftData(i,ch) - tempdataf(i,ch)); + else + break; + end + end + pF.f.fmd(ch) = pF.fV(i-1); + end +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_fpmn(pF) +% 2011-07-27 Max Ortiz / Creation + % Find the highest frequency peaks and gets its mean + if ~isfield(pF,'fPks') + nP = 5; % Number of peaks to be used + for i = 1 : pF.ch + [pks, locs] = findpeaks(pF.fftData(:,i),'SORTSTR','descend'); + pF.fPks(:,i) = pks(1:nP); + pF.fLocs(:,i) = locs(1:nP); + end + end + + for ch = 1 : pF.ch + pF.f.fpmn(ch) = mean(pF.fV(pF.fLocs(:,ch))); + end +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_fpmd(pF) +% 2011-07-27 Max Ortiz / Creation + % Find the highest frequency peaks and gets its mean + if ~isfield(pF,'fPks') + nP = 5; % Number of peaks to be used + for i = 1 : pF.ch + [pks, locs] = findpeaks(pF.fftData(:,i),'SORTSTR','descend'); + pF.fPks(:,i) = pks(1:nP); + pF.fLocs(:,i) = locs(1:nP); + end + end + + for ch = 1 : pF.ch + pF.f.fpmd(ch) = median(pF.fV(pF.fLocs(:,ch))); + end +end +% ----------------------------------------------- + +function pF = GetSigFeatures_fpstd(pF) +% 2011-07-27 Max Ortiz / Creation + % Find the highest frequency peaks and gets its mean + if ~isfield(pF,'fPks') + nP = 5; % Number of peaks to be used + for i = 1 : pF.ch + [pks, locs] = findpeaks(pF.fftData(:,i),'SORTSTR','descend'); + pF.fPks(:,i) = pks(1:nP); + pF.fLocs(:,i) = locs(1:nP); + end + end + + for ch = 1 : pF.ch + pF.f.fpstd(ch) = std(pF.fV(pF.fLocs(:,ch))); + end +end + +% ----------------------------------------------- + +function pF = GetSigFeatures_tdam(pF) +% 2012-05-22 Max Ortiz / Creation, found in FP10 + + temp = zeros(size(pF.data)); + temp(1:end-1,:) = pF.data(2:end,:); % Compute k+1 + diffAmp = abs(temp - pF.data); % compute the absulute difference + pF.f.tdam = sum(diffAmp(1:end-1,:)) ./ (pF.sp-1); + +end + +function pF = GetSigFeatures_tfd(pF) +% 2012-06-06 Max Ortiz / Creation, found in GS97 + + mdata = [zeros(1,pF.ch) ; pF.data(1:pF.sp-1,:)]; + absDiff = abs(pF.data - mdata); + L = sum(absDiff); + n = pF.sp; %Data points or samples + % This is not calculated properly, max of absDiff is only the max + % distance between adjacent points and not all the points in the set + d = max(absDiff); % Max distance between two points. + + pF.f.tfd = log(n) ./ (log(n) + (d./L)); + +end + +function pF = GetSigFeatures_tmfl(pF) +% 2012-06-06 Max Ortiz / Creation, found in AK10 +% Maximum Fractal Length + + N = pF.sp; %Total samples + m = 1; %Initial time + + for k = 1 : 9 : 10; + limTop = floor((N-m)/k); + for i = 1 : limTop + a = m+(i*k); + b = m+((i-1)*k); + tempL(i,:) = abs(pF.data(a,:) - pF.data(b,:)); + end + L(k,:) = sum(tempL) .* ((N-1)/(limTop*k)) ./ k; + clear tempL; + end + + pF.f.tmfl = L(1,:); + + pF.L = L; + +end + +function pF = GetSigFeatures_tfdh(pF) +% 2012-06-06 Max Ortiz / Creation, found in AK10 +% Fractal dimension using Higuchi algorithm + + if ~isfield(pF.f,'tmfl') + pF = GetSigFeatures_tmfl(pF); + end + + dX = log(pF.L(1,:))-log(pF.L(10,:)); + dY = log(10)-log(1); + pF.f.tfdh = dX./dY; + +end + diff --git a/SigFeatures/GetSigFeatures_tcard.m b/SigFeatures/GetSigFeatures_tcard.m new file mode 100644 index 0000000..6253df0 --- /dev/null +++ b/SigFeatures/GetSigFeatures_tcard.m @@ -0,0 +1,39 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% Compute cardinality +% +% I found out that an wrong implementation (trenx) of the rough entropy was +% given improved results than any other feature. This wrong implementation +% turned out to be the "cardinality" of the set. Max Ortz +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-05-01 / Max Ortiz / Created +% 20xx-xx-xx / Author / Comment on update + + +function pF = GetSigFeatures_tcard(pF) + for ch = 1 : pF.ch + % Get the number of different values and their number of repetitions + v = unique(pF.data(:,ch)); + m = size(v,1); % Number of unique values, or cardinality + pF.f.tcard(ch) = m; + end +end \ No newline at end of file diff --git a/SigFeatures/GetSigFeatures_tren.m b/SigFeatures/GetSigFeatures_tren.m index 0a4ee8b..0d47234 100644 --- a/SigFeatures/GetSigFeatures_tren.m +++ b/SigFeatures/GetSigFeatures_tren.m @@ -1,51 +1,51 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% -% Compute the rough entropy -% This implementation corresponds to Zhong et al. 2011 -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-04-09 / Max Ortiz / Created -% 20xx-xx-xx / Author / Comment on update - -function pF = GetSigFeatures_tren(pF) - % Compute the Rough Entropy per channel - u = pF.sp; % Univers or in this case samples (elements) - for ch = 1 : pF.ch - - % Get the number of different values and their number of repetitions - [v q] = unique(pF.data(:,ch)); - m = size(v,1); % Number of unique values - % Sort q (last apperance) to compute how many apperances each value had - sq = sort(q); - - % Compute the first value - card = zeros(m,1); - card(1) = sq(1); % How many elements are there of the first element - rEn = (card(1)/u) * log2(1/card(1)); - % Compute the rest of the values - for i = 2 : m - % Collect the number of aperances - card(i) = sq(i) - sq(i-1); - rEn = rEn + ((card(i)/u) * log2(1/card(i))); - end - pF.f.tren(ch) = -rEn; - end -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% +% Compute the rough entropy +% This implementation corresponds to Zhong et al. 2011 +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-04-09 / Max Ortiz / Created +% 20xx-xx-xx / Author / Comment on update + +function pF = GetSigFeatures_tren(pF) + % Compute the Rough Entropy per channel + u = pF.sp; % Univers or in this case samples (elements) + for ch = 1 : pF.ch + + % Get the number of different values and their number of repetitions + [v q] = unique(pF.data(:,ch)); + m = size(v,1); % Number of unique values + % Sort q (last apperance) to compute how many apperances each value had + sq = sort(q); + + % Compute the first value + card = zeros(m,1); + card(1) = sq(1); % How many elements are there of the first element + rEn = (card(1)/u) * log2(1/card(1)); + % Compute the rest of the values + for i = 2 : m + % Collect the number of aperances + card(i) = sq(i) - sq(i-1); + rEn = rEn + ((card(i)/u) * log2(1/card(i))); + end + pF.f.tren(ch) = -rEn; + end +end diff --git a/SigFeatures/LoadFeaturesIDs.m b/SigFeatures/LoadFeaturesIDs.m index e5c7f4f..a9b630d 100644 --- a/SigFeatures/LoadFeaturesIDs.m +++ b/SigFeatures/LoadFeaturesIDs.m @@ -1,45 +1,45 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% -% Reads the file features.def and loads the data into motor objects. -% -% --------------------------Updates-------------------------- -% 2012-07-18 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function fID = LoadFeaturesIDs - -fileid = fopen('features.def'); -tline = fgetl(fileid); -i=1; -fID = {}; -while ischar(tline) && ~isempty(tline) - t = textscan(tline,'%s'); - t = t{1}; - fID(i) = t(1); - %disp(t{1}); - tline = fgetl(fileid); - i=i+1; -end - -fID = fID'; % It made a vector to keep compatibility with the rest of BioPatRec - -fclose(fileid); - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% +% Reads the file features.def and loads the data into motor objects. +% +% --------------------------Updates-------------------------- +% 2012-07-18 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function fID = LoadFeaturesIDs + +fileid = fopen('features.def'); +tline = fgetl(fileid); +i=1; +fID = {}; +while ischar(tline) && ~isempty(tline) + t = textscan(tline,'%s'); + t = t{1}; + fID(i) = t(1); + %disp(t{1}); + tline = fgetl(fileid); + i=i+1; +end + +fID = fID'; % It made a vector to keep compatibility with the rest of BioPatRec + +fclose(fileid); + + diff --git a/SigFeatures/features.def b/SigFeatures/features.def index 5d62e9d..4288a6e 100644 --- a/SigFeatures/features.def +++ b/SigFeatures/features.def @@ -1,53 +1,57 @@ -tmabs % Time: Mean absolute value -tstd % Time: Standard deviation -tvar % Time: Variance -twl % Time: wave length -trms % Time: RMS -tzc % Time: Zerp crossing -tslpch2 % Time: Slope changes -tpwr % Time: Power -tdam % Time: Difference absolute mean value -tmfl % Time: Maximum fractal length -tfdh % Time: Fractal dimension Higuchi -tfd % Time: Fractal dimension -tren % Time: Rough Entropy - - -NOTE: Not all features are used constantly so -some of them are left out for speed - -All features: - -Time: - -tmn -tmabs -tmd -tstd -tvar -twl -trms -tzc -tpks -tmpks -tmvel -tslpch1 -tslpch2 -tpwr -tcr -tcv -tdam % Time: Difference absolute mean value -tmfl % Time: Maximum fractal length -tfdh % Time: Fractal dimension Higuchi -tfd % Time: Fractal dimension -tren % Time: Rough Entropy - - -Frequency: - -fwl -fmn -fmd -fpmn -fpmd -fpstd \ No newline at end of file +tmabs % Time: Mean absolute value +tstd % Time: Standard deviation +tvar % Time: Variance +twl % Time: wave length +trms % Time: RMS +tzc % Time: Zerp crossing +tslpch2 % Time: Slope changes +tpwr % Time: Power +tdam % Time: Difference absolute mean value +tmfl % Time: Maximum fractal length +tfdh % Time: Fractal dimension Higuchi +tfd % Time: Fractal dimension +tcard % Time: Cardinality +tren % Time: Rough Entropy +fwl % Freq: wave length +fmn % Freq: mean +fmd % Freq: median + +NOTE: Not all features are used constantly so +some of them are left out to improve speed + +All features: + +Time: + +tmn +tmabs +tmd +tstd +tvar +twl +trms +tzc +tpks +tmpks +tmvel +tslpch1 +tslpch2 +tpwr +tcr +tcv +tdam % Time: Difference absolute mean value +tmfl % Time: Maximum fractal length +tfdh % Time: Fractal dimension Higuchi +tfd % Time: Fractal dimension +tcard % Time: Cardinality +tren % Time: Rough Entropy + + +Frequency: + +fwl % Freq: wave length +fmn % Freq: mean +fmd % Freq: median +fpmn % Freq: peak mean +fpmd % Freq: peak median +fpstd % Freq: peak std \ No newline at end of file diff --git a/SigRecordings/Compatibility_recSession.m b/SigRecordings/Compatibility_recSession.m index d973830..0fe239d 100644 --- a/SigRecordings/Compatibility_recSession.m +++ b/SigRecordings/Compatibility_recSession.m @@ -1,45 +1,45 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to keep the compatibility with recorded sessions in older versions of -% the BioPatRec, so the EMG_AQ -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-07-11 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function recSession = Compatibility_recSession(ss) - - tempF = fieldnames(ss); - if strcmp(tempF(1),'Fs') - - recSession.sF = ss.Fs; - recSession.sT = ss.Ts; - recSession.nM = ss.Ne; - recSession.nR = ss.Nr; - recSession.cT = ss.Tc; - recSession.rT = ss.Tr; - recSession.cTp = ss.Psr; - recSession.date = ss.date; - recSession.mov = ss.msg; - - recSession.tdata = ss.tdata; - recSession.trdata = ss.trdata; - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to keep the compatibility with recorded sessions in older versions of +% the BioPatRec, so the EMG_AQ +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-11 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function recSession = Compatibility_recSession(ss) + + tempF = fieldnames(ss); + if strcmp(tempF(1),'Fs') + + recSession.sF = ss.Fs; + recSession.sT = ss.Ts; + recSession.nM = ss.Ne; + recSession.nR = ss.Nr; + recSession.cT = ss.Tc; + recSession.rT = ss.Tr; + recSession.cTp = ss.Psr; + recSession.date = ss.date; + recSession.mov = ss.msg; + + recSession.tdata = ss.tdata; + recSession.trdata = ss.trdata; + end \ No newline at end of file diff --git a/SigRecordings/DataShow.m b/SigRecordings/DataShow.m index dda23fe..7b2b3dc 100644 --- a/SigRecordings/DataShow.m +++ b/SigRecordings/DataShow.m @@ -1,95 +1,85 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to Show the SAVED data on the GUI -% Input = ai object, chp channels pressences -% Output = data and time -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-04-15 / Max Ortiz / Creation -% 2011-09-20 / Max Ortiz / New routine for BioPatRec based in previous implementation -% for EMG_AQ - -function DataShow(handles,cdata, sF, sT) - - tt = 0:1/sF:sT-1/sF; % Create vector of time - nS = length(cdata(:,1)); % It used to be sF*sT but due to change in lenght witht training data - nCh = size(cdata,2); - - %Fast Fourier Transform - NFFT = 2^nextpow2(nS); % Next power of 2 from number of samples - f = sF/2*linspace(0,1,NFFT/2); - dataf = fft(cdata(1:nS,:),NFFT)/nS; - m = 2*abs(dataf((1:NFFT/2),:)); - m(1,:) =0; - - - if nCh >= 1 - axes(handles.a_t0); - plot(tt(1:length(cdata(:,1))),cdata(:,1)); - axes(handles.a_f0); - plot(f,m(:,1)); - end - - if nCh >= 2 - axes(handles.a_t1); - plot(tt(1:length(cdata(:,1))),cdata(:,2)); - axes(handles.a_f1); - plot(f,m(:,2)); - end - if nCh >= 3 - axes(handles.a_t2); - plot(tt(1:length(cdata(:,1))),cdata(:,3)); - axes(handles.a_f2); - plot(f,m(:,3)); - end - if nCh >= 4 - axes(handles.a_t3); - plot(tt(1:length(cdata(:,1))),cdata(:,4)); - axes(handles.a_f3); - plot(f,m(:,4)); - end - if nCh >= 5 - axes(handles.a_t4); - plot(tt(1:length(cdata(:,1))),cdata(:,5)); -% axes(handles.a_f0); -% plot(f,m(:,1)); - end - - if nCh >= 6 - axes(handles.a_t5); - plot(tt(1:length(cdata(:,1))),cdata(:,6)); -% axes(handles.a_f1); -% plot(f,m(:,2)); - end - if nCh >= 7 - axes(handles.a_t6); - plot(tt(1:length(cdata(:,1))),cdata(:,7)); - % axes(handles.a_f2); - % plot(f,m(:,3)); - end - if nCh >= 8 - axes(handles.a_t7); - plot(tt(1:length(cdata(:,1))),cdata(:,8)); - % axes(handles.a_f3); - % plot(f,m(:,4)); - end - - -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to Show the SAVED data on the GUI +% Input = ai object, chp channels pressences +% Output = data and time +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-04-15 / Max Ortiz / Creation +% 2011-09-20 / Max Ortiz / New routine for BioPatRec based in previous implementation +% for EMG_AQ +% 2013-06-01 / Max Ortiz / Fixed an issue with ploting when cdata is +% slightly bigger that it should be (NI DAQ issues) +% 2015-02-20 / Enzo Mastinu / Added the scaling of the data: now every channel plot will be + % dynamically and automatically resize to fit in the proper portion + % of the main plot. This is to avoid overlapping of channels + % waveforms and to have always the best zoom for every channel. +% 2015-02-23 / Enzo Mastinu / The scale of every channel plot is now the + % same scale of the channel which has the + % maximum absolute value + + +function DataShow(handles,cdata, sF, sT) + + tt = 0:1/sF:sT-1/sF; % Create vector of time + nS = length(cdata(:,1)); % It used to be sF*sT but due to change in lenght witht training data + nCh = size(cdata,2); + + if size(tt,2) ~= size(cdata,1) + nSd = size(cdata,1)- size(tt,2); + tt(1,end:end+nSd) = 0; + end + + % Initialize plots + ampPP = 5; + ymin = -ampPP*2/3; + ymax = ampPP * nCh - ampPP*1/3; + + %Fast Fourier Transform + NFFT = 2^nextpow2(nS); % Next power of 2 from number of samples + f = sF/2*linspace(0,1,NFFT/2); + dataf = fft(cdata(1:nS,:),NFFT)/nS; + m = 2*abs(dataf((1:NFFT/2),:)); + + % Offset and scale the data + offVector = 0:nCh-1; + offVector = offVector .* ampPP; + Kt = ampPP/(2*max(max(abs(cdata)))); + Kf = ampPP/(max(max(abs(m)))); + for j = 1 : nCh + tempData(:,j) = cdata(:,j)*Kt + offVector(j); + fData(:,j) = m(:,j)*Kf + offVector(j); + end + + % plot + axes(handles.a_t0); + plot(tt(1:length(tempData(:,1))),tempData); + set(handles.a_t0,'YTick',offVector); + set(handles.a_t0,'YTickLabel',0:nCh-1); + ylim(handles.a_t0, [ymin ymax]); + axes(handles.a_f0); + plot(f,fData); + set(handles.a_f0,'YTick',offVector); + set(handles.a_f0,'YTickLabel',0:nCh-1); + xlim(handles.a_f0, [0,sF/2]); + ymax = ampPP * nCh; + ylim(handles.a_f0, [ymin ymax]); + +end diff --git a/SigRecordings/FastRecordingSession.m b/SigRecordings/FastRecordingSession.m new file mode 100644 index 0000000..929ce00 --- /dev/null +++ b/SigRecordings/FastRecordingSession.m @@ -0,0 +1,213 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% ------------------- Function Description ------------------ +% Function to Record Exc Sessions +% +% --------------------------Updates-------------------------- +% 2015-01-26 / Enzo Mastinu / A new GUI_Recordings has been developed for the + % BioPatRec_TRE release. Now it is possible to + % plot more then 8 channels at the same moment, for + % time and frequency plots both. It is faster and + % perfectly compatible with the ramp recording + % session. At the end of the recording session it + % is possible to check all channels individually, + % apply offline data process as feature extraction or filter etc. + +% 20xx-xx-xx / Author / Comment + + + +function [cdata, sF, sT] = FastRecordingSession(varargin) + + global handles; + global allData; + global timeStamps; + global samplesCounter; + allData = []; + handles = varargin{1}; + afeSettings = varargin{2}; + + % Get required informations from afeSettings structure + nCh = afeSettings.channels; + sF = afeSettings.sampleRate; + deviceName = afeSettings.name; + ComPortType = afeSettings.ComPortType; + if strcmp(ComPortType, 'COM') + ComPortName = afeSettings.ComPortName; + end + + % Save back acquisition parameters to the handles + handles.nCh = nCh; + handles.sF = sF; + handles.ComPortType = ComPortType; + if strcmp(ComPortType, 'COM') + handles.ComPortName = ComPortName; + end + handles.deviceName = deviceName; + % To avoid bugs in RecordingSession_ShowData function + handles.fast = 1; + handles.rep = 1; + handles.cT = 0; + handles.rT = 0; + handles.rampStatus = 0; + + % Setting for data peeking + sT = handles.sT; + handles.sT = sT; + handles.sTall = sT; + tW = handles.tW; + tWs = tW*sF; % Time window samples + handles.tWs = tWs; + timeStamps = 0:1/sF:tW-1/sF; % Create vector of time + + + %% Initialize GUI.. + + pause on; + + % Initialize plots, offset the data + ampPP = 5; + sData = zeros(tWs,nCh); + fData = zeros(tWs,nCh); + offVector = 0:nCh-1; + offVector = offVector .* ampPP; + for i = 1 : nCh + sData(:,i) = sData(:,i) + offVector(i); + fData(:,i) = fData(:,i) + offVector(i); + end + + % Draw figure + ymin = -ampPP*2/3; + ymax = ampPP * nCh - ampPP*1/3; + p_t0 = plot(handles.a_t0, timeStamps, sData); + handles.p_t0 = p_t0; + xlim(handles.a_t0, [0,tW]); + ylim(handles.a_t0, [ymin ymax]); + set(handles.a_t0,'YTick',offVector); + set(handles.a_t0,'YTickLabel',0:nCh-1); + p_f0 = plot(handles.a_f0,timeStamps,fData); + handles.p_f0 = p_f0; + xlim(handles.a_f0, [0,sF/2]); + ylim(handles.a_f0, [ymin ymax]); + set(handles.a_f0,'YTick',offVector); + set(handles.a_f0,'YTickLabel',0:nCh-1); + + % Initialization of progress bar + xpatch = [0 0 0 0]; + ypatch = [0 0 1 1]; + axes(handles.a_prog); + handles.hPatch = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); + + % Allocation of resource to improve speed, total data + recSessionData = zeros(sF*sT, nCh); + + + %% Starting Session.. + + % Warning to the user + set(handles.t_msg,'String','start'); + drawnow; + + % Run + timeStamps = 0:1/sF:tW-1/sF; % Timestamps used the time vector + currentTv = 1; % Current time vector + tV = timeStamps(currentTv):1/sF:(tW-1/sF)+timeStamps(currentTv); % Time vector used for drawing graphics + currentTv = currentTv - 1 + tWs; % Updated everytime tV is updated + acquireEvent.TimeStamps = tV'; + + %%%%% NI DAQ card %%%%% + if strcmp (ComPortType, 'NI') + + % Init SBI + sCh = 1:nCh; + s = InitSBI_NI(sF,sT,sCh); + s.NotifyWhenDataAvailableExceeds = tWs; % PEEK time + lh = s.addlistener('DataAvailable', @RecordingSession_ShowData); + + % Start DAQ + cData = zeros(sF*sT, nCh); + s.startBackground(); % Run in the backgroud + + startTimerTic = tic; + pause(sT - toc(startTimerTic)); + + % Repetitions other devices + else + + % Connect the chosen device, it returns the connection object + obj = ConnectDevice(handles); + + % Set the selected device and Start the acquisition + SetDeviceStartAcquisition(handles, obj); + + samplesCounter = 1; + cData = zeros(tWs, nCh); + + for timeWindowNr = 1:sT/tW + cData = Acquire_tWs(deviceName, obj, nCh, tWs); % acquire a new time window of samples + acquireEvent.Data = cData; + RecordingSession_ShowData(0, acquireEvent); % plot data and add cData to allData vector + samplesCounter = samplesCounter + tWs; + end + + % Stop acquisition + StopAcquisition(deviceName, obj); + end + + % NI DAQ card: "You must delete the listener once the operation is complete" + if strcmp(ComPortType,'NI'); + if ~s.IsDone % check if is done + s.wait(); + end + delete(lh); + end + + % Save Data + recSessionData = allData; + allData = []; % clean global data for next movement + + + %% Session finish.. + set(handles.t_msg,'String','Session Terminated'); % Show message about acquisition completed + fileName = 'Img/Agree.jpg'; + movI = importdata(fileName); % Import Image + set(handles.a_pic,'Visible','on'); % Turn on visibility + pic = image(movI,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + set(handles.a_prog,'visible','off'); + set(handles.hPatch,'Xdata',[0 0 0 0]); + + % Data Plot + cdata = recSessionData; + DataShow(handles,cdata,sF,sT); + + % Set visible the offline plot and process panels + set(handles.uipanel9,'Visible','on'); + set(handles.uipanel7,'Visible','on'); + set(handles.uipanel8,'Visible','on'); + set(handles.txt_it,'visible','on'); + set(handles.txt_ft,'visible','on'); + set(handles.et_it,'visible','on'); + set(handles.et_ft,'visible','on'); + set(handles.txt_if,'visible','on'); + set(handles.txt_ff,'visible','on'); + set(handles.et_if,'visible','on'); + set(handles.et_ff,'visible','on'); + + chVector = 0:nCh-1; + set(handles.lb_channels, 'String', chVector); +end diff --git a/SigRecordings/GUI_AFEselection.fig b/SigRecordings/GUI_AFEselection.fig index e0c9fb6ad760cb476ebc0d92a61b34c3de176e77..446a96f47ab95c99151eb6d8fc48d1672f8fecb1 100644 GIT binary patch literal 10786 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 diff --git a/SigRecordings/GUI_RecordingSession.m b/SigRecordings/GUI_RecordingSession.m index d0167a6..20fb35c 100644 --- a/SigRecordings/GUI_RecordingSession.m +++ b/SigRecordings/GUI_RecordingSession.m @@ -21,6 +21,7 @@ % ------------------------- Updates & Contributors ------------------------ % [Contributors are welcome to add their email] % 20xx-xx-xx / Max Ortiz / Creation +% 20xx-xx-xx / Pontus Lövinger / Added an alternative for ramp recording % 20xx-xx-xx / Author / Comment on update function varargout = GUI_RecordingSession(varargin) @@ -47,7 +48,7 @@ % Edit the above text to modify the response to help GUI_RecordingSession -% Last Modified by GUIDE v2.5 17-May-2012 09:47:59 +% Last Modified by GUIDE v2.5 19-Sep-2013 09:13:52 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; @@ -96,9 +97,6 @@ function GUI_RecordingSession_OpeningFcn(hObject, eventdata, handles, varargin) %remove the axis tick marks axis off - - - %clear value of msg set(handles.et_msg,'Value',1); @@ -122,7 +120,6 @@ function GUI_RecordingSession_OpeningFcn(hObject, eventdata, handles, varargin) varargout{1} = handles.output; - function et_Fs_Callback(hObject, eventdata, handles) input = str2double(get(hObject,'String')); if (isempty(input)) @@ -360,8 +357,10 @@ function et_msg_Callback(hObject, eventdata, handles) % --- Executes on button press in pb_Record. function pb_Record_Callback(hObject, eventdata, handles) % get the EMG_AQ Handles - h1 = GUI_Recordings; - hGUI_Rec = guidata(h1); + fast = 0; + h1 = GUI_Recordings(fast); + hGUI_Rec = guidata(h1); + rampStatus = get(handles.cb_ramp,'Value'); %psr = str2double(get(handles.et_Psr,'String')); % Percentage of the exercise time to be consider for training %sF = str2double(get(handles.et_Fs,'String')); % Sampling Frequency @@ -421,7 +420,8 @@ function pb_Record_Callback(hObject, eventdata, handles) end end - GUI_AFEselection(nM,nR,cT,rT,mov,hGUI_Rec,vreMovements) + fast = 0; + GUI_AFEselection(nM,nR,cT,rT,mov,hGUI_Rec,vreMovements,rampStatus,fast) %GUI_AFEselection(sF,nM,nR,cT,rT,psr,mov,hGUI_Rec) % Moved to AFE_select @@ -444,3 +444,12 @@ function cb_simultaneous_Callback(hObject, eventdata, handles) % handles structure with handles and user data (see GUIDATA) % Hint: get(hObject,'Value') returns toggle state of cb_simultaneous + + +% --- Executes on button press in cb_ramp. +function cb_ramp_Callback(hObject, eventdata, handles) +% hObject handle to cb_ramp (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hint: get(hObject,'Value') returns toggle state of cb_ramp diff --git a/SigRecordings/GUI_RecordingSessionShow.m b/SigRecordings/GUI_RecordingSessionShow.m index 61c5c49..e533ad2 100644 --- a/SigRecordings/GUI_RecordingSessionShow.m +++ b/SigRecordings/GUI_RecordingSessionShow.m @@ -288,15 +288,26 @@ function pb_load_Callback(hObject, eventdata, handles) nM = get(handles.pm_nM,'Value'); % number of excersices or movements recSession = get(handles.pm_data,'UserData'); sT = recSession.sT; + EMG_AQhandle.nCh = recSession.nCh; + EMG_AQhandle.deviceName = recSession.dev; + nCh = recSession.nCh; + deviceName = recSession.dev; cdata = recSession.tdata(:,:,nM); + if isfield(recSession,'comm') + EMG_AQhandle.ComPortType = recSession.comm; + ComPortType = recSession.comm; + else + EMG_AQhandle.ComPortType = 'NI'; + ComPortType = 'NI'; + end %if get(handles.pm_data,'Value') == 1 % cdata = recSession.tdata(:,:,nM); %else % cdata = recSession.trdata(:,:,nM); %end DataShow(EMG_AQhandle,cdata,sF,sT); - save('cdata.mat','cdata','sF','sT'); - + tempdata = cdata; + save('cdata.mat','cdata','tempdata','sF','sT','nCh','ComPortType','deviceName'); % --- Executes on button press in pb_cancel. diff --git a/SigRecordings/GUI_Recordings.fig b/SigRecordings/GUI_Recordings.fig index f189f1dac99da63e23ab6b34ab94747b894846a5..f520be7a3f13c125c3656265284b3ca9781be26b 100644 GIT binary patch literal 28922 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 CDfE~C literal 64565 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 M|V7ctaduK)l5 diff --git a/SigRecordings/GUI_Recordings.m b/SigRecordings/GUI_Recordings.m index 7ea452f..8426d19 100644 --- a/SigRecordings/GUI_Recordings.m +++ b/SigRecordings/GUI_Recordings.m @@ -1,3 +1,4 @@ + % ---------------------------- Copyright Notice --------------------------- % This file is part of BioPatRec © which is open and free software under % the GNU Lesser General Public License (LGPL). See the file "LICENSE" for @@ -21,8 +22,23 @@ % ------------------------- Updates & Contributors ------------------------ % [Contributors are welcome to add their email] % 20xx-xx-xx / Max Ortiz / Creation +% 20xx-09-19 / Pontus Lövinger / Added plot and text for ramp recording recording +% 2015-01-23 / Pontus Lövinger / New recording session GUI: it has been added the + % possibility to plot more then 8 channels (for both time + % and frequency plots) +% 2015-01-26 / Enzo Mastinu / A new GUI_Recordings has been developed for the + % BioPatRec_TRE release. Now it is possible to + % plot more then 8 channels at the same moment for + % time and frequency plots both. It is faster and + % perfectly compatible with the ramp recording + % session. At the end of the recording session it + % is possible to check all channels individually, + % apply offlinedata process as feature extraction or filter etc. + % 20xx-xx-xx / Author / Comment on update + + function varargout = GUI_Recordings(varargin) % GUI_Recordings M-file for GUI_Recordings.fig % GUI_Recordings, by itself, creates a new GUI_Recordings or raises the existing @@ -47,7 +63,7 @@ % Edit the above text to modify the response to help GUI_Recordings -% Last Modified by GUIDE v2.5 03-Jun-2012 19:04:56 +% Last Modified by GUIDE v2.5 24-Feb-2015 16:47:40 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; gui_State = struct('gui_Name', mfilename, ... @@ -94,6 +110,16 @@ function GUI_Recordings_OpeningFcn(hObject, eventdata, handles, varargin) % Update handles structure guidata(hObject, handles); +fast = varargin{1}; +if(fast) + set(handles.et_tW,'visible','on'); + set(handles.et_sT,'visible','on'); + set(handles.txt_tW,'visible','on'); + set(handles.txt_sT,'visible','on'); + set(handles.pb_Start,'visible','on'); +end + + % UIWAIT makes GUI_Recordings wait for user response (see UIRESUME) % uiwait(handles.figure1); @@ -109,206 +135,21 @@ function GUI_Recordings_OpeningFcn(hObject, eventdata, handles, varargin) varargout{1} = handles.output; -% --- Executes on button press in pb_StartRecording. -function pb_StartRecording_Callback(hObject, eventdata, handles) - - sF = str2double(get(handles.et_Fs,'String')); - sT = str2double(get(handles.et_Ts,'String')); - pT = str2double(get(handles.et_Tp,'String')); - - % Get chAI (String identifying each channel - % the number of channels to record is selected automatically from the graphs - sCh(1) = get(handles.cb_ch0,'Value'); - sCh(2) = get(handles.cb_ch1,'Value'); - sCh(3) = get(handles.cb_ch2,'Value'); - sCh(4) = get(handles.cb_ch3,'Value'); - sCh(5) = get(handles.cb_ch4,'Value'); - sCh(6) = get(handles.cb_ch5,'Value'); - sCh(7) = get(handles.cb_ch6,'Value'); - sCh(8) = get(handles.cb_ch7,'Value'); - - % Legacy routines - %[ai,chp] = Init_NI_AI(handles,sF,sT,8); & DAQ using legacy - %cdata = NI_DataShow(handles,ai,chp,sF,sT,pT); - - cdata = DAQShow_SBI(handles,sCh,sF,sT,pT); - - save('cdata.mat','cdata','sF','sT'); - - -function et_Fs_Callback(hObject, eventdata, handles) -% hObject handle to et_Fs (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -% Hints: get(hObject,'String') returns contents of et_Fs as text -% str2double(get(hObject,'String')) returns contents of et_Fs as a double - -input = str2double(get(hObject,'String')); -if (isempty(input)) - set(hObject,'String','0') -end -guidata(hObject, handles); - - -% --- Executes during object creation, after setting all properties. -function et_Fs_CreateFcn(hObject, eventdata, handles) -% hObject handle to et_Fs (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - - -function et_Ts_Callback(hObject, eventdata, handles) -% hObject handle to et_Ts (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of et_Ts as text -% str2double(get(hObject,'String')) returns contents of et_Ts as a double -input = str2double(get(hObject,'String')); -if (isempty(input)) - set(hObject,'String','0') -end -guidata(hObject, handles); - - -% --- Executes during object creation, after setting all properties. -function et_Ts_CreateFcn(hObject, eventdata, handles) -% hObject handle to et_Ts (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - - -function et_Tp_Callback(hObject, eventdata, handles) -% hObject handle to et_Tp (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hints: get(hObject,'String') returns contents of et_Tp as text -% str2double(get(hObject,'String')) returns contents of et_Tp as a double -input = str2double(get(hObject,'String')); -if (isempty(input)) - set(hObject,'String','0') -end -guidata(hObject, handles); - - -% --- Executes during object creation, after setting all properties. -function et_Tp_CreateFcn(hObject, eventdata, handles) -% hObject handle to et_Tp (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles empty - handles not created until after all CreateFcns called - -% Hint: edit controls usually have a white background on Windows. -% See ISPC and COMPUTER. -if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) - set(hObject,'BackgroundColor','white'); -end - - -% --- Executes on button press in pb_initai. -function pb_initai_Callback(hObject, eventdata, handles) -% hObject handle to pb_initai (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) -% Get user input from GUI - - -% --- Executes on button press in cb_filter50hz. -function cb_filter50hz_Callback(hObject, eventdata, handles) -% hObject handle to cb_filter50hz (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of cb_filter50hz - - -% --- Executes on button press in cb_filterBP. -function cb_filterBP_Callback(hObject, eventdata, handles) -% hObject handle to cb_filterBP (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of cb_filterBP - - -% --- Executes on button press in cb_filter80Hz. -function cb_filter80Hz_Callback(hObject, eventdata, handles) -% hObject handle to cb_filter80Hz (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of cb_filter80Hz - - -% --- Executes on button press in cb_ch0. -function cb_ch0_Callback(hObject, eventdata, handles) -% hObject handle to cb_ch0 (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of cb_ch0 - - -% --- Executes on button press in cb_ch1. -function cb_ch1_Callback(hObject, eventdata, handles) -% hObject handle to cb_ch1 (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of cb_ch1 - - -% --- Executes on button press in cb_ch2. -function cb_ch2_Callback(hObject, eventdata, handles) -% hObject handle to cb_ch2 (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of cb_ch2 - - -% --- Executes on button press in cb_ch3. -function cb_ch3_Callback(hObject, eventdata, handles) -% hObject handle to cb_ch3 (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) - -% Hint: get(hObject,'Value') returns toggle state of cb_ch3 - - - -function et_if0_Callback(hObject, eventdata, handles) +function et_if_Callback(hObject, eventdata, handles) input = str2double(get(hObject,'String')); if (isempty(input)) set(hObject,'String','0') end - xmax = str2double(get(handles.et_ff0,'String')); + xmax = str2double(get(handles.et_ff,'String')); set(handles.a_f0,'XLim',[input xmax]); - set(handles.a_f1,'XLim',[input xmax]); - set(handles.a_f2,'XLim',[input xmax]); - set(handles.a_f3,'XLim',[input xmax]); guidata(hObject, handles); % --- Executes during object creation, after setting all properties. -function et_if0_CreateFcn(hObject, eventdata, handles) -% hObject handle to et_if0 (see GCBO) +function et_if_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_if (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called @@ -319,25 +160,21 @@ function et_if0_CreateFcn(hObject, eventdata, handles) end - -function et_ff0_Callback(hObject, eventdata, handles) +function et_ff_Callback(hObject, eventdata, handles) input = str2double(get(hObject,'String')); if (isempty(input)) set(hObject,'String','0') end - xmin = str2double(get(handles.et_if0,'String')); + xmin = str2double(get(handles.et_if,'String')); set(handles.a_f0,'XLim',[xmin input]); - set(handles.a_f1,'XLim',[xmin input]); - set(handles.a_f2,'XLim',[xmin input]); - set(handles.a_f3,'XLim',[xmin input]); guidata(hObject, handles); % --- Executes during object creation, after setting all properties. -function et_ff0_CreateFcn(hObject, eventdata, handles) -% hObject handle to et_ff0 (see GCBO) +function et_ff_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_ff (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called @@ -348,7 +185,6 @@ function et_ff0_CreateFcn(hObject, eventdata, handles) end - function et_n_Callback(hObject, eventdata, handles) input = str2double(get(hObject,'String')); if (isempty(input)) @@ -390,8 +226,7 @@ function et_fc1_CreateFcn(hObject, eventdata, handles) if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end - - + function et_fc2_Callback(hObject, eventdata, handles) input = str2double(get(hObject,'String')); @@ -422,32 +257,28 @@ function pb_ApplyButter_Callback(hObject, eventdata, handles) cF2 = str2double(get(handles.et_fc2,'String')); % Load matrix load('cdata.mat'); - cdata = ApplyButterFilter(sF, N, cF1, cF2, cdata); - DataShow(handles,cdata,sF,sT); - save('cdata.mat','cdata','sF','sT'); + handles.nCh = size(tempdata,2); + handles.ComPortType = ComPortType; + handles.deviceName = deviceName; + tempdata = ApplyButterFilter(sF, N, cF1, cF2, tempdata); + DataShow(handles,tempdata,sF,sT); + save('cdata.mat','cdata','tempdata','sF','sT','nCh','ComPortType','deviceName'); -function et_it0_Callback(hObject, eventdata, handles) +function et_it_Callback(hObject, eventdata, handles) input = str2double(get(hObject,'String')); if (isempty(input)) set(hObject,'String','0') end - xmax = str2double(get(handles.et_ft0,'String')); + xmax = str2double(get(handles.et_ft,'String')); set(handles.a_t0,'XLim',[input xmax]); - set(handles.a_t1,'XLim',[input xmax]); - set(handles.a_t2,'XLim',[input xmax]); - set(handles.a_t3,'XLim',[input xmax]); - set(handles.a_t4,'XLim',[input xmax]); - set(handles.a_t5,'XLim',[input xmax]); - set(handles.a_t6,'XLim',[input xmax]); - set(handles.a_t7,'XLim',[input xmax]); guidata(hObject, handles); % --- Executes during object creation, after setting all properties. -function et_it0_CreateFcn(hObject, eventdata, handles) -% hObject handle to et_it0 (see GCBO) +function et_it_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_it (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called @@ -458,28 +289,20 @@ function et_it0_CreateFcn(hObject, eventdata, handles) end - -function et_ft0_Callback(hObject, eventdata, handles) +function et_ft_Callback(hObject, eventdata, handles) input = str2double(get(hObject,'String')); if (isempty(input)) set(hObject,'String','0') end - xmin = str2double(get(handles.et_it0,'String')); + xmin = str2double(get(handles.et_it,'String')); set(handles.a_t0,'XLim',[xmin input]); - set(handles.a_t1,'XLim',[xmin input]); - set(handles.a_t2,'XLim',[xmin input]); - set(handles.a_t3,'XLim',[xmin input]); - set(handles.a_t4,'XLim',[xmin input]); - set(handles.a_t5,'XLim',[xmin input]); - set(handles.a_t6,'XLim',[xmin input]); - set(handles.a_t7,'XLim',[xmin input]); guidata(hObject, handles); % --- Executes during object creation, after setting all properties. -function et_ft0_CreateFcn(hObject, eventdata, handles) -% hObject handle to et_ft0 (see GCBO) +function et_ft_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_ft (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called @@ -503,43 +326,143 @@ function m_load_Callback(hObject, eventdata, handles) % -------------------------------------------------------------------- function t_load_ClickedCallback(hObject, eventdata, handles) -% Callback function run when the Open menu item is selected -ss = []; -[file, path] = uigetfile('*.mat'); - if ~isequal(file, 0) - load([path,file]); - if(exist('sF','var')) == 1 % Load current data - DataShow(handles,cdata,sF,sT); - save('cdata.mat','cdata','sF','sT'); - elseif exist('recSession','var') || ... % Load session - exist('ss','var') - df = GUI_RecordingSessionShow(); - dfdata = guidata(df); - if ~isempty(ss) - recSession = Compatibility_recSession(ss); - end - set(dfdata.et_Fs,'String',num2str(recSession.sF)); - set(dfdata.et_Ne,'String',num2str(recSession.nM)); - set(dfdata.et_Nr,'String',num2str(recSession.nR)); - set(dfdata.et_Tc,'String',num2str(recSession.cT)); - set(dfdata.et_Tr,'String',num2str(recSession.rT)); - set(dfdata.et_msg,'String',recSession.mov); - sNe = 1:recSession.nM; - set(dfdata.pm_nM,'String',num2str(sNe')); - set(dfdata.pm_data,'UserData',recSession); % Save Struct in user data - set(dfdata.t_mhandles,'UserData',handles); % Save this GUI handles - if isfield(recSession,'cmt') - set(dfdata.et_cmt,'String',recSession.cmt); - else - set(dfdata.et_cmt,'String','No Comment'); - end - if isfield(recSession,'dev') - set(dfdata.t_dev,'String',recSession.dev); + % Callback function run when the Open menu item is selected + ss = []; + [file, path] = uigetfile({'*.mat';'*.csv'}); + if ~isequal(file, 0) + [pathstr,name,ext] = fileparts(file); + if(strcmp(ext,'.mat')) + load([path,file]); + if(exist('sF','var')) == 1 % Load current data + if(exist('cdata','var')) == 1 % Load current data (fix compatibility issues) + DataShow(handles,cdata,sF,sT); + tempdata = cdata; + save('cdata.mat','cdata','tempdata','sF','sT','nCh','ComPortType','deviceName'); + elseif(exist('cData','var')) == 1 + DataShow(handles,cData,sF,sT); + tempdata = cdata; + save('cdata.mat','cdata','tempdata','sF','sT','nCh','ComPortType','deviceName'); + end + elseif exist('recSession','var') || ... % Load session + exist('ss','var') + df = GUI_RecordingSessionShow(); + dfdata = guidata(df); + if ~isempty(ss) + recSession = Compatibility_recSession(ss); + end + set(dfdata.et_Fs,'String',num2str(recSession.sF)); + set(dfdata.et_Ne,'String',num2str(recSession.nM)); + set(dfdata.et_Nr,'String',num2str(recSession.nR)); + set(dfdata.et_Tc,'String',num2str(recSession.cT)); + set(dfdata.et_Tr,'String',num2str(recSession.rT)); + set(dfdata.et_msg,'String',recSession.mov); + sNe = 1:recSession.nM; + set(dfdata.pm_nM,'String',num2str(sNe')); + set(dfdata.pm_data,'UserData',recSession); % Save Struct in user data + set(dfdata.t_mhandles,'UserData',handles); % Save this GUI handles + if isfield(recSession,'cmt') + set(dfdata.et_cmt,'String',recSession.cmt); + else + set(dfdata.et_cmt,'String','No Comment'); + end + if isfield(recSession,'dev') + set(dfdata.t_dev,'String',recSession.dev); + else + set(dfdata.t_dev,'String','Unknown'); + end + end else - set(dfdata.t_dev,'String','Unknown'); + %CSV / MCARE + fid = fopen(file); + fullDir = strcat(path,name,ext); % We get the path of the selected file + fileDir = dir(fullDir); % We use this to get the size, which is a field of dir + movText = fgetl(fid); % We read the first line + movText = textscan(movText, '%s', 'Delimiter', ',', 'BufSize', fileDir.bytes); %Scans for objects seperated with commas + recSession.mov = movText{1}; %And load them into the recSession + fclose(fid); %We need to close the file, before we can textscan it with other parameters + fid = fopen(file); + C = textscan(fid, '%s', 'Delimiter', '\n', 'BufSize', fileDir.bytes); % Scans for objects seperated by line breaks + recSession.date = C{1}{2}; + recSession.comm = C{1}{3}; + recSession.sF = csvread(file,3,0,[3, 0, 3, 0]); + recSession.nM = csvread(file,3,1,[3, 1, 3, 1]); + recSession.sT = csvread(file, 3,2,[3,2,3,2]); + recSession.cT = csvread(file, 3,3,[3,3,3,3]); + recSession.rT = csvread(file, 3,4,[3,4,3,4]); + recSession.nR = csvread(file, 3,5,[3,5,3,5]); + recSession.nCh = csvread(file, 3,6,[3,6,3,6]); + %Loading raw data + rawData = csvread(file,4,0)'; + %Preallocate memory + recSession.tdata = zeros(recSession.sF*recSession.cT*recSession.nR*2,recSession.nCh,recSession.nM); + %Sorts the data + for movements = 1 : recSession.nM + for channels = 1 : recSession.nCh + % We iterate over the repititions, as data from MCARE is exported channel-wise (as a subset of each repitition) + % The system is like this (for 3 repititions and 4 + % channels) + %iterator = + % 1 5 9 -> First channel + % 2 6 10 -> Second Channel + % 3 7 11 -> Third Channel + % 4 8 12 -> Fourth Channel + % ^ "lines of data" E.g. "1" = first channel from first + % repition. "5" = first channel from second repitition. + % "2" = second channel from first repitition. + iterator = channels+((movements-1)*(recSession.nCh*recSession.nR)); + for repititions = 1 : recSession.nR + recSession.tdata(1+((repititions-1)*(recSession.sF*(recSession.cT+recSession.rT))):repititions*(recSession.sF*(recSession.cT+recSession.rT)),channels,movements) = rawData(:,iterator); + iterator = iterator + recSession.nCh; + end + end + end + + df = GUI_RecordingSessionShow(); + dfdata = guidata(df); + if ~isempty(ss) + recSession = Compatibility_recSession(ss); + end + set(dfdata.et_Fs,'String',num2str(recSession.sF)); + set(dfdata.et_Ne,'String',num2str(recSession.nM)); + set(dfdata.et_Nr,'String',num2str(recSession.nR)); + set(dfdata.et_Tc,'String',num2str(recSession.cT)); + set(dfdata.et_Tr,'String',num2str(recSession.rT)); + set(dfdata.et_msg,'String',recSession.mov); + sNe = 1:recSession.nM; + set(dfdata.pm_nM,'String',num2str(sNe')); + set(dfdata.pm_data,'UserData',recSession); % Save Struct in user data + set(dfdata.t_mhandles,'UserData',handles); % Save this GUI handles + if isfield(recSession,'cmt') + set(dfdata.et_cmt,'String',recSession.cmt); + else + set(dfdata.et_cmt,'String','No Comment'); + end + if isfield(recSession,'dev') + set(dfdata.t_dev,'String',recSession.dev); + else + set(dfdata.t_dev,'String','Unknown'); + end end end + + % Set visible the offline plot and process panels + set(handles.uipanel9,'Visible','on'); + set(handles.uipanel7,'Visible','on'); + set(handles.uipanel8,'Visible','on'); + set(handles.txt_it,'visible','on'); + set(handles.txt_ft,'visible','on'); + set(handles.et_it,'visible','on'); + set(handles.et_ft,'visible','on'); + set(handles.txt_if,'visible','on'); + set(handles.txt_ff,'visible','on'); + set(handles.et_if,'visible','on'); + set(handles.et_ff,'visible','on'); + if exist('recSession','var') + chVector = 0:recSession.nCh-1; + else + chVector = 0:size(cdata,2)-1; end + set(handles.lb_channels, 'String', chVector); % -------------------------------------------------------------------- @@ -557,37 +480,11 @@ function m_record_Callback(hObject, eventdata, handles) % -------------------------------------------------------------------- -function m_Rstandardsession_Callback(hObject, eventdata, handles) -%Function that calls the standard recording session - Fs = 10000; % Sampling Frequency - Ne = 4; % number of excersices or movements - Nr = 10; % number of excersice repetition - Tc = 2; % time that the contractions should last - Tr = 3; % relaxing time - Psr = .5; % Percentage of the escersice time to be consider for training - msg = {'Open Hand'; - 'Close Hand'; - 'Flex Hand'; - 'Extend Hand'}; - %'Pronation '; - %'Supination '}; - - cdata = recording_session(Fs,Ne,Nr,Tc,Tr,Psr,msg,handles); - Ts = (Tc+Tr)*Nr; - save('cdata.mat','cdata','Fs','Ts'); - - -% -------------------------------------------------------------------- -function m_Recordoneshot_Callback(hObject, eventdata, handles) - pb_StartRecording_Callback(hObject, eventdata, handles) - - -% -------------------------------------------------------------------- function m_Rcustomizedsession_Callback(hObject, eventdata, handles) %Call the figure recording_Session_fig and pass this figure handles GUI_RecordingSession; - + % -------------------------------------------------------------------- function m_filters_Callback(hObject, eventdata, handles) % hObject handle to m_filters (see GCBO) @@ -596,168 +493,264 @@ function m_filters_Callback(hObject, eventdata, handles) % -------------------------------------------------------------------- -function m_Fcustomized_Callback(hObject, eventdata, handles) -% hObject handle to m_Fcustomized (see GCBO) -% eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) +function m_Fplh_Callback(hObject, eventdata, handles) + load('cdata.mat'); + handles.nCh = size(tempdata,2); + handles.ComPortType = ComPortType; + handles.deviceName = deviceName; + tempdata = BSbutterPLHarmonics(sF,tempdata); + DataShow(handles,tempdata,sF,sT); + save('cdata.mat','cdata','tempdata','sF','sT','nCh','ComPortType','deviceName'); % -------------------------------------------------------------------- -function m_Fbandstop_Callback(hObject, eventdata, handles) -% hObject handle to m_Fbandstop (see GCBO) +function m_FBSbutter_Callback(hObject, eventdata, handles) +% hObject handle to m_FBSbutter (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) - + % -------------------------------------------------------------------- -function m_Fplh_Callback(hObject, eventdata, handles) +function m_spatialFilterDDF_Callback(hObject, eventdata, handles) +% hObject handle to m_spatialFilterDDF (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) load('cdata.mat'); - cdata = BSbutterPLHarmonics(sF,cdata); - data_show(handles,cdata,sF,sT); - save('cdata.mat','cdata','sF','sT'); + handles.nCh = size(tempdata,2); + handles.ComPortType = ComPortType; + handles.deviceName = deviceName; + tempdata = SpatialFilterDDF(tempdata); + DataShow(handles,tempdata,sF,sT); + save('cdata.mat','cdata','tempdata','sF','sT','nCh','ComPortType','deviceName'); % -------------------------------------------------------------------- -function m_FBSbutter_Callback(hObject, eventdata, handles) -% hObject handle to m_FBSbutter (see GCBO) +function m_spatialFilterSDF_Callback(hObject, eventdata, handles) +% hObject handle to m_spatialFilterSDF (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) + load('cdata.mat'); + handles.nCh = size(tempdata,2); + handles.ComPortType = ComPortType; + handles.deviceName = deviceName; + tempdata = SpatialFilterSDF(tempdata); + DataShow(handles,tempdata,sF,sT); + save('cdata.mat','cdata','tempdata','sF','sT','nCh','ComPortType','deviceName'); % -------------------------------------------------------------------- -function m_Pattern_Recognition_Callback(hObject, eventdata, handles) - pattern_recognition_fig(); - -% -------------------------------------------------------------------- -function m_PR_train_ANN_Callback(hObject, eventdata, handles) -% hObject handle to m_PR_train_ANN (see GCBO) +function m_spatialFilterDDFAbs_Callback(hObject, eventdata, handles) +% hObject handle to m_spatialFilterDDFAbs (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) + load('cdata.mat'); + handles.nCh = size(tempdata,2); + handles.ComPortType = ComPortType; + handles.deviceName = deviceName; + tempdata = SpatialFilterDDFAbs(tempdata); + DataShow(handles,tempdata,sF,sT); + save('cdata.mat','cdata','tempdata','sF','sT','nCh','ComPortType','deviceName'); -% -------------------------------------------------------------------- -function m_Control_Callback(hObject, eventdata, handles) +% --- Executes on selection change in pm_features. +function pm_features_Callback(hObject, eventdata, handles) +% hObject handle to pm_features (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +% Hints: contents = cellstr(get(hObject,'String')) returns pm_features contents as cell array +% contents{get(hObject,'Value')} returns selected item from pm_features -% -------------------------------------------------------------------- -function m_signalanalysis_Callback(hObject, eventdata, handles) - signalchrs_fig(); +% --- Executes during object creation, after setting all properties. +function pm_features_CreateFcn(hObject, eventdata, handles) +% hObject handle to pm_features (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called -% -------------------------------------------------------------------- -function m_onemotorTP_Callback(hObject, eventdata, handles) - one_motro_test_panel_fig(); +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end -% --- Executes on button press in cb_ch4. -function cb_ch4_Callback(hObject, eventdata, handles) -% hObject handle to cb_ch4 (see GCBO) +% --- Executes on button press in pb_extract. +function pb_extract_Callback(hObject, eventdata, handles) +% hObject handle to pb_extract (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) -% Hint: get(hObject,'Value') returns toggle state of cb_ch4 + allF = get(handles.pm_features,'String'); + fID = char(allF(get(handles.pm_features,'Value'))); + load('cdata.mat'); + handles.nCh = size(tempdata,2); + handles.ComPortType = ComPortType; + handles.deviceName = deviceName; + fD = 0.02*sF; % considering an overlap of 20 ms + tempdata = ExtractSigFeature(tempdata,sF,fID); +% sF = sF/fD; % Adjust the sample frequency by the overlap + DataShow(handles,tempdata,sF,sT); + save('cdata.mat','cdata','tempdata','sF','sT','nCh','ComPortType','deviceName'); -% --- Executes on button press in cb_ch5. -function cb_ch5_Callback(hObject, eventdata, handles) -% hObject handle to cb_ch5 (see GCBO) +% -------------------------------------------------------------------- +function m_F50hz_Callback(hObject, eventdata, handles) +% hObject handle to m_F50hz (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) + load('cdata.mat'); + handles.nCh = size(tempdata,2); + handles.ComPortType = ComPortType; + handles.deviceName = deviceName; + tempdata = Filter50hz(sF,tempdata); + DataShow(handles,tempdata,sF,sT); + save('cdata.mat','cdata','tempdata','sF','sT','nCh','ComPortType','deviceName'); -% Hint: get(hObject,'Value') returns toggle state of cb_ch5 - - -% --- Executes on button press in cb_ch6. -function cb_ch6_Callback(hObject, eventdata, handles) -% hObject handle to cb_ch6 (see GCBO) +% --- Executes on selection change in lb_channels. +function lb_channels_Callback(hObject, eventdata, handles) +% hObject handle to lb_channels (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) -% Hint: get(hObject,'Value') returns toggle state of cb_ch6 +% Hints: contents = cellstr(get(hObject,'String')) returns lb_channels contents as cell array +% contents{get(hObject,'Value')} returns selected item from lb_channels -% --- Executes on button press in cb_ch7. -function cb_ch7_Callback(hObject, eventdata, handles) -% hObject handle to cb_ch7 (see GCBO) +% --- Executes during object creation, after setting all properties. +function lb_channels_CreateFcn(hObject, eventdata, handles) +% hObject handle to lb_channels (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB -% handles structure with handles and user data (see GUIDATA) +% handles empty - handles not created until after all CreateFcns called -% Hint: get(hObject,'Value') returns toggle state of cb_ch7 +% Hint: listbox controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end -% -------------------------------------------------------------------- -function Untitled_1_Callback(hObject, eventdata, handles) -% hObject handle to Untitled_1 (see GCBO) +% --- Executes on button press in pb_plotAll. +function pb_plotAll_Callback(hObject, eventdata, handles) +% hObject handle to pb_plotAll (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) + load('cdata.mat'); + handles.nCh = nCh; + handles.ComPortType = ComPortType; + handles.deviceName = deviceName; + DataShow(handles,cdata,sF,sT); + tempdata = cdata; + save('cdata.mat','cdata','tempdata','sF','sT','nCh','ComPortType','deviceName'); + set(handles.pb_plotSelected,'enable','on'); - -% -------------------------------------------------------------------- -function m_spatialFilterDDF_Callback(hObject, eventdata, handles) -% hObject handle to m_spatialFilterDDF (see GCBO) + +% --- Executes on button press in pb_plotSelected. +function pb_plotSelected_Callback(hObject, eventdata, handles) +% hObject handle to pb_plotSelected (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) + % Load data load('cdata.mat'); - cdata = SpatialFilterDDF(cdata); - data_show(handles,cdata,sF,sT); - save('cdata.mat','cdata','sF','sT'); + %Selected channels + Ch = get(handles.lb_channels,'Value'); + tempdata = tempdata(:,Ch); + tt = 0:1/sF:(length(tempdata)-1)/sF; + axes(handles.a_t0); + plot(tt, tempdata); + %Fast Fourier Transform + nS = length(tempdata); + NFFT = 2^nextpow2(nS); + f = sF/2*linspace(0,1,NFFT/2); + dataf = fft(tempdata(1:nS,:),NFFT)/nS; + m = 2*abs(dataf((1:NFFT/2),:)); + axes(handles.a_f0); + plot(f,m); + save('cdata.mat','cdata','tempdata','sF','sT','nCh','ComPortType','deviceName'); + set(handles.pb_plotSelected,'enable','off'); + + + +% --- Executes when figure1 is resized. +function figure1_ResizeFcn(hObject, eventdata, handles) +% hObject handle to figure1 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) -% -------------------------------------------------------------------- -function m_spatialFilterSDF_Callback(hObject, eventdata, handles) -% hObject handle to m_spatialFilterSDF (see GCBO) +% --- Executes on button press in pb_Start. +function pb_Start_Callback(hObject, eventdata, handles) +% hObject handle to pb_Start (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) - load('cdata.mat'); - cdata = SpatialFilterSDF(cdata); - data_show(handles,cdata,sF,sT); - save('cdata.mat','cdata','sF','sT'); + fast = 1; + sT = str2double(get(handles.et_sT,'String')); + tW = str2double(get(handles.et_tW,'String')); + handles.sT = sT; + handles.tW = tW; + GUI_AFEselection(0,0,0,0,0,handles,0,0,fast); -% -------------------------------------------------------------------- -function m_spatialFilterDDFAbs_Callback(hObject, eventdata, handles) -% hObject handle to m_spatialFilterDDFAbs (see GCBO) + +function et_sT_Callback(hObject, eventdata, handles) +% hObject handle to et_sT (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) - load('cdata.mat'); - cdata = SpatialFilterDDFAbs(cdata); - data_show(handles,cdata,sF,sT); - save('cdata.mat','cdata','sF','sT'); +% Hints: get(hObject,'String') returns contents of et_sT as text +% str2double(get(hObject,'String')) returns contents of et_sT as a double -% --- Executes on selection change in pm_features. -function pm_features_Callback(hObject, eventdata, handles) -% hObject handle to pm_features (see GCBO) + +% --- Executes during object creation, after setting all properties. +function et_sT_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_sT (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +function et_tW_Callback(hObject, eventdata, handles) +% hObject handle to et_tW (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) -% Hints: contents = cellstr(get(hObject,'String')) returns pm_features contents as cell array -% contents{get(hObject,'Value')} returns selected item from pm_features +% Hints: get(hObject,'String') returns contents of et_tW as text +% str2double(get(hObject,'String')) returns contents of et_tW as a double % --- Executes during object creation, after setting all properties. -function pm_features_CreateFcn(hObject, eventdata, handles) -% hObject handle to pm_features (see GCBO) +function et_tW_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_tW (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles empty - handles not created until after all CreateFcns called -% Hint: popupmenu controls usually have a white background on Windows. +% Hint: edit controls usually have a white background on Windows. % See ISPC and COMPUTER. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end -% --- Executes on button press in pb_extract. -function pb_extract_Callback(hObject, eventdata, handles) -% hObject handle to pb_extract (see GCBO) +% -------------------------------------------------------------------- +function m_Spatial_Callback(hObject, eventdata, handles) +% hObject handle to m_Spatial (see GCBO) % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) - allF = get(handles.pm_features,'String'); - fID = char(allF(get(handles.pm_features,'Value'))); - load('cdata.mat'); - cdata = ExtractSigFeature(cdata,sF,fID); - DataShow(handles,cdata,sF,sT); - save('cdata.mat','cdata','sF','sT'); +% -------------------------------------------------------------------- +function m_Recordings_Callback(hObject, eventdata, handles) +% hObject handle to m_Recordings (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + set(handles.et_tW,'visible','on'); + set(handles.et_sT,'visible','on'); + set(handles.txt_tW,'visible','on'); + set(handles.txt_sT,'visible','on'); + set(handles.pb_Start,'visible','on'); + \ No newline at end of file diff --git a/SigRecordings/ObtainRampMax.m b/SigRecordings/ObtainRampMax.m new file mode 100644 index 0000000..87aed64 --- /dev/null +++ b/SigRecordings/ObtainRampMax.m @@ -0,0 +1,350 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% ------------------- Function Description ------------------ +% Function to Record Exc Sessions +% +% --------------------------Updates-------------------------- +% 2009-04-17 / Max Ortiz / Creation +% 2009-06-29 / Max Ortiz / A dummy repeticion added before start recording +% 2011-06-00 / Per and Gustav / Added the analog front end sections +% 2011-06-29 / Max Ortiz / Optimization to be integrated in the whole system and Fixed to new coding standard. + % Any filtering was removed from this routine + % Filtering and any other signal processing should be done in + % a singal treatment routine +% 2011-08-04 / Max Ortiz / The 10% of extra indication for the user + % to contract was removed. +% 2012-02-xx / Max Ortiz / Upgrade DAQ routines for MATLAB R2011b, SBI + % Old version was kept as: + % RecordinSession_Legacy +% 2012-03-27 / Max Ortiz / Bug fixed when an arbitrary selection of channels + % However, the NI doesn't allow to skip channels +% 2012-04-30 / Max Ortiz / The possibility of simultaneous recordings was + % was removed since it didn't worked with the SBI + % in the current implementation. To see how the + % simultaneus recordings were done, see + % RecordingSession_Legacy +% 2012-12-07 / Nichlas Sander / Added VRE as an option during training. + % Movements to display are loaded in + % varargin{8}. This since previous movements + % were simply loaded as strings. +% 2013-03-07 / Nichlas Sander / When training Arm Flex/Extend the system + % will automatically change the view so the + % user can see the movement. +% 2013-08-23 / Morten Kristoffersen / Updated the recording sessions to support the new GUI_AFESelection structure +% 2013-09-19 / Pontus Lövinger / Copy of RecordingSession.m + % Now used to obatin the effort during rest + % Removed plotting functions, only use one + % repetiotionm always 3 sec contraction + % (relax), no VRE, changed instructions to user +% 2014-11-19 / Enzo Mastinu / Added the ADS1299 AFE routines, modified the + % abs based mean into RMS based mean, set the + % time window size to be samplingTime/100, RMS + % mean is done "manually" for Matlab compatibility + % reasons +% 2015-01-13 / Enzo Mastinu / The control of the different devices is now + % managed in other several functions placed + % into COMM/AFE folder. See recordingSession.m +% 2015-01-26 / Enzo Mastinu / A new GUI_Recordings has been developed for the + % BioPatRec_TRE release. Now it is possible to + % plot more then 8 channels at the same moment for + % time and frequency plots both. It is faster and + % perfectly compatible with the ramp recording + % session. At the end of the recording session it + % is possible to check all channels individually, + % apply offlinedata process as feature extraction or filter etc. + +% 20xx-xx-xx / Author / Comment + + + +function [rampMax, maxData] = ObtainRampMax(varargin) + + global handles; + global allData; + global timeStamps; + global samplesCounter; + + allData = []; + nM = varargin{1}; + cT = 3; + mov = varargin{2}; + handles = varargin{3}; + afeSettings = varargin{4}; + trainWithVr = varargin{5}; + vreMovements = varargin{6}; + vreLeftHand = varargin{7}; + + % Get required informations from afeSettings structure + nCh = afeSettings.channels; + sF = afeSettings.sampleRate; + deviceName = afeSettings.name; + ComPortType = afeSettings.ComPortType; + if strcmp(ComPortType, 'COM') + ComPortName = afeSettings.ComPortName; + end + + % Save back acquisition parameters to the handles + handles.cT = cT; + handles.nCh = nCh; + handles.sF = sF; + handles.ComPortType = ComPortType; + if strcmp(ComPortType, 'COM') + handles.ComPortName = ComPortName; + end + handles.deviceName = deviceName; + % To avoid bugs in RecordingSession_ShowData function + handles.fast = 1; + handles.rep = 1; + handles.rT = cT; + handles.rampStatus = 0; + + % Initialization of sampling time + sT = cT; + sTall = cT; + handles.sTall = sTall; + handles.sT = sT; + + % Setting for data peeking + tW = sT/100; % Time window size + tWs = tW*sF; % Time window samples + handles.tWs = tWs; + timeStamps = 0:1/sF:tW-1/sF; % Create vector of time + + + %% Initialize GUI.. + + % Initialization of the VRE for training if required + if trainWithVr + % Parameters for the movement in the VRE + distance = 60; + numJumps = 30; + % Open and connect to the VRE + open('Virtual Reality.exe'); + handles.vreCommunication = tcpip('127.0.0.1',23068,'NetworkRole','server'); + fopen(handles.vreCommunication); + + %Set up VRE to not return any data. This will speed up the + %communication. + fwrite(handles.vreCommunication,sprintf('%c%c%c%c%c','c',char(7),char(0),char(0),char(0))); + + % Send value to show Arm if that value is chosen. + sent = 0; + for i = 1:nM + for j = 1:length(vreMovements{i}) + movement = vreMovements{i}{j}; + if ismember(movement.id,[20 21]) && sent == 0 + fwrite(handles.vreCommunication,sprintf('%c%c%c%c%c','c',char(6),char(1),char(0),char(0))); + sent = 1; + end + end + end + + if vreLeftHand + fwrite(handles.vreCommunication,sprintf('%c%c%c%c%c','c',char(5),char(1),char(0),char(0))); + end + end + + pause on; + + % Initialize plots, offset the data + ampPP = 5; + sData = zeros(tWs,nCh); + fData = zeros(tWs,nCh); + offVector = 0:nCh-1; + offVector = offVector .* ampPP; + for i = 1 : nCh + sData(:,i) = sData(:,i) + offVector(i); + fData(:,i) = fData(:,i) + offVector(i); + end + + % Draw figure + ymin = -ampPP*2/3; + ymax = ampPP * nCh - ampPP*1/3; + p_t0 = plot(handles.a_t0, timeStamps, sData); + handles.p_t0 = p_t0; + xlim(handles.a_t0, [0,tW]); + ylim(handles.a_t0, [ymin ymax]); + set(handles.a_t0,'YTick',offVector); + set(handles.a_t0,'YTickLabel',0:nCh-1); + p_f0 = plot(handles.a_f0,timeStamps,fData); + handles.p_f0 = p_f0; + xlim(handles.a_f0, [0,sF/2]); + ylim(handles.a_f0, [ymin ymax]); + set(handles.a_f0,'YTick',offVector); + set(handles.a_f0,'YTickLabel',0:nCh-1); + + % Initialization of progress bar + xpatch = [0 0 0 0]; + ypatch = [0 0 1 1]; + % set(handles.figure1,'CurrentAxes',handles.a_prog); + axes(handles.a_prog); + handles.hPatch = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); + + % Allocation of resource to improve speed, total data + recSessionData = zeros(sF*sTall, nCh, nM); + + + %% Starting Session.. + + % Warning to the user + set(handles.t_msg,'String','Get ready for Maximum Voluntary Contraction'); + pause(7); + importdata('Img/relax.jpg'); % Import Image + drawnow; + + handles.contraction = 1; + + % Run all movements or excersices + for ex = 1 : nM + + timeStamps = 0:1/sF:tW-1/sF; % Timestamps used the time vector + currentTv = 1; % Current time vector + tV = timeStamps(currentTv):1/sF:(tW-1/sF)+timeStamps(currentTv); % Time vector used for drawing graphics + currentTv = currentTv - 1 + tWs; % Updated everytime tV is updated + acquireEvent.TimeStamps = tV'; + + disp(['Start ex: ' num2str(ex) ]) + + % Warning to the user + fileName = ['Img/' char(mov(ex)) '.jpg']; + if ~exist(fileName,'file') + fileName = 'Img/relax.jpg'; + end + + movI = importdata(fileName); % Import Image + set(handles.a_pic,'Visible','on'); % Turn on visibility + pic = image(movI,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + + % Show warning to prepare + set(handles.t_msg,'String',['Get ready for Maximum Effort ' mov(ex) ' in 3 s']); + pause(1); + set(handles.t_msg,'String',['Get ready for Maximum Effort ' mov(ex) ' in 2 s']); + pause(1); + set(handles.t_msg,'String',['Get ready for Maximum Effort ' mov(ex) ' in 1 s']); + pause(1); + + % Minimum Maximum Contraction with NI DAQ card + if strcmp (ComPortType, 'NI') + + % Init SBI + sCh = 1:nCh; + s = InitSBI_NI(sF,sT,sCh); + s.NotifyWhenDataAvailableExceeds = tWs; % PEEK time + lh = s.addlistener('DataAvailable', @RecordingSession_ShowData); + + % Start DAQ + s.startBackground(); % Run in the backgroud + + % Contraction + set(handles.t_msg,'String',mov(ex)); + pic = image(movI,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + startContractionTic = tic; + if trainWithVr + numberOfMovements = length(vreMovements{ex}); + tempJumps = numJumps/numberOfMovements; + tempDistance = round(distance/tempJumps); + vreString = ''; + for i = 1:numberOfMovements + movement = vreMovements{ex}{i}; + vreString = sprintf('%s%c%c%c%c%c',vreString,char(1),char(movement.idVRE),char(movement.vreDir),char(tempDistance),char(1)); + end + for j = 1:tempJumps + fwrite(handles.vreCommunication,vreString); + end + end + pause(cT - toc(startContractionTic)); + + % Minimum Voluntary Contraction with other devices + else + + % Connect the chosen device, it returns the connection object + obj = ConnectDevice(handles); + + % Set the selected device and Start the acquisition + SetDeviceStartAcquisition(handles, obj); + + % new contraction, the GUI must be updated + set(handles.t_msg,'String',mov(ex)); + pic = image(movI,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + drawnow; + if trainWithVr + numberOfMovements = length(vreMovements{ex}); + tempJumps = numJumps/numberOfMovements; + tempDistance = round(distance/tempJumps); + vreString = ''; + for i = 1:numberOfMovements + movement = vreMovements{ex}{i}; + vreString = sprintf('%s%c%c%c%c%c',vreString,char(1),char(movement.idVRE),char(movement.vreDir),char(tempDistance),char(1)); + end + for j = 1:tempJumps + fwrite(handles.vreCommunication,vreString); + end + end + + samplesCounter = 1; % variable used to track the progressing time of the recording session + tic + for timeWindowNr = 1:sT/tW + + cData = Acquire_tWs(deviceName, obj, nCh, tWs); % acquire a new time window of samples + acquireEvent.Data = cData; + RecordingSession_ShowData(0, acquireEvent); % plot data and add cData to allData vector + + samplesCounter = samplesCounter + tWs; + end + toc + + % Stop acquisition, contraction time expired + StopAcquisition(deviceName, obj); + + end + + % NI DAQ card: "You must delete the listener once the operation is complete" + if strcmp(ComPortType,'NI'); + if ~s.IsDone % check if is done + s.wait(); + end + delete(lh); + end + + % Save Data + recSessionData(:,:,ex) = allData(:,:); + allData = []; % clean global data for next movement + + end + + set(handles.a_pic,'Visible','off'); % Turn OFF visibility + delete(pic); % Delete image + set(handles.t_msg,'String','Maximum and Minimum Session Terminated'); % Show message about acquisition + pause(3); + + if trainWithVr + fclose(handles.vreCommunication); + end + + %% Get the max value and organize them acccording to the selected movements + maxData = recSessionData(1:cT*sF,:,:); % Take only contraction part, from 0 to cT [s] + percent = (cT/100)*20; + cleaned = maxData(percent*sF:(cT-percent)*sF-1,:,:); % Remove the 20% from the begin and from the end of the data + avgSample = sqrt(mean((cleaned.^2),1)); % RMS Mean value compatible with old version of Matlab + + chMean = mean(avgSample); % Averages the time windows then averages the channels + rampMax = squeeze(chMean); + +end diff --git a/SigRecordings/ObtainRampMin.m b/SigRecordings/ObtainRampMin.m new file mode 100644 index 0000000..c9ca2ea --- /dev/null +++ b/SigRecordings/ObtainRampMin.m @@ -0,0 +1,265 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% ------------------- Function Description ------------------ +% Function to Record Exc Sessions +% +% --------------------------Updates-------------------------- +% 2009-04-17 / Max Ortiz / Creation +% 2009-06-29 / Max Ortiz / A dummy repeticion added before start recording +% 2011-06-00 / Per and Gustav / Added the analog front end sections +% 2011-06-29 / Max Ortiz / Optimization to be integrated in the whole system and Fixed to new coding standard. + % Any filtering was removed from this routine + % Filtering and any other signal processing should be done in + % a singal treatment routine +% 2011-08-04 / Max Ortiz / The 10% of extra indication for the user + % to contract was removed. +% 2012-02-xx / Max Ortiz / Upgrade DAQ routines for MATLAB R2011b, SBI + % Old version was kept as: + % RecordinSession_Legacy +% 2012-03-27 / Max Ortiz / Bug fixed when an arbitrary selection of channels + % However, the NI doesn't allow to skip channels +% 2012-04-30 / Max Ortiz / The possibility of simultaneous recordings was + % was removed since it didn't worked with the SBI + % in the current implementation. To see how the + % simultaneus recordings were done, see + % RecordingSession_Legacy +% 2012-12-07 / Nichlas Sander / Added VRE as an option during training. + % Movements to display are loaded in + % varargin{8}. This since previous movements + % were simply loaded as strings. +% 2013-03-07 / Nichlas Sander / When training Arm Flex/Extend the system + % will automatically change the view so the + % user can see the movement. +% 2013-08-23 / Morten Kristoffersen / Updated the recording sessions to support the new GUI_AFESelection structure +% 2013-09-19 / Pontus Lövinger / Copy of RecordingSession.m + % Now used to obatin the effort during rest + % Removed plotting functions, only use one + % repetiotionm always 3 sec contraction + % (relax), no VRE, changed instructions to user +% 2014-11-19 / Enzo Mastinu / Added the ADS1299 AFE routines, modified the + % abs based mean into RMS based mean, set the + % time window size to be samplingTime/100, RMS + % mean is done "manually" for Matlab compatibility + % reasons +% 2015-01-13 / Enzo Mastinu / The control of the different devices is now + % managed in other several functions placed + % into COMM/AFE folder. See recordingSession.m +% 2015-01-26 / Enzo Mastinu / A new GUI_Recordings has been developed for the + % BioPatRec_TRE release. Now it is possible to + % plot more then 8 channels at the same moment for + % time and frequency plots both. It is faster and + % perfectly compatible with the ramp recording + % session. At the end of the recording session it + % is possible to check all channels individually, + % apply offlinedata process as feature extraction or filter etc. + +% 20xx-xx-xx / Author / Comment + + + +function [rampMin, minData] = ObtainRampMin(varargin) + + global handles; + global allData; + global timeStamps; + global samplesCounter; + + allData = []; + nR = 1; % Only one repetition + rT = 3; % Always use 3 sec relaxation time + handles = varargin{1}; + afeSettings = varargin{2}; + + % Get required informations from afeSettings structure + nCh = afeSettings.channels; + sF = afeSettings.sampleRate; + deviceName = afeSettings.name; + ComPortType = afeSettings.ComPortType; + if strcmp(ComPortType, 'COM') + ComPortName = afeSettings.ComPortName; + end + + % Save back acquisition parameters to the handles + handles.nR = nR; + handles.rT = rT; + handles.nCh = nCh; + handles.sF = sF; + handles.ComPortType = ComPortType; + if strcmp(ComPortType, 'COM') + handles.ComPortName = ComPortName; + end + handles.deviceName = deviceName; + % To avoid bugs in RecordingSession_ShowData function + handles.fast = 1; + handles.rep = 1; + handles.cT = rT; + handles.rampStatus = 0; + + % Initialization of sampling time + sT = rT; + sTall = rT; + handles.sTall = sTall; + handles.sT = sT; + + % Setting for data peeking + tW = sT/100; % Time window size + tWs = tW*sF; % Time window samples + handles.tWs = tWs; + timeStamps = 0:1/sF:tW-1/sF; % Create vector of time + + + %% Initialize GUI.. + pause on; + + % Initialize plots, offset the data + ampPP = 5; + sData = zeros(tWs,nCh); + fData = zeros(tWs,nCh); + offVector = 0:nCh-1; + offVector = offVector .* ampPP; + for i = 1 : nCh + sData(:,i) = sData(:,i) + offVector(i); + fData(:,i) = fData(:,i) + offVector(i); + end + + % Draw figure + ymin = -ampPP*2/3; + ymax = ampPP * nCh - ampPP*1/3; + p_t0 = plot(handles.a_t0, timeStamps, sData); + handles.p_t0 = p_t0; + xlim(handles.a_t0, [0,tW]); + ylim(handles.a_t0, [ymin ymax]); + set(handles.a_t0,'YTick',offVector); + set(handles.a_t0,'YTickLabel',0:nCh-1); + p_f0 = plot(handles.a_f0,timeStamps,fData); + handles.p_f0 = p_f0; + xlim(handles.a_f0, [0,sF/2]); + ylim(handles.a_f0, [ymin ymax]); + set(handles.a_f0,'YTick',offVector); + set(handles.a_f0,'YTickLabel',0:nCh-1); + + % Initialization of progress bar + xpatch = [0 0 0 0]; + ypatch = [0 0 1 1]; + % set(handles.figure1,'CurrentAxes',handles.a_prog); + axes(handles.a_prog); + handles.hPatch = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); + + + %% Starting Session.. + + % Warning to the user + set(handles.t_msg,'String','Keep a relaxed/rest position, avoid every contraction'); + relax = importdata('Img/relax.jpg'); % Import Image + set(handles.a_pic,'Visible','on'); % Turn on visibility + pic = image(relax,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + drawnow; + pause(7); + + handles.contraction = 0; + + % for ex = 1 : nM + ex = 1; + + % Show warning to prepare + set(handles.t_msg,'String','Get ready to Relax in 3 s'); + pause(1); + set(handles.t_msg,'String','Get ready to Relax in 2 s'); + pause(1); + set(handles.t_msg,'String','Get ready to Relax in 1 s'); + pause(1); + + timeStamps = 0:1/sF:tW-1/sF; % Timestamps used the time vector + currentTv = 1; % Current time vector + tV = timeStamps(currentTv):1/sF:(tW-1/sF)+timeStamps(currentTv); % Time vector used for drawing graphics + currentTv = currentTv - 1 + tWs; % Updated everytime tV is updated + acquireEvent.TimeStamps = tV'; + + % Minimum Voluntary Contraction with NI DAQ card + if strcmp (ComPortType, 'NI') + + % Init SBI + sCh = 1:nCh; + s = InitSBI_NI(sF,sT,sCh); + s.NotifyWhenDataAvailableExceeds = tWs; % PEEK time + lh = s.addlistener('DataAvailable', @RecordingSession_ShowData); + + % Start DAQ + cData = zeros(sF*sT, nCh); + s.startBackground(); % Run in the backgroud + + % Contraction + set(handles.t_msg,'String','Relax'); + startRelaxationTic = tic; + pause(rT - toc(startRelaxationTic)); + + % Minimum Voluntary Contraction with other devices + else + + % Connect the chosen device, it returns the connection object + obj = ConnectDevice(handles); + + % Set the selected device and Start the acquisition + SetDeviceStartAcquisition(handles, obj); + + % update the GUI + set(handles.t_msg,'String','Relax'); + + samplesCounter = 1; % variable used to track the progressing time of the recording session + tic + for timeWindowNr = 1:sT/tW + + cData = Acquire_tWs(deviceName, obj, nCh, tWs); % acquire a new time window of samples + acquireEvent.Data = cData; + RecordingSession_ShowData(0, acquireEvent); % plot data and add cData to allData vector + + samplesCounter = samplesCounter + tWs; + end + toc + + % Stop acquisition, "contraction" time expired + StopAcquisition(deviceName, obj); + + end + + % NI DAQ card: "You must delete the listener once the operation is complete" + if strcmp(ComPortType,'NI'); + if ~s.IsDone % check if is done + s.wait(); + end + delete(lh); + end + + set(handles.a_pic,'Visible','off'); % Turn OFF visibility + delete(pic); % Delete image + set(handles.t_msg,'String','Relaxed State Recorded'); % Show message about acquisition + pause(3); + + + %% Get the minimum voluntary contraction informations from allData vector + minData = allData(1:rT*sF,:); % Take only "contraction" part, from 0 to rT [s] + percent = (rT/100)*20; + cleaned = minData(percent*sF:(rT-percent)*sF-1,:); % Remove the 20% from the begin and from the end of the data +% avgSample = mean(rms(cleaned),1); % RMS Mean value over the samples + avgSample = sqrt(mean((cleaned.^2),1)); % RMS Mean value compatible with old version of Matlab + + chMean = mean(avgSample); % Averages over the channels + rampMin = chMean(:,:,1); % Get the averge minimum value + % rampMin = squeeze(chMean); + +end diff --git a/SigRecordings/RecordingSession.m b/SigRecordings/RecordingSession.m index b78fb32..610227e 100644 --- a/SigRecordings/RecordingSession.m +++ b/SigRecordings/RecordingSession.m @@ -1,719 +1,536 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% ------------------- Function Description ------------------ -% Function to Record Exc Sessions -% -% --------------------------Updates-------------------------- -% 2009-04-17 / Max Ortiz / Creation -% 2009-06-29 / Max Ortiz / A dummy repeticion added before start recording -% 2011-06-00 / Per and Gustav / Added the analog front end sections -% 2011-06-29 / Max Ortiz / Optimization to be integrated in the whole system and Fixed to new coding standard. - % Any filtering was removed from this routine - % Filtering and any other signal processing should be done in - % a singal treatment routine -% 2011-08-04 / Max Ortiz / The 10% of extra indication for the user - % to contract was removed. -% 2012-02-xx / Max Ortiz / Upgrade DAQ routines for MATLAB R2011b, SBI - % Old version was kept as: - % RecordinSession_Legacy -% 2012-03-27 / Max Ortiz / Bug fixed when an arbitrary selection of channels - % However, the NI doesn't allow to skip channels -% 2012-04-30 / Max Ortiz / The possibility of simultaneous recordings was - % was removed since it didn't worked with the SBI - % in the current implementation. To see how the - % simultaneus recordings were done, see - % RecordingSession_Legacy -% 2012-12-07 / Nichlas Sander / Added VRE as an option during training. - % Movements to display are loaded in - % varargin{8}. This since previous movements - % were simply loaded as strings. -% 20xx-xx-xx / Author / Comment - - - -function [cdata, sF] = RecordingSession(varargin) - -global handles; -global allData; - -allData = []; -nM = varargin{1}; -nR = varargin{2}; -cT = varargin{3}; -rT = varargin{4}; -mov = varargin{5}; -handles = varargin{6}; -afeSettings = varargin{7}; -trainWithVr = varargin{8}; -vreMovements = varargin{9}; -vreLeftHand = varargin{10}; - -distance = 90; -numJumps = 45; - -sT = (cT+rT)*nR; % Sampling time, it is the time of contraction + - % Time of relaxation x Number of repetitions - -% Get number of channels and sampling frequency -% for the device to be displayed -if afeSettings.NI.show - nCh = afeSettings.NI.channels; - sF = afeSettings.NI.sampleRate; -elseif afeSettings.ADS.show - nCh = afeSettings.ADS.channels; - sF = afeSettings.ADS.sampleRate; -elseif afeSettings.RHA.show - nCh = afeSettings.RHA.channels; - sF = afeSettings.RHA.sampleRate; -end - -if trainWithVr - open('Virtual Reality.exe'); - handles.vreCommunication = tcpip('127.0.0.1',23068,'NetworkRole','server'); - fopen(handles.vreCommunication); - - %Set up VRE to not return any data. This will speed up the - %communication. - fwrite(handles.vreCommunication,sprintf('%c%c%c%c%c','c',char(7),char(0),char(0),char(0))); - - % Send value to show Arm if that value is chosen. - sent = 0; - for i = 1:nM - for j = 1:length(vreMovements{i}) - movement = vreMovements{i}{j}; - if ismember(movement.id,[20 21]) && sent == 0 - fwrite(handles.vreCommunication,sprintf('%c%c%c%c%c','c',char(6),char(1),char(0),char(0))); - sent = 1; - end - end - end - - if vreLeftHand - fwrite(handles.vreCommunication,sprintf('%c%c%c%c%c','c',char(5),char(1),char(0),char(0))); - end -end - -handles.sF = sF; -handles.cT = cT; -handles.rT = rT; -handles.nCh = nCh; - -pause on; - -%% Initialize plots -% Create handles for the plots -% this is faster than creating the plot everytime -tt = 0:1/sF:sT/100-1/sF; % Create data for sT / 100 -ymin = -3; -ymax = 3; - -% Init the plots -if nCh >= 1 - %axes(handles.a_t0); - p_t0 = plot(handles.a_t0,tt,tt); - xlim('auto'); - ylim(handles.a_t0, [ymin ymax]); - handles.p_t0 = p_t0; - %axes(handles.a_f0); - p_f0 = plot(handles.a_f0,1,1); - handles.p_f0 = p_f0; -end -if nCh >= 2 - p_t1 = plot(handles.a_t1,tt,tt); - ylim(handles.a_t1, [ymin ymax]); - p_f1 = plot(handles.a_f1,1,1); - handles.p_t1 = p_t1; - handles.p_f1 = p_f1; -end -if nCh >= 3 - p_t2 = plot(handles.a_t2,tt,tt); - ylim(handles.a_t2, [ymin ymax]); - p_f2 = plot(handles.a_f2,1,1); - handles.p_t2 = p_t2; - handles.p_f2 = p_f2; -end - -if nCh >= 4 - p_t3 = plot(handles.a_t3,tt,tt); - ylim(handles.a_t3, [ymin ymax]); - p_f3 = plot(handles.a_f3,1,1); - handles.p_t3 = p_t3; - handles.p_f3 = p_f3; -end - -if nCh >= 5 - p_t4 = plot(handles.a_t4,tt,tt); - ylim(handles.a_t4, [ymin ymax]); - %axes(handles.a_f4); - %p_f4 = plot(1,1); - handles.p_t4 = p_t4; -end - -if nCh >= 6 - p_t5 = plot(handles.a_t5,tt,tt); - ylim(handles.a_t5, [ymin ymax]); - %axes(handles.a_f5); - %p_f5 = plot(1,1); - handles.p_t5 = p_t5; -end - -if nCh >= 7 - p_t6 = plot(handles.a_t6,tt,tt); - ylim(handles.a_t6, [ymin ymax]); - %axes(handles.a_f6); - %p_f6 = plot(1,1); - handles.p_t6 = p_t6; -end - -if nCh >= 8 - p_t7 = plot(handles.a_t7,tt,tt); - ylim(handles.a_t7, [ymin ymax]); - %axes(handles.a_f7); - %p_f7 = plot(1,1); - handles.p_t7 = p_t7; -end - -%% Initialization of progress bar -xpatch = [0 0 0 0]; -ypatch = [0 0 1 1]; -%set(handles.figure1,'CurrentAxes',handles.a_prog); -axes(handles.a_prog); -handles.hPatch = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); - -%% Initialization of the effort bar -xpatch = [1 1 0 0]; -ypatch = [0 0 0 0]; -axes(handles.a_effort0); -handles.hPatch0 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(handles.a_effort1); -handles.hPatch1 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(handles.a_effort1); -handles.hPatch1 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(handles.a_effort2); -handles.hPatch2 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(handles.a_effort3); -handles.hPatch3 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(handles.a_effort4); -handles.hPatch4 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(handles.a_effort5); -handles.hPatch5 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(handles.a_effort6); -handles.hPatch6 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(handles.a_effort7); -handles.hPatch7 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -handles.effortMax = 2; - - -%% Initialize DAQ card -if afeSettings.NI.active - %afeSettings.NI.sampleRate=sF; %overrides the individual samplerate choise in AFS_select - % instruction from Per and Gustav, why - % would you overwrite the selected sF? - - % All following sF needs to be changed to - % invidual sF in order to make it work - % /Per - %ai = Init_NI_AI(handles,afeSettings.NI.sampleRate,sT,nCh); %Legacy - %Init SBI - sCh = 1:nCh; - s = InitSBI_NI(afeSettings.NI.sampleRate,sT,sCh); - s.NotifyWhenDataAvailableExceeds = (sF*sT)/100; % PEEK time - lh = s.addlistener('DataAvailable', @RecordingSession_ShowData); - - dev = afeSettings.NI.name; - -end -if afeSettings.ADS.active - Amp=12; - Vref=2.4*2; %*2 is from bipolar reference - ByteDepth=3; - afeSettings.ADS.sampleRate=sF; %overrides the individual samplerate choise in AFS_select - dataFormat=2; - ADS = AFE_PICCOLO(afeSettings.ADS.ComPortType,afeSettings.ADS.sampleRate, ... - sT,nCh,Amp,Vref,dataFormat,afeSettings.ADS.name,ByteDepth); -end -if afeSettings.RHA.active - Amp=200; - Vref=2.5; - ByteDepth=2; - afeSettings.RHA.sampleRate=sF; %overrides the individual samplerate choise in AFS_select - dataFormat=1; - RHA = AFE_PICCOLO(afeSettings.RHA.ComPortType,afeSettings.RHA.sampleRate, ... - sT,nCh,Amp,Vref,dataFormat,afeSettings.RHA.name,ByteDepth); -end - -%% Allocation of resource to improve speed, total data - -sbiData = zeros(sF*sT,nCh,nM); -ADStdata = zeros(sF*sT,nCh,nM); -RHAtdata = zeros(sF*sT,nCh,nM); - -% Warning to the user -set(handles.t_msg,'String','Get ready to start: 3'); -pause(1); -set(handles.t_msg,'String','Get ready to start: 2'); -pause(1); -set(handles.t_msg,'String','Get ready to start: 1'); -pause(1); - -relax = importdata('Img/relax.jpg'); % Import Image -drawnow; - -%% Run all movements or excersices -for ex = 1 : nM - disp(['Start ex: ' num2str(ex) ]) - - % Warning to the user - fileName = ['Img/' char(mov(ex)) '.jpg']; - if ~exist(fileName,'file') - fileName = 'Img/relax.jpg'; - end - - movI = importdata(fileName); % Import Image - set(handles.a_pic,'Visible','on'); % Turn on visibility - %axes(handles.a_pic); % get handles - pic = image(movI,'Parent',handles.a_pic); % set image - axis(handles.a_pic,'off'); % Remove axis tick marks - - % Show warning to prepare - set(handles.t_msg,'String',['Get ready for ' mov(ex) ' in 3 s']); - pause(1); - set(handles.t_msg,'String',['Get ready for ' mov(ex) ' in 2 s']); - pause(1); - set(handles.t_msg,'String',['Get ready for ' mov(ex) ' in 1 s']); - %set(handles.a_pic,'Visible','off'); % Turn OFF visibility - %delete(pic); % Delete image - pause(1); - - %% Dummy Contraction - set(handles.t_msg,'String',mov(ex)); - if afeSettings.prepare - if trainWithVr - numberOfMovements = length(vreMovements{ex}); - tempJumps = numJumps/numberOfMovements; - tempDistance = round(distance/tempJumps); - vreString = ''; - for i = 1:numberOfMovements - movement = vreMovements{ex}{i}; - vreString = sprintf('%s%c%c%c%c%c',vreString,char(1),char(movement.idVRE),char(movement.vreDir),char(tempDistance),char(1)); - end - for j = 1:tempJumps - fwrite(handles.vreCommunication,vreString); - end - end - - pause(cT); - set(handles.t_msg,'String','Relax'); - if trainWithVr - fwrite(handles.vreCommunication,sprintf(sprintf('%c%c%c%c%c','r',char(1),char(1),char(1),char(1)))); - end - pic = image(relax,'Parent',handles.a_pic); % set image - axis(handles.a_pic,'off'); % Remove axis tick marks - pause(rT); - end - - - %% Start DAQ - if afeSettings.NI.active - % start(ai); - % Run in the backgroud - s.startBackground(); - end - if afeSettings.ADS.active - ADS.startRecording - end - if afeSettings.RHA.active - RHA.startRecording - end - - %% Repetitions - for rep = 1 : nR - handles.rep = rep; - - % Contraction - set(handles.t_msg,'String',mov(ex)); - pic = image(movI,'Parent',handles.a_pic); % set image - axis(handles.a_pic,'off'); % Remove axis tick marks - handles.contraction = 1; - startContractionTic = tic; - if trainWithVr - numberOfMovements = length(vreMovements{ex}); - tempJumps = numJumps/numberOfMovements; - tempDistance = round(distance/tempJumps); - vreString = ''; - for i = 1:numberOfMovements - movement = vreMovements{ex}{i}; - vreString = sprintf('%s%c%c%c%c%c',vreString,char(1),char(movement.idVRE),char(movement.vreDir),char(tempDistance),char(1)); - end - for j = 1:tempJumps - fwrite(handles.vreCommunication,vreString); - end - end - pause(cT - toc(startContractionTic)); - - % Relax - set(handles.t_msg,'String','Relax'); - startRelaxingTic = tic; - if trainWithVr - for i = 1:numJumps - fwrite(handles.vreCommunication,sprintf(sprintf('%c%c%c%c%c',char(1),char(14),char(1),char(distance/numJumps),char(1)))); - end - end - pic = image(relax,'Parent',handles.a_pic); % set image - axis(handles.a_pic,'off'); % Remove axis tick marks - handles.contraction = 0; - pause(rT - toc(startRelaxingTic)); - end - - %% Save Data - if afeSettings.NI.active - %check if is done - if ~s.IsDone - s.wait(); - end - sbiData(:,:,ex) = allData; - % clean global data for next movement - allData = []; - - end - if afeSettings.RHA.active -% disp('RHA:') -% disp(RHA.SamplesAcquired) -% disp(RHA.bytesAcquired) -% disp(size(RHA.data)) - data = [RHA.data ; RHA.storeFromBuffer]; -% disp(size(data)) -% disp(size(RHAtdata)) - RHAtdata(:,:,ex)=data * (RHA.vref/(RHA.amplification*2^(RHA.byteDepth*8))); %Converting to input referred voltage - end - if afeSettings.ADS.active -% disp('ADS:') -% disp(ADS.SamplesAcquired) -% disp(ADS.bytesAcquired) -% disp(size(data)) - - data = [ADS.data ; ADS.storeFromBuffer]; -% disp(size(data)) -% disp(size(ADStdata)) - ADStdata(:,:,ex)=data * (ADS.vref/(ADS.amplification*2^(ADS.byteDepth*8))); %Converting to input referred voltage - end - -end - - -set(handles.t_msg,'String','Session Terminated'); % Show message about acquisition - -%% Save data and compute training data using cTp -if afeSettings.NI.active - delete(lh); - NItdata=sbiData; -else - NItdata=[]; -end -if afeSettings.ADS.active - ADStdata=ADStdata; - %ADStrdata=ComputeTrainingData(ADStdata,sF,cT,rT,nR,nM,cTp); -else - ADStdata=[]; - %ADStrdata=[]; -end - -if afeSettings.RHA.active - RHAtdata=RHAtdata; - %RHAtrdata=ComputeTrainingData(RHAtdata,sF,cT,rT,nR,nM,cTp); -else - RHAtdata=[]; - %RHAtrdata=[]; -end - - -%% Save Session to file -recSession.sF = sF; -recSession.sT = sT; -recSession.cT = cT; -recSession.rT = rT; -recSession.nM = nM; -recSession.nR = nR; -recSession.nCh = nCh; -recSession.dev = dev; -recSession.mov = mov; -recSession.date = fix(clock); -recSession.cmt = inputdlg('Additional comment on the recording session','Comments'); - -[filename, pathname] = uiputfile({'*.mat','MAT-files (*.mat)'},'Save as', 'Untitled.mat'); - if isequal(filename,0) || isequal(pathname,0) - disp('User pressed cancel') - else - disp(['User selected ', fullfile(pathname, filename)]) - if afeSettings.NI.active - recSession.tdata = NItdata; - save([pathname,filename],'recSession'); - - %recSession.trdata = NItrdata; -% if exist([pathname 'NI\'],'dir') == 0 -% mkdir(pathname,'NI') -% end -% save([pathname, 'NI\',filename],'recSession'); - end - if afeSettings.ADS.active - recSession.tdata = ADStdata; - %recSession.trdata = ADStrdata; - %Possible to add string with device info for example recSession.device=afeSettings.ADS.name; - if exist([pathname 'ADS1298\'],'dir') == 0 - mkdir(pathname,'ADS1298') - end - save([pathname, 'ADS1298\',filename],'recSession'); - - % not needed here any more? -% if filters.PLH || filters.BP -% recSession.tdata = ADStdataFiltered; -% % recSession.trdata = ADStrdataFiltered; -% if exist([pathname 'ADS1298Filtered\'],'dir') == 0 -% mkdir(pathname,'ADS1298Filtered') -% end -% save([pathname,'ADS1298Filtered\',filename],'recSession'); -% end - end - if afeSettings.RHA.active - recSession.tdata = RHAtdata; - %recSession.trdata = RHAtrdata; - %Possible to add string with device info for example recSession.device=afeSettings.RHA.name; - if exist([pathname 'RHA2216\'],'dir') == 0 - mkdir(pathname,'RHA2216') - end - save([pathname 'RHA2216\',filename],'recSession'); - - % not needed here any more? -% if filters.PLH || filters.BP -% recSession.tdata = RHAtdataFiltered; -% %recSession.trdata = RHAtrdataFiltered; -% if exist([pathname 'RHA2216Filtered\'],'dir') == 0 -% mkdir(pathname,'RHA2216Filtered') -% end -% save([pathname,'RHA2216Filtered\',filename],'recSession'); -% end - end - end - -% Copy acquired data from the last excersice into cdata -% Display it -disp(recSession); - -if afeSettings.NI.show - data = NItdata(:,:,end); -elseif afeSettings.ADS.show - data = ADStdata(:,:,end).* (ADS.vref/(ADS.amplification*2^(ADS.byteDepth*8))); -elseif afeSettings.RHA.show - data = RHAtdata(:,:,end).* (RHA.vref/(RHA.amplification*2^(RHA.byteDepth*8))); -end - -chIdx=1; -if nCh >= 1 - cdata(:,1) = data(:,chIdx); - chIdx=chIdx+1; -end -if nCh >= 2 - cdata(:,2) = data(:,chIdx); - chIdx=chIdx+1; -end -if nCh >= 3 - cdata(:,3) = data(:,chIdx); - chIdx=chIdx+1; -end -if nCh >= 4 - cdata(:,4) = data(:,chIdx); - chIdx=chIdx+1; -end -if nCh >= 5 - cdata(:,5) = data(:,chIdx); - chIdx=chIdx+1; -end -if nCh >= 6 - cdata(:,6) = data(:,chIdx); - chIdx=chIdx+1; -end -if nCh >= 7 - cdata(:,7) = data(:,chIdx); - chIdx=chIdx+1; -end -if nCh >= 8 - cdata(:,8) = data(:,chIdx); -end - -if trainWithVr - fclose(handles.vreCommunication); -end - -DataShow(handles,cdata,sF,sT); -set(handles.a_pic,'Visible','off'); % Turn OFF visibility -delete(pic); % Delete image -end - - -function RecordingSession_ShowData(src,event) - - global handles; - global allData; - persistent timeStamps; - - % Get info from hendles - sF = handles.sF; - nCh = handles.nCh; - effortMax = handles.effortMax; - rep = handles.rep; - cT = handles.cT; - rT = handles.rT; - - - % Get data - if(isempty(allData)) % Fist DAQ callback - timeStamps = []; - end - - tempData = event.Data; - allData = [allData; tempData]; - timeStamps = [timeStamps; event.TimeStamps]; - - %% Status bar update - %thisToc = timeStamps(end) - ((rep-1)*(cT+rT)); - %lastToc = (cT+rT); - if handles.contraction - thisToc = timeStamps(end) - ((rep-1)*(cT+rT)); - lastToc = cT; - else - thisToc = timeStamps(end) - ((rep*cT)+((rep-1)*rT)); - lastToc = rT; - end - - x =1-(thisToc/lastToc); -% set(handles.figure1,'CurrentAxes',handles.a_prog); - set(handles.hPatch,'Xdata',[0 x x 0]); - - - %% Display peeked Data - aNs = length(tempData(:,1)); - NFFT = 2^nextpow2(aNs); % Next power of 2 from number of samples - f = sF/2*linspace(0,1,NFFT/2); - dataf = fft(tempData(1:aNs,:),NFFT)/aNs; - m = 2*abs(dataf((1:NFFT/2),:)); - - - chIdx = 1;%Channel Index for map data - if nCh >= 1 - p_t0 = handles.p_t0; - p_f0 = handles.p_f0; - set(p_t0,'YData',tempData(:,chIdx)); - set(p_f0,'XData',f); - set(p_f0,'YData',m(:,chIdx)); - % Update effort bar - xT = mean(abs(tempData(:,chIdx))); - x = xT / effortMax; - set(handles.hPatch0,'Ydata',[0 x x 0]); - - chIdx=chIdx+1; - end - if nCh >= 2 - p_t1 = handles.p_t1; - p_f1 = handles.p_f1; - set(p_t1,'YData',tempData(:,chIdx)); - set(p_f1,'XData',f); - set(p_f1,'YData',m(:,chIdx)); - % Update effort bar - xT = mean(abs(tempData(:,chIdx))); - x = xT / effortMax; - set(handles.hPatch1,'Ydata',[0 x x 0]); - - chIdx=chIdx+1; - end - if nCh >= 3 - p_t2 = handles.p_t2; - p_f2 = handles.p_f2; - set(p_t2,'YData',tempData(:,chIdx)); - set(p_f2,'XData',f); - set(p_f2,'YData',m(:,chIdx)); - % Update effort bar - xT = mean(abs(tempData(:,chIdx))); - x = xT / effortMax; - set(handles.hPatch2,'Ydata',[0 x x 0]); - - chIdx=chIdx+1; - end - if nCh >= 4 - p_t3 = handles.p_t3; - p_f3 = handles.p_f3; - set(p_t3,'YData',tempData(:,chIdx)); - set(p_f3,'XData',f); - set(p_f3,'YData',m(:,chIdx)); - % Update effort bar - xT = mean(abs(tempData(:,chIdx))); - x = xT / effortMax; - set(handles.hPatch3,'Ydata',[0 x x 0]); - - chIdx=chIdx+1; - end - if nCh >= 5 - p_t4 = handles.p_t4; - set(p_t4,'YData',tempData(:,chIdx)); - % Update effort bar - xT = mean(abs(tempData(:,chIdx))); - x = xT / effortMax; - set(handles.hPatch4,'Ydata',[0 x x 0]); - - chIdx=chIdx+1; - %set(p_f4,'XData',f); - %set(p_f4,'YData',m(:,chIdx)); - end - if nCh >= 6 - p_t5 = handles.p_t5; - set(p_t5,'YData',tempData(:,chIdx)); - % Update effort bar - xT = mean(abs(tempData(:,chIdx))); - x = xT / effortMax; - set(handles.hPatch5,'Ydata',[0 x x 0]); - - chIdx=chIdx+1; - %set(p_f5,'XData',f); - %set(p_f5,'YData',m(:,chIdx)); - end - if nCh >= 7 - p_t6 = handles.p_t6; - set(p_t6,'YData',tempData(:,chIdx)); - % Update effort bar - xT = mean(abs(tempData(:,chIdx))); - x = xT / effortMax; - set(handles.hPatch6,'Ydata',[0 x x 0]); - - chIdx=chIdx+1; - %set(p_f6,'XData',f); - %set(p_f6,'YData',m(:,chIdx)); - end - if nCh >= 8 - p_t7 = handles.p_t7; - set(p_t7,'YData',tempData(:,chIdx)); - % Update effort bar - xT = mean(abs(tempData(:,chIdx))); - x = xT / effortMax; - set(handles.hPatch7,'Ydata',[0 x x 0]); - - %set(p_f7,'XData',f); - %set(p_f7,'YData',m(:,chIdx)); - end - - -end - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% ------------------- Function Description ------------------ +% Function to Record Exc Sessions +% +% --------------------------Updates-------------------------- +% 2009-04-17 / Max Ortiz / Creation +% 2009-06-29 / Max Ortiz / A dummy repeticion added before start recording +% 2011-06-00 / Per and Gustav / Added the analog front end sections +% 2011-06-29 / Max Ortiz / Optimization to be integrated in the whole system and Fixed to new coding standard. + % Any filtering was removed from this routine + % Filtering and any other signal processing should be done in + % a singal treatment routine +% 2011-08-04 / Max Ortiz / The 10% of extra indication for the user + % to contract was removed. +% 2012-02-xx / Max Ortiz / Upgrade DAQ routines for MATLAB R2011b, SBI + % Old version was kept as: + % RecordinSession_Legacy +% 2012-03-27 / Max Ortiz / Bug fixed when an arbitrary selection of channels + % However, the NI doesn't allow to skip channels +% 2012-04-30 / Max Ortiz / The possibility of simultaneous recordings was + % was removed since it didn't worked with the SBI + % in the current implementation. To see how the + % simultaneus recordings were done, see + % RecordingSession_Legacy +% 2012-12-07 / Nichlas Sander / Added VRE as an option during training. + % Movements to display are loaded in + % varargin{8}. This since previous movements + % were simply loaded as strings. +% 2013-03-07 / Nichlas Sander / When training Arm Flex/Extend the system + % will automatically change the view so the + % user can see the movement. +% 2013-08-23 / Morten Kristoffersen / Updated the recording sessions to support + % the new GUI_AFESelection structure +% 2014-11-10 / Enzo Mastinu / include routines for ADS1299 AFE recordings using + % the "afeSettings.name" property to differentiate them, + % add the ramp routines, set the time window + % size to be samplingTime/100, delete the + % peeking time field in the GUI, RMS mean is done + % "manually" for Matlab compatibility + % reasons +% 2015-01-12 / Enzo Mastinu / Divided the RecordingSession function into + % several functions: ConnectDevice(), + % SetDeviceStartAcquisition(), + % Acquire_tWs(), StopAcquisition(). + % These functions has been moved into COMM/AFE + % folder in separate files. All the parameters + % pass and global variables has been optimized + % due to the new functions. Now this file is + % totally independent from the inner working of + % the all devices. In this way, to introduce a + % new device for acquisition you need to modify + % the files in COMM folder, leaving the recording + % session file almost inalterate. +% 2015-01-26 / Enzo Mastinu / A new GUI_Recordings has been developed for the + % BioPatRec_TRE release. Now it is possible to + % plot more then 8 channels at the same moment, for + % time and frequency plots both. It is faster and + % perfectly compatible with the ramp recording + % session. At the end of the recording session it + % is possible to check all channels individually, + % apply offline data process as feature extraction or filter etc. + +% 20xx-xx-xx / Author / Comment + + + +function [cdata, sF] = RecordingSession(varargin) + + global handles; + global allData; + global timeStamps; + global samplesCounter; + allData = []; + nM = varargin{1}; + nR = varargin{2}; + cT = varargin{3}; + rT = varargin{4}; + mov = varargin{5}; + handles = varargin{6}; + afeSettings = varargin{7}; + trainWithVr = varargin{8}; + vreMovements = varargin{9}; + vreLeftHand = varargin{10}; + rampStatus = varargin{11}; + + % Get required informations from afeSettings structure + nCh = afeSettings.channels; + sF = afeSettings.sampleRate; + deviceName = afeSettings.name; + ComPortType = afeSettings.ComPortType; + if strcmp(ComPortType, 'COM') + ComPortName = afeSettings.ComPortName; + end + + % Save back acquisition parameters to the handles + handles.nR = nR; + handles.cT = cT; + handles.rT = rT; + handles.nCh = nCh; + handles.sF = sF; + handles.ComPortType = ComPortType; + if strcmp(ComPortType, 'COM') + handles.ComPortName = ComPortName; + end + handles.deviceName = deviceName; + handles.rampStatus = rampStatus; + handles.fast = 0; + + % Initialization of sampling time + sTall = (cT+rT)*nR; + sT = cT+rT; + handles.sTall = sTall; + handles.sT = sT; + + % Setting for data peeking + tW = sT/100; % Time window size + tWs = tW*sF; % Time window samples + handles.tWs = tWs; + timeStamps = 0:1/sF:tW-1/sF; % Create vector of time + + + %% Initialize GUI.. + + % Initialization of the VRE for training if required + if trainWithVr + % Parameters for the movement in the VRE + distance = 60; + numJumps = 30; + % Open and connect to the VRE + open('Virtual Reality.exe'); + handles.vreCommunication = tcpip('127.0.0.1',23068,'NetworkRole','server'); + fopen(handles.vreCommunication); + + %Set up VRE to not return any data. This will speed up the + %communication. + fwrite(handles.vreCommunication,sprintf('%c%c%c%c%c','c',char(7),char(0),char(0),char(0))); + + % Send value to show Arm if that value is chosen. + sent = 0; + for i = 1:nM + for j = 1:length(vreMovements{i}) + movement = vreMovements{i}{j}; + if ismember(movement.id,[20 21]) && sent == 0 + fwrite(handles.vreCommunication,sprintf('%c%c%c%c%c','c',char(6),char(1),char(0),char(0))); + sent = 1; + end + end + end + + if vreLeftHand + fwrite(handles.vreCommunication,sprintf('%c%c%c%c%c','c',char(5),char(1),char(0),char(0))); + end + end + + pause on; + + % Offset the data on plots + ampPP = 5; + ymin = -ampPP*2/3; + ymax = ampPP * nCh - ampPP*1/3; + sData = zeros(tWs,nCh); + fData = zeros(tWs,nCh); + offVector = 0:nCh-1; + offVector = offVector .* ampPP; + for i = 1 : nCh + sData(:,i) = sData(:,i) + offVector(i); + fData(:,i) = fData(:,i) + offVector(i); + end + + if rampStatus + rampParams = varargin{12}; + % make the ramp objects visible + set(handles.a_effortPlot,'Visible','on'); + set(handles.txt_effortPlot,'Visible','on'); + % Init the effort tracking plot and draw the guide line + p_effortPlot = plot(handles.a_effortPlot,linspace(0,cT,sF*cT),linspace(0,100,sF*cT),'LineWidth',2); + ylim(handles.a_effortPlot, [0 100]); + xlim(handles.a_effortPlot, [0 cT]); + handles.p_effortPlot = p_effortPlot; + axes(handles.a_effortPlot) + hLine = line('XData', 0, 'YData', 0, 'Color', 'r', 'Marker', 'o', 'MarkerSize', 8, 'LineWidth', 2); + handles.hLine = hLine; + end + + % Initialization of progress bar + xpatch = [0 0 0 0]; + ypatch = [0 0 1 1]; + axes(handles.a_prog); + handles.hPatch = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); + + % Allocation of resource to improve speed, total data + recSessionData = zeros(sF*sTall, nCh, nM); + + + %% Starting Session.. + + % Warning to the user + set(handles.t_msg,'String','Get ready to start: 3'); + pause(1); + set(handles.t_msg,'String','Get ready to start: 2'); + pause(1); + set(handles.t_msg,'String','Get ready to start: 1'); + pause(1); + relax = importdata('Img/relax.jpg'); % Import Image + drawnow; + + % Run all movements or exercise + for ex = 1 : nM + + timeStamps = 0:1/sF:tW-1/sF; % Timestamps used the time vector + currentTv = 1; % Current time vector + tV = timeStamps(currentTv):1/sF:(tW-1/sF)+timeStamps(currentTv); % Time vector used for drawing graphics + currentTv = currentTv - 1 + tWs; % Updated everytime tV is updated + acquireEvent.TimeStamps = tV'; + + if rampStatus == 1 + % Update ramp params for this exercise + handles.RampMin = rampParams{1}; + handles.currentRampMax = rampParams{2}(ex); + end + + disp(['Start ex: ' num2str(ex) ]) + + % Warning to the user + fileName = ['Img/' char(mov(ex)) '.jpg']; + if ~exist(fileName,'file') + fileName = 'Img/relax.jpg'; + end + + movI = importdata(fileName); % Import Image + set(handles.a_pic,'Visible','on'); % Turn on visibility + pic = image(movI,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + + % Show warning to prepare + set(handles.t_msg,'String',['Get ready for ' mov(ex) ' in 3 s']); + pause(1); + set(handles.t_msg,'String',['Get ready for ' mov(ex) ' in 2 s']); + pause(1); + set(handles.t_msg,'String',['Get ready for ' mov(ex) ' in 1 s']); + pause(1); + + % Dummy Contraction + set(handles.t_msg,'String',mov(ex)); + if afeSettings.prepare + if trainWithVr + numberOfMovements = length(vreMovements{ex}); + tempJumps = numJumps/numberOfMovements; + tempDistance = round(distance/tempJumps); + vreString = ''; + for i = 1:numberOfMovements + movement = vreMovements{ex}{i}; + vreString = sprintf('%s%c%c%c%c%c',vreString,char(1),char(movement.idVRE),char(movement.vreDir),char(tempDistance),char(1)); + end + for j = 1:tempJumps + fwrite(handles.vreCommunication,vreString); + end + end + + pause(cT); + set(handles.t_msg,'String','Relax'); + if trainWithVr + fwrite(handles.vreCommunication,sprintf(sprintf('%c%c%c%c%c','r',char(1),char(1),char(1),char(1)))); + end + pic = image(relax,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + pause(rT); + end + + % Draw figure + p_t0 = plot(handles.a_t0, timeStamps, sData); + handles.p_t0 = p_t0; + xlim(handles.a_t0, [0,tW]); + ylim(handles.a_t0, [ymin ymax]); + set(handles.a_t0,'YTick',offVector); + set(handles.a_t0,'YTickLabel',0:nCh-1); + p_f0 = plot(handles.a_f0, timeStamps, fData); + handles.p_f0 = p_f0; + xlim(handles.a_f0, [0,sF/2]); + ylim(handles.a_f0, [ymin ymax]); + set(handles.a_f0,'YTick',offVector); + set(handles.a_f0,'YTickLabel',0:nCh-1); + + % Repetitions + %%%%% NI DAQ card %%%%% + if strcmp (ComPortType, 'NI') + + % Init SBI + sCh = 1:nCh; + s = InitSBI_NI(sF,sTall,sCh); + s.NotifyWhenDataAvailableExceeds = tWs; % PEEK time + lh = s.addlistener('DataAvailable', @RecordingSession_ShowData); + + % Start DAQ + cData = zeros(sF*sTall, nCh, nM); + s.startBackground(); % Run in the backgroud + + for rep = 1 : nR + handles.rep = rep; + % Contraction + set(handles.t_msg,'String',mov(ex)); + pic = image(movI,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + handles.contraction = 1; + startContractionTic = tic; + if trainWithVr + numberOfMovements = length(vreMovements{ex}); + tempJumps = numJumps/numberOfMovements; + tempDistance = round(distance/tempJumps); + vreString = ''; + for i = 1:numberOfMovements + movement = vreMovements{ex}{i}; + vreString = sprintf('%s%c%c%c%c%c',vreString,char(1),char(movement.idVRE),char(movement.vreDir),char(tempDistance),char(1)); + end + for j = 1:tempJumps + fwrite(handles.vreCommunication,vreString); + end + end + pause(cT - toc(startContractionTic)); + % Relax + set(handles.t_msg,'String','Relax'); + startRelaxingTic = tic; + if trainWithVr + for i = 1:numJumps + fwrite(handles.vreCommunication,sprintf(sprintf('%c%c%c%c%c',char(1),char(14),char(1),char(distance/numJumps),char(1)))); + end + end + pic = image(relax,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + handles.contraction = 0; + pause(rT - toc(startRelaxingTic)); + end + + % Repetitions other devices + else + + % Connect the chosen device, it returns the connection object + obj = ConnectDevice(handles); + + % Set the selected device and Start the acquisition + SetDeviceStartAcquisition(handles, obj); + + for rep = 1 : nR + + rep + handles.rep = rep; + handles.contraction = 1; % 1 means contraction, 0 means relaxation + samplesCounter = 1; % variable used to track the progressing time of the recording session + UpdateGUI = 1; + cData = zeros(tWs, nCh); + + tic + for timeWindowNr = 1:sT/tW + + cData = Acquire_tWs(deviceName, obj, nCh, tWs); % acquire a new time window of samples + acquireEvent.Data = cData; + RecordingSession_ShowData(0, acquireEvent); % plot data and add cData to allData vector + + samplesCounter = samplesCounter + tWs; + switch handles.contraction + case 1 + % CONTRACTION + if (samplesCounter/sF) <= cT + % contraction time not expired yet + if UpdateGUI == 1 + % new contraction, the GUI must be updated + set(handles.t_msg,'String',mov(ex)); + pic = image(movI,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + drawnow; + if trainWithVr + numberOfMovements = length(vreMovements{ex}); + tempJumps = numJumps/numberOfMovements; + tempDistance = round(distance/tempJumps); + vreString = ''; + for i = 1:numberOfMovements + movement = vreMovements{ex}{i}; + vreString = sprintf('%s%c%c%c%c%c',vreString,char(1),char(movement.idVRE),char(movement.vreDir),char(tempDistance),char(1)); + end + for j = 1:tempJumps + fwrite(handles.vreCommunication,vreString); + end + end + UpdateGUI = 0; + end + else + % contraction time expired + handles.contraction = 0; % 1 means contraction, 0 means relaxation + UpdateGUI = 1; + samplesCounter = 1; + end + case 0 + % RELAXATION + if (samplesCounter/sF) <= rT + % relaxation time not expired yet + if UpdateGUI == 1 + % new relaxation, the GUI must be updated + set(handles.t_msg,'String','Relax'); + pic = image(relax,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + drawnow; + if trainWithVr + for i = 1:numJumps + fwrite(handles.vreCommunication,sprintf(sprintf('%c%c%c%c%c',char(1),char(14),char(1),char(distance/numJumps),char(1)))); + end + end + UpdateGUI = 0; + end + else + % relaxation time expired + handles.contraction = 1; % 1 means contraction, 0 means relaxation + if rep ~= nR + UpdateGUI = 1; + end + samplesCounter = 1; + end + otherwise + set(handles.t_msg,'String','Error with the Contraction-Relaxation switch statement'); + end + end + toc + end + + % Stop acquisition + StopAcquisition(deviceName, obj); + end + + % NI DAQ card: "You must delete the listener once the operation is complete" + if strcmp(ComPortType,'NI'); + if ~s.IsDone % check if is done + s.wait(); + end + delete(lh); + end + + % Save Data + recSessionData(:,:,ex) = allData(:,:); + % Plot movement just recorded + DataShow(handles, allData, sF, sTall); + % Clean global data for next movement + allData = []; + + end + + + %% Session finish.. + + % Save data into cdata output matrix + cdata = recSessionData(:,:,:); + + set(handles.t_msg,'String','Session Terminated'); % Show message about acquisition completed + fileName = 'Img/Agree.jpg'; + movI = importdata(fileName); % Import Image + set(handles.a_pic,'Visible','on'); % Turn on visibility + pic = image(movI,'Parent',handles.a_pic); % set image + axis(handles.a_pic,'off'); % Remove axis tick marks + + % Save Session to file + recSession.sF = sF; + recSession.sT = sTall; + recSession.cT = cT; + recSession.rT = rT; + recSession.nM = nM; + recSession.nR = nR; + recSession.nCh = nCh; + recSession.dev = deviceName; + recSession.comm = ComPortType; + if strcmp(ComPortType, 'COM') + recSession.comn = ComPortName; + end + recSession.mov = mov; + recSession.date = fix(clock); + recSession.cmt = inputdlg('Additional comment on the recording session','Comments'); + if rampStatus + recSession.ramp.rampMin = rampParams{1}; % Save the ramp parameters to the file + recSession.ramp.rampMax = rampParams{2}; + recSession.ramp.minData = rampParams{3}; + recSession.ramp.maxData = rampParams{4}; + end + + [filename, pathname] = uiputfile({'*.mat','MAT-files (*.mat)'},'Save as', 'Untitled.mat'); + if isequal(filename,0) || isequal(pathname,0) + disp('User pressed cancel') + else + disp(['User selected ', fullfile(pathname, filename)]) + recSession.tdata = recSessionData; + save([pathname,filename],'recSession'); + end + disp(recSession); + + if trainWithVr + fclose(handles.vreCommunication); + end + + % Turn OFF visibility + set(handles.a_prog,'visible','off'); + if(rampStatus) + set(handles.a_effortPlot,'visible','off'); + set(handles.txt_effortPlot,'visible','off'); + set(p_effortPlot,'visible','off'); + set(handles.hLine,'visible','off'); + end + set(handles.hPatch,'Xdata',[0 0 0 0]); + + % Set visible the offline plot and process panels + set(handles.uipanel9,'Visible','on'); + set(handles.uipanel7,'Visible','on'); + set(handles.uipanel8,'Visible','on'); + set(handles.txt_it,'visible','on'); + set(handles.txt_ft,'visible','on'); + set(handles.et_it,'visible','on'); + set(handles.et_ft,'visible','on'); + set(handles.txt_if,'visible','on'); + set(handles.txt_ff,'visible','on'); + set(handles.et_if,'visible','on'); + set(handles.et_ff,'visible','on'); + + chVector = 0:nCh-1; + set(handles.lb_channels, 'String', chVector); + +end diff --git a/SigRecordings/RecordingSession_Legacy.m b/SigRecordings/RecordingSession_Legacy.m index f03988e..0eaf3b0 100644 --- a/SigRecordings/RecordingSession_Legacy.m +++ b/SigRecordings/RecordingSession_Legacy.m @@ -1,657 +1,657 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to Record Exc Sessions -% Input : -% Fs = Sampling frequenicy -% nM = number of excersices or movements -% nR = number of excersice repetition -% cT = time that the contractions should last -% rT = relaxing time -% cTp = Porcentage of the signal to record -% mov = message to be send to the user -% hGUI_Rec= handles of the axes to plot -% Output = total data and data of interest -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-04-17 / Max Ortiz / Creation -% 2009-06-29 / Max Ortiz / A dummy repeticion added before start recording -% 2011-06 / Per and Gustav / added the analog front end sections -% 2011-06-29 / Max ORtiz / Optimization to be integrated in the whole system and Fixed to new coding standard. - % Any filgering was removed from this routine - % Filtering and any other signal processing should be done in - % a singal treatment routine -% 2011-08-04 / Max Ortiz / % The 10% of extra indication for the user to contract was - % removed. - - - -function [cdata, sF] = RecordingSession_Legacy(varargin) - -nM = varargin{1}; -nR = varargin{2}; -cT = varargin{3}; -rT = varargin{4}; -mov = varargin{5}; -hGUI_Rec = varargin{6}; -afeSettings = varargin{7}; - -% Get number of channels -if afeSettings.NI.show - nCh = afeSettings.NI.channels; - sF = afeSettings.NI.sampleRate; -elseif afeSettings.ADS.show - nCh = afeSettings.ADS.channels; - sF = afeSettings.ADS.sampleRate; -elseif afeSettings.RHA.show - nCh = afeSettings.RHA.channels; - sF = afeSettings.RHA.sampleRate; -end - -%% Initialize plots -% Create handles for the plots -% this is faster than creating the plot everytime -tt = 0:1/sF:cT/100-1/sF; -ymin = -3; -ymax = 3; - - -if nCh >= 1 - %axes(hGUI_Rec.a_t0); - p_t0 = plot(hGUI_Rec.a_t0,tt,tt); - ylim(hGUI_Rec.a_t0, [ymin ymax]); - %axes(hGUI_Rec.a_f0); - p_f0 = plot(hGUI_Rec.a_f0,1,1); -end -if nCh >= 2 - p_t1 = plot(hGUI_Rec.a_t1,tt,tt); - ylim(hGUI_Rec.a_t1, [ymin ymax]); - p_f1 = plot(hGUI_Rec.a_f1,1,1); -end -if nCh >= 3 - p_t2 = plot(hGUI_Rec.a_t2,tt,tt); - ylim(hGUI_Rec.a_t2, [ymin ymax]); - p_f2 = plot(hGUI_Rec.a_f2,1,1); -end - -if nCh >= 4 - p_t3 = plot(hGUI_Rec.a_t3,tt,tt); - ylim(hGUI_Rec.a_t3, [ymin ymax]); - p_f3 = plot(hGUI_Rec.a_f3,1,1); -end - -if nCh >= 5 - p_t4 = plot(hGUI_Rec.a_t4,tt,tt); - ylim(hGUI_Rec.a_t4, [ymin ymax]); - %axes(hGUI_Rec.a_f4); - %p_f4 = plot(1,1); -end - -if nCh >= 6 - p_t5 = plot(hGUI_Rec.a_t5,tt,tt); - ylim(hGUI_Rec.a_t5, [ymin ymax]); - %axes(hGUI_Rec.a_f5); - %p_f5 = plot(1,1); -end - -if nCh >= 7 - p_t6 = plot(hGUI_Rec.a_t6,tt,tt); - ylim(hGUI_Rec.a_t6, [ymin ymax]); - %axes(hGUI_Rec.a_f6); - %p_f6 = plot(1,1); -end - -if nCh >= 8 - p_t7 = plot(hGUI_Rec.a_t7,tt,tt); - ylim(hGUI_Rec.a_t7, [ymin ymax]); - %axes(hGUI_Rec.a_f7); - %p_f7 = plot(1,1); -end - - -%% -% Initialize DAQ card -sT = (cT+rT)*nR; % Sampling time, it is the time of contraction + - % Time of relaxation x Number of repetitions - - -if afeSettings.NI.active - %afeSettings.NI.sampleRate=sF; %overrides the individual samplerate choise in AFS_select - % instruction from Per and Gustav, why - % would you overwrite the selected sF? - - % All following sF needs to be changed to - % invidual sF in order to make it work - % /Per - ai = Init_NI_AI(hGUI_Rec,afeSettings.NI.sampleRate,sT,nCh); - dev = afeSettings.NI.name; -end -if afeSettings.ADS.active - Amp=12; - Vref=2.4*2; %*2 is from bipolar reference - ByteDepth=3; - afeSettings.ADS.sampleRate=sF; %overrides the individual samplerate choise in AFS_select - dataFormat=2; - ADS = AFE_PICCOLO(afeSettings.ADS.ComPortType,afeSettings.ADS.sampleRate, ... - sT,nCh,Amp,Vref,dataFormat,afeSettings.ADS.name,ByteDepth); -end -if afeSettings.RHA.active - Amp=200; - Vref=2.5; - ByteDepth=2; - afeSettings.RHA.sampleRate=sF; %overrides the individual samplerate choise in AFS_select - dataFormat=1; - RHA = AFE_PICCOLO(afeSettings.RHA.ComPortType,afeSettings.RHA.sampleRate, ... - sT,nCh,Amp,Vref,dataFormat,afeSettings.RHA.name,ByteDepth); -end - -%% Initialization of progress bar -xpatch = [0 0 0 0]; -ypatch = [0 0 1 1]; -%set(hGUI_Rec.figure1,'CurrentAxes',hGUI_Rec.a_prog); -axes(hGUI_Rec.a_prog); -hGUI_Rec.hPatch = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); - -xpatch = [1 1 0 0]; -ypatch = [0 0 0 0]; -axes(hGUI_Rec.a_effort0); -hGUI_Rec.hPatch0 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(hGUI_Rec.a_effort1); -hGUI_Rec.hPatch1 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(hGUI_Rec.a_effort1); -hGUI_Rec.hPatch1 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(hGUI_Rec.a_effort2); -hGUI_Rec.hPatch2 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(hGUI_Rec.a_effort3); -hGUI_Rec.hPatch3 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(hGUI_Rec.a_effort4); -hGUI_Rec.hPatch4 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(hGUI_Rec.a_effort5); -hGUI_Rec.hPatch5 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(hGUI_Rec.a_effort6); -hGUI_Rec.hPatch6 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -axes(hGUI_Rec.a_effort7); -hGUI_Rec.hPatch7 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); -effortMax = 2; - -%% -% Allocation of resource to improve speed, total data - % Identified problem if the different devices have different sF - % then different amount of data is comming from the differnt devices - % also problem in the counting of incoming data, needs to be changed so it - % expects right amount of data /Per -tdata = zeros(sF*sT,nCh,nM); -ADStdata = zeros(sF*sT,nCh,nM); -RHAtdata = zeros(sF*sT,nCh,nM); - -% Warning to the user -if afeSettings.prepare - set(hGUI_Rec.t_msg,'String','Get ready to start: 3'); - pause(1); - set(hGUI_Rec.t_msg,'String','Get ready to start: 2'); - pause(1); - set(hGUI_Rec.t_msg,'String','Get ready to start: 1'); - pause(1); -end -relax = importdata('Img/relax.jpg'); % Import Image - - -for ex = 1 : nM - disp(['Start ex: ' num2str(ex) ]) - - % Warning to the user - fileName = ['Img/' char(mov(ex)) '.jpg']; - if ~exist(fileName,'file') - fileName = 'Img/relax.jpg'; - end - - %movI = importdata(['Img/mov' num2str(ex) '.jpg']); % Import Image - movI = importdata(fileName); % Import Image - set(hGUI_Rec.a_pic,'Visible','on'); % Turn on visibility - %axes(hGUI_Rec.a_pic); % get hGUI_Rec - pic = image(movI,'Parent',hGUI_Rec.a_pic); % set image - axis(hGUI_Rec.a_pic,'off'); % Remove axis tick marks - - if afeSettings.prepare - set(hGUI_Rec.t_msg,'String',['Get ready for ' mov(ex) ' in 3 s']); - pause(1); - set(hGUI_Rec.t_msg,'String',['Get ready for ' mov(ex) ' in 2 s']); - pause(1); - set(hGUI_Rec.t_msg,'String',['Get ready for ' mov(ex) ' in 1 s']); - %set(hGUI_Rec.a_pic,'Visible','off'); % Turn OFF visibility - %delete(pic); % Delete image - pause(1); - end - - %% Dummy Contraction - set(hGUI_Rec.t_msg,'String',mov(ex)); - if afeSettings.prepare - pause(cT); - set(hGUI_Rec.t_msg,'String','Relax'); - pic = image(relax,'Parent',hGUI_Rec.a_pic); % set image - axis off; % Remove axis tick marks - pause(rT); - end - - - %% Start DAQ - if afeSettings.NI.active - start(ai); - end - if afeSettings.ADS.active - ADS.startRecording - end - if afeSettings.RHA.active - RHA.startRecording - end - - - for rep = 1 : nR - - while afeSettings.NI.active && afeSettings.NI.show && (ai.SamplesAcquired < (cT+rT)*sF*rep) || ... - afeSettings.ADS.active && afeSettings.ADS.show && (ADS.SamplesAcquired < (cT+rT)*sF*rep*nCh) || ... - afeSettings.RHA.active && afeSettings.RHA.show && (RHA.SamplesAcquired < (cT+rT)*sF*rep*nCh) - - %sF in both condition needs to be changed to the invidual samplings - %frequencies of the different devices - - %Instructions to the user - if afeSettings.NI.active && afeSettings.NI.show && (ai.SamplesAcquired <= sF*cT*rep + sF*rT*(rep-1)) || ... - afeSettings.ADS.active && afeSettings.ADS.show && (ADS.SamplesAcquired <= (sF*cT*rep + sF*rT*(rep-1)) * nCh) || ... - afeSettings.RHA.active && afeSettings.RHA.show && (RHA.SamplesAcquired <= (sF*cT*rep + sF*rT*(rep-1)) * nCh) - - set(hGUI_Rec.t_msg,'String',mov(ex)); - %axes(hGUI_Rec.a_pic); - pic = image(movI,'Parent',hGUI_Rec.a_pic); % set image - axis(hGUI_Rec.a_pic,'off'); % Remove axis tick marks - - % Status bar update - lastToc = sF*cT; - thisToc = ai.SamplesAcquired - (sF*cT*(rep-1)) - sF*rT*(rep-1); - else - set(hGUI_Rec.t_msg,'String','Relax'); - pic = image(relax,'Parent',hGUI_Rec.a_pic); % set image - axis(hGUI_Rec.a_pic,'off'); % Remove axis tick marks - - % Status bar update - lastToc = sF*rT; - thisToc = ai.SamplesAcquired - (sF*cT*rep) - sF*rT*(rep-1); - - end - % Status bar update - x =1-(thisToc/lastToc); - set(hGUI_Rec.figure1,'CurrentAxes',hGUI_Rec.a_prog); - set(hGUI_Rec.hPatch,'Xdata',[0 x x 0]); - drawnow; - - %--------------------------------- - if afeSettings.NI.show - data = peekdata(ai,cT*sF/100); - %data=data/1700; %Converting to input referred voltage - elseif afeSettings.ADS.show - data = ADS.storeFromBuffer; - - ADS.data = [ADS.data ; data]; - - if size(ADS.data,1) > (cT*sF/100) - data = (ADS.data(end-(cT*sF/100)+1:end,:) ).* (ADS.vref/(ADS.amplification*2^(ADS.byteDepth*8) )); %Converting to input referred voltage - else - data = zeros((cT*sF/100),nCh); - end - elseif afeSettings.RHA.show - data = RHA.storeFromBuffer; - - RHA.data = [RHA.data ; data]; - if size(RHA.data,1) > (cT*sF/100) - data = (RHA.data(end-(cT*sF/100)+1:end,:) ).* (RHA.vref/(RHA.amplification*2^(RHA.byteDepth*8))); %Converting to input referred voltage - else - data = zeros((cT*sF/100),nCh); - end - end - - aNs = length(data(:,1)); - NFFT = 2^nextpow2(aNs); % Next power of 2 from number of samples - f = sF/2*linspace(0,1,NFFT/2); - dataf = fft(data(1:aNs,:),NFFT)/aNs; - m = 2*abs(dataf((1:NFFT/2),:)); - - - chi = 1;%Channel Index for map data - if nCh >= 1 - set(p_t0,'YData',data(:,chi)); - set(p_f0,'XData',f); - set(p_f0,'YData',m(:,chi)); - % Update effort bar - xT = mean(abs(data(:,chi))); - x = xT / effortMax; - set(hGUI_Rec.hPatch0,'Ydata',[0 x x 0]); - - chi=chi+1; - end - if nCh >= 2 - set(p_t1,'YData',data(:,chi)); - set(p_f1,'XData',f); - set(p_f1,'YData',m(:,chi)); - % Update effort bar - xT = mean(abs(data(:,chi))); - x = xT / effortMax; - set(hGUI_Rec.hPatch1,'Ydata',[0 x x 0]); - - chi=chi+1; - end - if nCh >= 3 - set(p_t2,'YData',data(:,chi)); - set(p_f2,'XData',f); - set(p_f2,'YData',m(:,chi)); - % Update effort bar - xT = mean(abs(data(:,chi))); - x = xT / effortMax; - set(hGUI_Rec.hPatch2,'Ydata',[0 x x 0]); - - chi=chi+1; - end - if nCh >= 4 - set(p_t3,'YData',data(:,chi)); - set(p_f3,'XData',f); - set(p_f3,'YData',m(:,chi)); - % Update effort bar - xT = mean(abs(data(:,chi))); - x = xT / effortMax; - set(hGUI_Rec.hPatch3,'Ydata',[0 x x 0]); - - chi=chi+1; - end - if nCh >= 5 - set(p_t4,'YData',data(:,chi)); - % Update effort bar - xT = mean(abs(data(:,chi))); - x = xT / effortMax; - set(hGUI_Rec.hPatch4,'Ydata',[0 x x 0]); - - chi=chi+1; - %set(p_f4,'XData',f); - %set(p_f4,'YData',m(:,chi)); - end - if nCh >= 6 - set(p_t5,'YData',data(:,chi)); - % Update effort bar - xT = mean(abs(data(:,chi))); - x = xT / effortMax; - set(hGUI_Rec.hPatch5,'Ydata',[0 x x 0]); - - chi=chi+1; - %set(p_f5,'XData',f); - %set(p_f5,'YData',m(:,chi)); - end - if nCh >= 7 - set(p_t6,'YData',data(:,chi)); - % Update effort bar - xT = mean(abs(data(:,chi))); - x = xT / effortMax; - set(hGUI_Rec.hPatch6,'Ydata',[0 x x 0]); - - chi=chi+1; - %set(p_f6,'XData',f); - %set(p_f6,'YData',m(:,chi)); - end - if nCh >= 8 - set(p_t7,'YData',data(:,chi)); - % Update effort bar - xT = mean(abs(data(:,chi))); - x = xT / effortMax; - set(hGUI_Rec.hPatch7,'Ydata',[0 x x 0]); - - %set(p_f7,'XData',f); - %set(p_f7,'YData',m(:,chi)); - end - - - %--------------------------------- - end - - end - - %Check if all data has arrived from the other devices - while (afeSettings.NI.active && (ai.SamplesAcquired < (cT+rT)*sF*nR)) || ... - (afeSettings.ADS.active && (ADS.SamplesAcquired < (cT+rT)*sF*nR*nCh)) || ... - (afeSettings.RHA.active && (RHA.SamplesAcquired < (cT+rT)*sF*nR*nCh)) - - pause(1) - disp('Waiting for more data') - if afeSettings.NI.active - disp('NI') - disp((cT+rT)*sF*nR-ai.SamplesAcquired) - disp([ai.SamplesAcquired ai.SamplesAcquired*4]) - end - if afeSettings.RHA.active - disp('RHA') - disp((cT+rT)*sF*nR*nCh-RHA.SamplesAcquired) - disp(RHA.SamplesAcquired) - end - if afeSettings.ADS.active - disp('ADS') - disp((cT+rT)*sF*nR*nCh-ADS.SamplesAcquired) - disp(ADS.SamplesAcquired) - disp((cT+rT)*sF*nR*nCh) - end - end - - % Save Data - if afeSettings.NI.active -% disp('NI:') -% ai.SamplesAcquired - wait(ai,sT+1); - [data,tt,abstime] = getdata(ai); -% disp(['date = ' num2str(fix(abstime))]) -% size(data) -% size(tdata) - tdata(:,:,ex) = data; % / 1.70e3; %Converting to input referred voltage - end - if afeSettings.RHA.active -% disp('RHA:') -% disp(RHA.SamplesAcquired) -% disp(RHA.bytesAcquired) -% disp(size(RHA.data)) - data = [RHA.data ; RHA.storeFromBuffer]; -% disp(size(data)) -% disp(size(RHAtdata)) - RHAtdata(:,:,ex)=data * (RHA.vref/(RHA.amplification*2^(RHA.byteDepth*8))); %Converting to input referred voltage - end - - if afeSettings.ADS.active -% disp('ADS:') -% disp(ADS.SamplesAcquired) -% disp(ADS.bytesAcquired) -% disp(size(data)) - - data = [ADS.data ; ADS.storeFromBuffer]; -% disp(size(data)) -% disp(size(ADStdata)) - ADStdata(:,:,ex)=data * (ADS.vref/(ADS.amplification*2^(ADS.byteDepth*8))); %Converting to input referred voltage - end - - -end -set(hGUI_Rec.t_msg,'String','Session Terminated'); % Show message about acquisition - -%% Save data and compute training data using cTp -if afeSettings.NI.active - stop(ai); - delete(ai); - NItdata=tdata; - %NItrdata=ComputeTrainingData(tdata,sF,cT,rT,nR,nM,cTp); -else - NItdata=[]; - %NItrdata=[]; -end - -if afeSettings.ADS.active - ADStdata=ADStdata; - %ADStrdata=ComputeTrainingData(ADStdata,sF,cT,rT,nR,nM,cTp); -else - ADStdata=[]; - %ADStrdata=[]; -end - -if afeSettings.RHA.active - RHAtdata=RHAtdata; - %RHAtrdata=ComputeTrainingData(RHAtdata,sF,cT,rT,nR,nM,cTp); -else - RHAtdata=[]; - %RHAtrdata=[]; -end - - -%% Save Session to file -% date = [];%fix(abstime); -recSession.sF = sF; -recSession.sT = sT; -recSession.cT = cT; -recSession.rT = rT; -%recSession.cTp = cTp; -recSession.nM = nM; -recSession.nR = nR; -recSession.nCh = nCh; -recSession.dev = dev; -recSession.mov = mov; -recSession.date = fix(clock); -recSession.cmt = inputdlg('Additional comment on the recording session','Comments'); - -[filename, pathname] = uiputfile({'*.mat','MAT-files (*.mat)'},'Save as', 'Untitled.mat'); - if isequal(filename,0) || isequal(pathname,0) - disp('User pressed cancel') - else - disp(['User selected ', fullfile(pathname, filename)]) - if afeSettings.NI.active - recSession.tdata = NItdata; - %recSession.trdata = NItrdata; - if exist([pathname 'NI\'],'dir') == 0 - mkdir(pathname,'NI') - end - save([pathname, 'NI\',filename],'recSession'); - end - if afeSettings.ADS.active - recSession.tdata = ADStdata; - %recSession.trdata = ADStrdata; - %Possible to add string with device info for example recSession.device=afeSettings.ADS.name; - if exist([pathname 'ADS1298\'],'dir') == 0 - mkdir(pathname,'ADS1298') - end - save([pathname, 'ADS1298\',filename],'recSession'); - - % not needed here any more? -% if filters.PLH || filters.BP -% recSession.tdata = ADStdataFiltered; -% % recSession.trdata = ADStrdataFiltered; -% if exist([pathname 'ADS1298Filtered\'],'dir') == 0 -% mkdir(pathname,'ADS1298Filtered') -% end -% save([pathname,'ADS1298Filtered\',filename],'recSession'); -% end - end - if afeSettings.RHA.active - recSession.tdata = RHAtdata; - %recSession.trdata = RHAtrdata; - %Possible to add string with device info for example recSession.device=afeSettings.RHA.name; - if exist([pathname 'RHA2216\'],'dir') == 0 - mkdir(pathname,'RHA2216') - end - save([pathname 'RHA2216\',filename],'recSession'); - - % not needed here any more? -% if filters.PLH || filters.BP -% recSession.tdata = RHAtdataFiltered; -% %recSession.trdata = RHAtrdataFiltered; -% if exist([pathname 'RHA2216Filtered\'],'dir') == 0 -% mkdir(pathname,'RHA2216Filtered') -% end -% save([pathname,'RHA2216Filtered\',filename],'recSession'); -% end - end -end - -% Copy acquired data from the last excersice into cdata -% Display it -disp(recSession); - -if afeSettings.NI.show - data = NItdata(:,:,end); -elseif afeSettings.ADS.show - data = ADStdata(:,:,end).* (ADS.vref/(ADS.amplification*2^(ADS.byteDepth*8))); -elseif afeSettings.RHA.show - data = RHAtdata(:,:,end).* (RHA.vref/(RHA.amplification*2^(RHA.byteDepth*8))); -end - -chi=1; -if nCh >= 1 - cdata(:,1) = data(:,chi); - chi=chi+1; -end -if nCh >= 2 - cdata(:,2) = data(:,chi); - chi=chi+1; -end -if nCh >= 3 - cdata(:,3) = data(:,chi); - chi=chi+1; -end -if nCh >= 4 - cdata(:,4) = data(:,chi); - chi=chi+1; -end -if nCh >= 5 - cdata(:,5) = data(:,chi); - chi=chi+1; -end -if nCh >= 6 - cdata(:,6) = data(:,chi); - chi=chi+1; -end -if nCh >= 7 - cdata(:,7) = data(:,chi); - chi=chi+1; -end -if nCh >= 8 - cdata(:,8) = data(:,chi); -end - - -data_show(hGUI_Rec,cdata,sF,sT); -set(hGUI_Rec.a_pic,'Visible','off'); % Turn OFF visibility -delete(pic); % Delete image -end - -% function trdata = ComputeTrainingData(tdata,sF,cT,rT,nR,nM,cTp) -% %Compute Traning Data -% -% for ex = 1 : nM -% tempdata =[]; -% for rep = 1 : nR -% % Samples of the exersice to be consider for training -% % (sF*cT*cTp) Number of the samples that wont be consider for training -% % (sF*cT*rep) Number of samples that takes a contraction -% % (sF*rT*rep) Number of samples that takes a relaxation -% is = (sF*cT*cTp) + (sF*cT*(rep-1)) + (sF*rT*(rep-1)) + 1; -% fs = (sF*cT) + (sF*cT*(rep-1)) + (sF*rT*(rep-1)); -% tempdata = [tempdata ; tdata(is:fs,:,ex)]; -% end -% trdata(:,:,ex) = tempdata; -% end -% +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to Record Exc Sessions +% Input : +% Fs = Sampling frequenicy +% nM = number of excersices or movements +% nR = number of excersice repetition +% cT = time that the contractions should last +% rT = relaxing time +% cTp = Porcentage of the signal to record +% mov = message to be send to the user +% hGUI_Rec= handles of the axes to plot +% Output = total data and data of interest +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-04-17 / Max Ortiz / Creation +% 2009-06-29 / Max Ortiz / A dummy repeticion added before start recording +% 2011-06 / Per and Gustav / added the analog front end sections +% 2011-06-29 / Max ORtiz / Optimization to be integrated in the whole system and Fixed to new coding standard. + % Any filgering was removed from this routine + % Filtering and any other signal processing should be done in + % a singal treatment routine +% 2011-08-04 / Max Ortiz / % The 10% of extra indication for the user to contract was + % removed. + + + +function [cdata, sF] = RecordingSession_Legacy(varargin) + +nM = varargin{1}; +nR = varargin{2}; +cT = varargin{3}; +rT = varargin{4}; +mov = varargin{5}; +hGUI_Rec = varargin{6}; +afeSettings = varargin{7}; + +% Get number of channels +if afeSettings.NI.show + nCh = afeSettings.NI.channels; + sF = afeSettings.NI.sampleRate; +elseif afeSettings.ADS.show + nCh = afeSettings.ADS.channels; + sF = afeSettings.ADS.sampleRate; +elseif afeSettings.RHA.show + nCh = afeSettings.RHA.channels; + sF = afeSettings.RHA.sampleRate; +end + +%% Initialize plots +% Create handles for the plots +% this is faster than creating the plot everytime +tt = 0:1/sF:cT/100-1/sF; +ymin = -3; +ymax = 3; + + +if nCh >= 1 + %axes(hGUI_Rec.a_t0); + p_t0 = plot(hGUI_Rec.a_t0,tt,tt); + ylim(hGUI_Rec.a_t0, [ymin ymax]); + %axes(hGUI_Rec.a_f0); + p_f0 = plot(hGUI_Rec.a_f0,1,1); +end +if nCh >= 2 + p_t1 = plot(hGUI_Rec.a_t1,tt,tt); + ylim(hGUI_Rec.a_t1, [ymin ymax]); + p_f1 = plot(hGUI_Rec.a_f1,1,1); +end +if nCh >= 3 + p_t2 = plot(hGUI_Rec.a_t2,tt,tt); + ylim(hGUI_Rec.a_t2, [ymin ymax]); + p_f2 = plot(hGUI_Rec.a_f2,1,1); +end + +if nCh >= 4 + p_t3 = plot(hGUI_Rec.a_t3,tt,tt); + ylim(hGUI_Rec.a_t3, [ymin ymax]); + p_f3 = plot(hGUI_Rec.a_f3,1,1); +end + +if nCh >= 5 + p_t4 = plot(hGUI_Rec.a_t4,tt,tt); + ylim(hGUI_Rec.a_t4, [ymin ymax]); + %axes(hGUI_Rec.a_f4); + %p_f4 = plot(1,1); +end + +if nCh >= 6 + p_t5 = plot(hGUI_Rec.a_t5,tt,tt); + ylim(hGUI_Rec.a_t5, [ymin ymax]); + %axes(hGUI_Rec.a_f5); + %p_f5 = plot(1,1); +end + +if nCh >= 7 + p_t6 = plot(hGUI_Rec.a_t6,tt,tt); + ylim(hGUI_Rec.a_t6, [ymin ymax]); + %axes(hGUI_Rec.a_f6); + %p_f6 = plot(1,1); +end + +if nCh >= 8 + p_t7 = plot(hGUI_Rec.a_t7,tt,tt); + ylim(hGUI_Rec.a_t7, [ymin ymax]); + %axes(hGUI_Rec.a_f7); + %p_f7 = plot(1,1); +end + + +%% +% Initialize DAQ card +sT = (cT+rT)*nR; % Sampling time, it is the time of contraction + + % Time of relaxation x Number of repetitions + + +if afeSettings.NI.active + %afeSettings.NI.sampleRate=sF; %overrides the individual samplerate choise in AFS_select + % instruction from Per and Gustav, why + % would you overwrite the selected sF? + + % All following sF needs to be changed to + % invidual sF in order to make it work + % /Per + ai = Init_NI_AI(hGUI_Rec,afeSettings.NI.sampleRate,sT,nCh); + dev = afeSettings.NI.name; +end +if afeSettings.ADS.active + Amp=12; + Vref=2.4*2; %*2 is from bipolar reference + ByteDepth=3; + afeSettings.ADS.sampleRate=sF; %overrides the individual samplerate choise in AFS_select + dataFormat=2; + ADS = AFE_PICCOLO(afeSettings.ADS.ComPortType,afeSettings.ADS.sampleRate, ... + sT,nCh,Amp,Vref,dataFormat,afeSettings.ADS.name,ByteDepth); +end +if afeSettings.RHA.active + Amp=200; + Vref=2.5; + ByteDepth=2; + afeSettings.RHA.sampleRate=sF; %overrides the individual samplerate choise in AFS_select + dataFormat=1; + RHA = AFE_PICCOLO(afeSettings.RHA.ComPortType,afeSettings.RHA.sampleRate, ... + sT,nCh,Amp,Vref,dataFormat,afeSettings.RHA.name,ByteDepth); +end + +%% Initialization of progress bar +xpatch = [0 0 0 0]; +ypatch = [0 0 1 1]; +%set(hGUI_Rec.figure1,'CurrentAxes',hGUI_Rec.a_prog); +axes(hGUI_Rec.a_prog); +hGUI_Rec.hPatch = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); + +xpatch = [1 1 0 0]; +ypatch = [0 0 0 0]; +axes(hGUI_Rec.a_effort0); +hGUI_Rec.hPatch0 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); +axes(hGUI_Rec.a_effort1); +hGUI_Rec.hPatch1 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); +axes(hGUI_Rec.a_effort1); +hGUI_Rec.hPatch1 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); +axes(hGUI_Rec.a_effort2); +hGUI_Rec.hPatch2 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); +axes(hGUI_Rec.a_effort3); +hGUI_Rec.hPatch3 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); +axes(hGUI_Rec.a_effort4); +hGUI_Rec.hPatch4 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); +axes(hGUI_Rec.a_effort5); +hGUI_Rec.hPatch5 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); +axes(hGUI_Rec.a_effort6); +hGUI_Rec.hPatch6 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); +axes(hGUI_Rec.a_effort7); +hGUI_Rec.hPatch7 = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','xor','visible','on'); +effortMax = 2; + +%% +% Allocation of resource to improve speed, total data + % Identified problem if the different devices have different sF + % then different amount of data is comming from the differnt devices + % also problem in the counting of incoming data, needs to be changed so it + % expects right amount of data /Per +tdata = zeros(sF*sT,nCh,nM); +ADStdata = zeros(sF*sT,nCh,nM); +RHAtdata = zeros(sF*sT,nCh,nM); + +% Warning to the user +if afeSettings.prepare + set(hGUI_Rec.t_msg,'String','Get ready to start: 3'); + pause(1); + set(hGUI_Rec.t_msg,'String','Get ready to start: 2'); + pause(1); + set(hGUI_Rec.t_msg,'String','Get ready to start: 1'); + pause(1); +end +relax = importdata('Img/relax.jpg'); % Import Image + + +for ex = 1 : nM + disp(['Start ex: ' num2str(ex) ]) + + % Warning to the user + fileName = ['Img/' char(mov(ex)) '.jpg']; + if ~exist(fileName,'file') + fileName = 'Img/relax.jpg'; + end + + %movI = importdata(['Img/mov' num2str(ex) '.jpg']); % Import Image + movI = importdata(fileName); % Import Image + set(hGUI_Rec.a_pic,'Visible','on'); % Turn on visibility + %axes(hGUI_Rec.a_pic); % get hGUI_Rec + pic = image(movI,'Parent',hGUI_Rec.a_pic); % set image + axis(hGUI_Rec.a_pic,'off'); % Remove axis tick marks + + if afeSettings.prepare + set(hGUI_Rec.t_msg,'String',['Get ready for ' mov(ex) ' in 3 s']); + pause(1); + set(hGUI_Rec.t_msg,'String',['Get ready for ' mov(ex) ' in 2 s']); + pause(1); + set(hGUI_Rec.t_msg,'String',['Get ready for ' mov(ex) ' in 1 s']); + %set(hGUI_Rec.a_pic,'Visible','off'); % Turn OFF visibility + %delete(pic); % Delete image + pause(1); + end + + %% Dummy Contraction + set(hGUI_Rec.t_msg,'String',mov(ex)); + if afeSettings.prepare + pause(cT); + set(hGUI_Rec.t_msg,'String','Relax'); + pic = image(relax,'Parent',hGUI_Rec.a_pic); % set image + axis off; % Remove axis tick marks + pause(rT); + end + + + %% Start DAQ + if afeSettings.NI.active + start(ai); + end + if afeSettings.ADS.active + ADS.startRecording + end + if afeSettings.RHA.active + RHA.startRecording + end + + + for rep = 1 : nR + + while afeSettings.NI.active && afeSettings.NI.show && (ai.SamplesAcquired < (cT+rT)*sF*rep) || ... + afeSettings.ADS.active && afeSettings.ADS.show && (ADS.SamplesAcquired < (cT+rT)*sF*rep*nCh) || ... + afeSettings.RHA.active && afeSettings.RHA.show && (RHA.SamplesAcquired < (cT+rT)*sF*rep*nCh) + + %sF in both condition needs to be changed to the invidual samplings + %frequencies of the different devices + + %Instructions to the user + if afeSettings.NI.active && afeSettings.NI.show && (ai.SamplesAcquired <= sF*cT*rep + sF*rT*(rep-1)) || ... + afeSettings.ADS.active && afeSettings.ADS.show && (ADS.SamplesAcquired <= (sF*cT*rep + sF*rT*(rep-1)) * nCh) || ... + afeSettings.RHA.active && afeSettings.RHA.show && (RHA.SamplesAcquired <= (sF*cT*rep + sF*rT*(rep-1)) * nCh) + + set(hGUI_Rec.t_msg,'String',mov(ex)); + %axes(hGUI_Rec.a_pic); + pic = image(movI,'Parent',hGUI_Rec.a_pic); % set image + axis(hGUI_Rec.a_pic,'off'); % Remove axis tick marks + + % Status bar update + lastToc = sF*cT; + thisToc = ai.SamplesAcquired - (sF*cT*(rep-1)) - sF*rT*(rep-1); + else + set(hGUI_Rec.t_msg,'String','Relax'); + pic = image(relax,'Parent',hGUI_Rec.a_pic); % set image + axis(hGUI_Rec.a_pic,'off'); % Remove axis tick marks + + % Status bar update + lastToc = sF*rT; + thisToc = ai.SamplesAcquired - (sF*cT*rep) - sF*rT*(rep-1); + + end + % Status bar update + x =1-(thisToc/lastToc); + set(hGUI_Rec.figure1,'CurrentAxes',hGUI_Rec.a_prog); + set(hGUI_Rec.hPatch,'Xdata',[0 x x 0]); + drawnow; + + %--------------------------------- + if afeSettings.NI.show + data = peekdata(ai,cT*sF/100); + %data=data/1700; %Converting to input referred voltage + elseif afeSettings.ADS.show + data = ADS.storeFromBuffer; + + ADS.data = [ADS.data ; data]; + + if size(ADS.data,1) > (cT*sF/100) + data = (ADS.data(end-(cT*sF/100)+1:end,:) ).* (ADS.vref/(ADS.amplification*2^(ADS.byteDepth*8) )); %Converting to input referred voltage + else + data = zeros((cT*sF/100),nCh); + end + elseif afeSettings.RHA.show + data = RHA.storeFromBuffer; + + RHA.data = [RHA.data ; data]; + if size(RHA.data,1) > (cT*sF/100) + data = (RHA.data(end-(cT*sF/100)+1:end,:) ).* (RHA.vref/(RHA.amplification*2^(RHA.byteDepth*8))); %Converting to input referred voltage + else + data = zeros((cT*sF/100),nCh); + end + end + + aNs = length(data(:,1)); + NFFT = 2^nextpow2(aNs); % Next power of 2 from number of samples + f = sF/2*linspace(0,1,NFFT/2); + dataf = fft(data(1:aNs,:),NFFT)/aNs; + m = 2*abs(dataf((1:NFFT/2),:)); + + + chi = 1;%Channel Index for map data + if nCh >= 1 + set(p_t0,'YData',data(:,chi)); + set(p_f0,'XData',f); + set(p_f0,'YData',m(:,chi)); + % Update effort bar + xT = mean(abs(data(:,chi))); + x = xT / effortMax; + set(hGUI_Rec.hPatch0,'Ydata',[0 x x 0]); + + chi=chi+1; + end + if nCh >= 2 + set(p_t1,'YData',data(:,chi)); + set(p_f1,'XData',f); + set(p_f1,'YData',m(:,chi)); + % Update effort bar + xT = mean(abs(data(:,chi))); + x = xT / effortMax; + set(hGUI_Rec.hPatch1,'Ydata',[0 x x 0]); + + chi=chi+1; + end + if nCh >= 3 + set(p_t2,'YData',data(:,chi)); + set(p_f2,'XData',f); + set(p_f2,'YData',m(:,chi)); + % Update effort bar + xT = mean(abs(data(:,chi))); + x = xT / effortMax; + set(hGUI_Rec.hPatch2,'Ydata',[0 x x 0]); + + chi=chi+1; + end + if nCh >= 4 + set(p_t3,'YData',data(:,chi)); + set(p_f3,'XData',f); + set(p_f3,'YData',m(:,chi)); + % Update effort bar + xT = mean(abs(data(:,chi))); + x = xT / effortMax; + set(hGUI_Rec.hPatch3,'Ydata',[0 x x 0]); + + chi=chi+1; + end + if nCh >= 5 + set(p_t4,'YData',data(:,chi)); + % Update effort bar + xT = mean(abs(data(:,chi))); + x = xT / effortMax; + set(hGUI_Rec.hPatch4,'Ydata',[0 x x 0]); + + chi=chi+1; + %set(p_f4,'XData',f); + %set(p_f4,'YData',m(:,chi)); + end + if nCh >= 6 + set(p_t5,'YData',data(:,chi)); + % Update effort bar + xT = mean(abs(data(:,chi))); + x = xT / effortMax; + set(hGUI_Rec.hPatch5,'Ydata',[0 x x 0]); + + chi=chi+1; + %set(p_f5,'XData',f); + %set(p_f5,'YData',m(:,chi)); + end + if nCh >= 7 + set(p_t6,'YData',data(:,chi)); + % Update effort bar + xT = mean(abs(data(:,chi))); + x = xT / effortMax; + set(hGUI_Rec.hPatch6,'Ydata',[0 x x 0]); + + chi=chi+1; + %set(p_f6,'XData',f); + %set(p_f6,'YData',m(:,chi)); + end + if nCh >= 8 + set(p_t7,'YData',data(:,chi)); + % Update effort bar + xT = mean(abs(data(:,chi))); + x = xT / effortMax; + set(hGUI_Rec.hPatch7,'Ydata',[0 x x 0]); + + %set(p_f7,'XData',f); + %set(p_f7,'YData',m(:,chi)); + end + + + %--------------------------------- + end + + end + + %Check if all data has arrived from the other devices + while (afeSettings.NI.active && (ai.SamplesAcquired < (cT+rT)*sF*nR)) || ... + (afeSettings.ADS.active && (ADS.SamplesAcquired < (cT+rT)*sF*nR*nCh)) || ... + (afeSettings.RHA.active && (RHA.SamplesAcquired < (cT+rT)*sF*nR*nCh)) + + pause(1) + disp('Waiting for more data') + if afeSettings.NI.active + disp('NI') + disp((cT+rT)*sF*nR-ai.SamplesAcquired) + disp([ai.SamplesAcquired ai.SamplesAcquired*4]) + end + if afeSettings.RHA.active + disp('RHA') + disp((cT+rT)*sF*nR*nCh-RHA.SamplesAcquired) + disp(RHA.SamplesAcquired) + end + if afeSettings.ADS.active + disp('ADS') + disp((cT+rT)*sF*nR*nCh-ADS.SamplesAcquired) + disp(ADS.SamplesAcquired) + disp((cT+rT)*sF*nR*nCh) + end + end + + % Save Data + if afeSettings.NI.active +% disp('NI:') +% ai.SamplesAcquired + wait(ai,sT+1); + [data,tt,abstime] = getdata(ai); +% disp(['date = ' num2str(fix(abstime))]) +% size(data) +% size(tdata) + tdata(:,:,ex) = data; % / 1.70e3; %Converting to input referred voltage + end + if afeSettings.RHA.active +% disp('RHA:') +% disp(RHA.SamplesAcquired) +% disp(RHA.bytesAcquired) +% disp(size(RHA.data)) + data = [RHA.data ; RHA.storeFromBuffer]; +% disp(size(data)) +% disp(size(RHAtdata)) + RHAtdata(:,:,ex)=data * (RHA.vref/(RHA.amplification*2^(RHA.byteDepth*8))); %Converting to input referred voltage + end + + if afeSettings.ADS.active +% disp('ADS:') +% disp(ADS.SamplesAcquired) +% disp(ADS.bytesAcquired) +% disp(size(data)) + + data = [ADS.data ; ADS.storeFromBuffer]; +% disp(size(data)) +% disp(size(ADStdata)) + ADStdata(:,:,ex)=data * (ADS.vref/(ADS.amplification*2^(ADS.byteDepth*8))); %Converting to input referred voltage + end + + +end +set(hGUI_Rec.t_msg,'String','Session Terminated'); % Show message about acquisition + +%% Save data and compute training data using cTp +if afeSettings.NI.active + stop(ai); + delete(ai); + NItdata=tdata; + %NItrdata=ComputeTrainingData(tdata,sF,cT,rT,nR,nM,cTp); +else + NItdata=[]; + %NItrdata=[]; +end + +if afeSettings.ADS.active + ADStdata=ADStdata; + %ADStrdata=ComputeTrainingData(ADStdata,sF,cT,rT,nR,nM,cTp); +else + ADStdata=[]; + %ADStrdata=[]; +end + +if afeSettings.RHA.active + RHAtdata=RHAtdata; + %RHAtrdata=ComputeTrainingData(RHAtdata,sF,cT,rT,nR,nM,cTp); +else + RHAtdata=[]; + %RHAtrdata=[]; +end + + +%% Save Session to file +% date = [];%fix(abstime); +recSession.sF = sF; +recSession.sT = sT; +recSession.cT = cT; +recSession.rT = rT; +%recSession.cTp = cTp; +recSession.nM = nM; +recSession.nR = nR; +recSession.nCh = nCh; +recSession.dev = dev; +recSession.mov = mov; +recSession.date = fix(clock); +recSession.cmt = inputdlg('Additional comment on the recording session','Comments'); + +[filename, pathname] = uiputfile({'*.mat','MAT-files (*.mat)'},'Save as', 'Untitled.mat'); + if isequal(filename,0) || isequal(pathname,0) + disp('User pressed cancel') + else + disp(['User selected ', fullfile(pathname, filename)]) + if afeSettings.NI.active + recSession.tdata = NItdata; + %recSession.trdata = NItrdata; + if exist([pathname 'NI\'],'dir') == 0 + mkdir(pathname,'NI') + end + save([pathname, 'NI\',filename],'recSession'); + end + if afeSettings.ADS.active + recSession.tdata = ADStdata; + %recSession.trdata = ADStrdata; + %Possible to add string with device info for example recSession.device=afeSettings.ADS.name; + if exist([pathname 'ADS1298\'],'dir') == 0 + mkdir(pathname,'ADS1298') + end + save([pathname, 'ADS1298\',filename],'recSession'); + + % not needed here any more? +% if filters.PLH || filters.BP +% recSession.tdata = ADStdataFiltered; +% % recSession.trdata = ADStrdataFiltered; +% if exist([pathname 'ADS1298Filtered\'],'dir') == 0 +% mkdir(pathname,'ADS1298Filtered') +% end +% save([pathname,'ADS1298Filtered\',filename],'recSession'); +% end + end + if afeSettings.RHA.active + recSession.tdata = RHAtdata; + %recSession.trdata = RHAtrdata; + %Possible to add string with device info for example recSession.device=afeSettings.RHA.name; + if exist([pathname 'RHA2216\'],'dir') == 0 + mkdir(pathname,'RHA2216') + end + save([pathname 'RHA2216\',filename],'recSession'); + + % not needed here any more? +% if filters.PLH || filters.BP +% recSession.tdata = RHAtdataFiltered; +% %recSession.trdata = RHAtrdataFiltered; +% if exist([pathname 'RHA2216Filtered\'],'dir') == 0 +% mkdir(pathname,'RHA2216Filtered') +% end +% save([pathname,'RHA2216Filtered\',filename],'recSession'); +% end + end +end + +% Copy acquired data from the last excersice into cdata +% Display it +disp(recSession); + +if afeSettings.NI.show + data = NItdata(:,:,end); +elseif afeSettings.ADS.show + data = ADStdata(:,:,end).* (ADS.vref/(ADS.amplification*2^(ADS.byteDepth*8))); +elseif afeSettings.RHA.show + data = RHAtdata(:,:,end).* (RHA.vref/(RHA.amplification*2^(RHA.byteDepth*8))); +end + +chi=1; +if nCh >= 1 + cdata(:,1) = data(:,chi); + chi=chi+1; +end +if nCh >= 2 + cdata(:,2) = data(:,chi); + chi=chi+1; +end +if nCh >= 3 + cdata(:,3) = data(:,chi); + chi=chi+1; +end +if nCh >= 4 + cdata(:,4) = data(:,chi); + chi=chi+1; +end +if nCh >= 5 + cdata(:,5) = data(:,chi); + chi=chi+1; +end +if nCh >= 6 + cdata(:,6) = data(:,chi); + chi=chi+1; +end +if nCh >= 7 + cdata(:,7) = data(:,chi); + chi=chi+1; +end +if nCh >= 8 + cdata(:,8) = data(:,chi); +end + + +data_show(hGUI_Rec,cdata,sF,sT); +set(hGUI_Rec.a_pic,'Visible','off'); % Turn OFF visibility +delete(pic); % Delete image +end + +% function trdata = ComputeTrainingData(tdata,sF,cT,rT,nR,nM,cTp) +% %Compute Traning Data +% +% for ex = 1 : nM +% tempdata =[]; +% for rep = 1 : nR +% % Samples of the exersice to be consider for training +% % (sF*cT*cTp) Number of the samples that wont be consider for training +% % (sF*cT*rep) Number of samples that takes a contraction +% % (sF*rT*rep) Number of samples that takes a relaxation +% is = (sF*cT*cTp) + (sF*cT*(rep-1)) + (sF*rT*(rep-1)) + 1; +% fs = (sF*cT) + (sF*cT*(rep-1)) + (sF*rT*(rep-1)); +% tempdata = [tempdata ; tdata(is:fs,:,ex)]; +% end +% trdata(:,:,ex) = tempdata; +% end +% % end \ No newline at end of file diff --git a/SigRecordings/RecordingSession_ShowData.m b/SigRecordings/RecordingSession_ShowData.m new file mode 100644 index 0000000..fb1e716 --- /dev/null +++ b/SigRecordings/RecordingSession_ShowData.m @@ -0,0 +1,177 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% ------------------- Function Description ------------------ +% Function to Record Exc Sessions +% +% --------------------------Updates-------------------------- +% 2015-01-26 / Enzo Mastinu / A new GUI_Recordings has been developed for the + % BioPatRec_TRE release. Now it is possible to + % plot more then 8 channels at the same moment, for + % time and frequency plots both. It is faster and + % perfectly compatible with the ramp recording + % session. At the end of the recording session it + % is possible to check all channels individually, + % apply offline data process as feature extraction or filter etc. +% 2015-02-20 / Enzo Mastinu / Added the scaling of the data: now every channel plot will be + % dynamically and automatically resize to fit in the proper portion + % of the main plot. This is to avoid overlapping of channels + % waveforms and to have always the best zoom for every channel. +% 2015-02-23 / Enzo Mastinu / The scale of every channel plot is now the + % same scale of the channel which has the + % maximum absolute value +% 2015-02-24 / Enzo Mastinu / Now it is possible to choose if delete or not + % the offset introduced by the AFE from the time + % window plot + +% 20xx-xx-xx / Author / Comment + + + +function RecordingSession_ShowData(src, event) + + global handles; + global allData; + global timeStamps; + global samplesCounter; + global offsetDelete; + global plotGain; + + % Get required info from handles + sF = handles.sF; + nCh = handles.nCh; + rep = handles.rep; + cT = handles.cT; + rT = handles.rT; + sT = handles.sT; + ComPortType = handles.ComPortType; + rampStatus = handles.rampStatus; + if rampStatus + rampMin = handles.RampMin; + rampMax = handles.currentRampMax; + end + p_t0 = handles.p_t0; + p_f0 = handles.p_f0; + + % Get data from tempData and add to allData global vector + if(isempty(allData)) % Fist DAQ callback + timeStamps = []; + % the variable plotGain must be reloaded on every starting of a new + % recording, the reason of set it on a huge values is that in this + % way we are sure that this value will be overwritten with a lower + % value, see the code below for more details + plotGain = 10000000; + end + tempData = event.Data; + allData = [allData; tempData]; + timeStamps = [timeStamps; event.TimeStamps]; + + + %% Status bar update + if handles.fast + if strcmp(ComPortType,'NI') + % NI DAQ card + x = 1-(timeStamps(end)/sT); + else + x = 1-(samplesCounter/(sT*sF)); + end + else + if strcmp(ComPortType,'NI') + % NI DAQ card + if handles.contraction + thisToc = timeStamps(end) - ((rep-1)*(cT+rT)); + lastToc = cT; + else + thisToc = timeStamps(end) - ((rep*cT)+((rep-1)*rT)); + lastToc = rT; + end + x = 1-(thisToc/lastToc); + else + % other devices + if handles.contraction + x = 1-(samplesCounter/(cT*sF)); + else + x = 1-(samplesCounter/(rT*sF)); + end + end + end + set(handles.hPatch,'Xdata',[0 x x 0]); + drawnow; + + + if rampStatus + %% Uppdate ramp effort tracker + % Absolute value over all the channels and the current data + if handles.contraction + chAvgRMS = mean(sqrt(mean((tempData.^2),2))); % RMS Mean value on all Chs (compatible with old version of Matlab) + effortRatio = 100*(abs((chAvgRMS-rampMin)/(rampMax-rampMin))); + if effortRatio > 100 + effortRatio = 100; + end + if strcmp(ComPortType,'NI') + x = timeStamps(end)-(rep-1)*(cT+rT); + else + x = (samplesCounter/sF); + end + set(handles.hLine,'XData', x,'Ydata', effortRatio); + else + set(handles.hLine,'XData', 0,'Ydata', 0); + end + end + + + %% Display peeked Data + + aNs = length(tempData(:,1)); + NFFT = 2^nextpow2(aNs); % Next power of 2 from number of samples + f = sF/2*linspace(0,1,NFFT/2); + dataf = fft(tempData(1:aNs,:),NFFT)/aNs; + m = 2*abs(dataf((1:NFFT/2),:)); + set(p_f0,'XData',f); + + % Offset the plot of the different channels to fit into the main figure + ampPP = 5; + offVector = 0:nCh-1; + offVector = offVector .* ampPP; + + if offsetDelete + % delete the offset of the AFE and add offsets to plot channels in same graph + offset = mean(tempData); + for j = 1 : nCh + tempData(:,j) = tempData(:,j) - offset(j); + end + end + % calculate single channel's plot gain for frequency window + Kf = ampPP/(2*(max(max(abs(m))))); + % calculate single channel's plot gain for time window + % the idea is that the gain is automatically scaled depending on the absolute maximum + % value found in this new recording. In this way the gain will be changed + % dynamically to be able to discern a "rest" from a "contraction" plot + K = ampPP/(2*(max(max(abs(tempData))))); + if K < plotGain + % if the signals in the different windows is getting bigger the gain + % must be reduced consequently, the channels plots must always fit + % the main plot + plotGain = K; + end + % plot a new tWs sized window + for j = 1 : nCh + set(p_t0(j),'YData',tempData(:,j)*plotGain + offVector(j)); % add offsets to plot channels in same graph + set(p_f0(j),'YData',m(:,j)*Kf + offVector(j)); + end + drawnow + +end \ No newline at end of file diff --git a/SigRecordings/Split_recSession_Ch_Independent.m b/SigRecordings/Split_recSession_Ch_Independent.m index 4740bd4..bbc6a2a 100644 --- a/SigRecordings/Split_recSession_Ch_Independent.m +++ b/SigRecordings/Split_recSession_Ch_Independent.m @@ -1,52 +1,52 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function the slipt the information of recording sessions in different -% channels -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Otiz / Creation -% 20xx-xx-xx / Author / Comment on update - - -function [rS1 rS2] = Split_recSession_Ch(chA,chB) - - [file, path] = uigetfile('*.mat'); - if ~isequal(file, 0) - load([path,file]); - if exist('recSession','var') - % Make a copy of the original recording session - rS1 = recSession; - rS2 = recSession; - - rS1.tdata = rS1.tdata(:,chA,:); - rS1.trdata = rS1.trdata(:,chA,:); - rS1.nCh = length(chA); - recSession = rS1; - save([path,'\BiLong_',file],'recSession') - - rS2.tdata = rS2.tdata(:,chB,:); - rS2.trdata = rS2.trdata(:,chB,:); - rS2.nCh = length(chB); - recSession = rS2; - save([path,'\BiTrans_',file],'recSession') - - end - end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function the slipt the information of recording sessions in different +% channels +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Otiz / Creation +% 20xx-xx-xx / Author / Comment on update + + +function [rS1 rS2] = Split_recSession_Ch(chA,chB) + + [file, path] = uigetfile('*.mat'); + if ~isequal(file, 0) + load([path,file]); + if exist('recSession','var') + % Make a copy of the original recording session + rS1 = recSession; + rS2 = recSession; + + rS1.tdata = rS1.tdata(:,chA,:); + rS1.trdata = rS1.trdata(:,chA,:); + rS1.nCh = length(chA); + recSession = rS1; + save([path,'\BiLong_',file],'recSession') + + rS2.tdata = rS2.tdata(:,chB,:); + rS2.trdata = rS2.trdata(:,chB,:); + rS2.nCh = length(chB); + recSession = rS2; + save([path,'\BiTrans_',file],'recSession') + + end + end + diff --git a/SigTreatment/AddNoise_recSession.m b/SigTreatment/AddNoise_recSession.m new file mode 100644 index 0000000..a9086c4 --- /dev/null +++ b/SigTreatment/AddNoise_recSession.m @@ -0,0 +1,86 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to add white noise to a rec_Session using random numbers with +% mean equal to the mean of the signal, and standard deviation (std) as +% perentage of the maximum std in all channels and all movements. +% +% input : recSession Recording session +% pStd Percentagle of standard deviation +% +% output: recSession +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2014-12-30 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function recSession = AddNoise_recSession(recSession, pStd) + +% Parameters +cTp = 0.4; % percentage of the contraction time to be considered from the raw signal +tW = 0.2; % time window length +tI = 0.02; % time increment +sF = recSession.sF; +cT = recSession.cT; +rT = recSession.rT; +nS = recSession.sF * recSession.sT; +eRed = (1-cTp)/2; % effective reduction at the begining and at the end of contraction + +%% Extract the portion of interest of the raw signal using the cTp +for mov = 1 : recSession.nM + tempEMGData = []; + tempRestData = []; + for rep = 1 : recSession.nR + % Samples of the exersice to be consider for training + % (sF*cT*(cTp-1)) Number of the samples that wont be consider for training + % (sF*cT*rep) Number of samples that takes a contraction + % (sF*rT*rep) Number of samples that takes a relaxation + % EMG + is = fix((sF*cT*(1-cTp-eRed)) + (sF*cT*(rep-1)) + (sF*rT*(rep-1)) + 1); + fs = fix((sF*cT*(cTp+eRed)) + (sF*cT*(rep-1)) + (sF*rT*(rep-1))); + tempEMGData = [tempEMGData ; recSession.tdata(is:fs,:,mov)]; + % floor noise + is = fix((sF*cT*rep) + (sF*rT*(1-cTp-eRed)) + (sF*rT*(rep-1)) + 1); + fs = fix((sF*cT*rep) + (sF*rT*(cTp+eRed)) + (sF*rT*(rep-1))); + tempRestData = [tempRestData ; recSession.tdata(is:fs,:,mov)]; + end + emgData(:,:,mov) = tempEMGData; + restData(:,:,mov) = tempRestData; +end + +%% Extract standard deviation (EMG, and mean (floor noise) +twS = tW * sF; % Time window samples +overlapS = tI*sF; % Time increment samples +for mov = 1 : recSession.nM + emgDataMov = emgData(:,:,mov); + emgFeature(:,mov) = mean(ExtractSigFeatureVar(emgDataMov,sF,'tstd',twS,overlapS)); + restDataMov = restData(:,:,mov); + restFeature(:,mov) = mean(ExtractSigFeatureVar(restDataMov,sF,'tmn',twS,overlapS)); +end +% Get maximum variance for EMG and mean var for the rest/floor noise +maxEmgStd = max(max(emgFeature)); +meanRest = mean(mean(restFeature)); + +%% Generate white noise matrix +meanNoise = meanRest; +stdNoise = pStd * maxEmgStd; +noiseMat = meanNoise + stdNoise .* randn(nS,size(recSession.nCh,2),recSession.nM); + +%% Add noise to the recSession +recSession.tdata = recSession.tdata + noiseMat; + diff --git a/SigTreatment/AddRestAsMovement.m b/SigTreatment/AddRestAsMovement.m index fb74cdf..e949887 100644 --- a/SigTreatment/AddRestAsMovement.m +++ b/SigTreatment/AddRestAsMovement.m @@ -1,92 +1,92 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% funtion to add information about rest or no movement as an actuall -% movemen for training -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-xx-xx / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - - -function sigTreated = AddRestAsMovement(sigTreated, recSession) - - sF = recSession.sF; - cT = recSession.cT; - rT = recSession.rT; - nR = recSession.nR; - nM = recSession.nM; - tdata = recSession.tdata; - - % Collect the 50% to 75% of rest in between each contraction per each - % movement - for ex = 1 : nM - tempdata =[]; - for rep = 1 : nR - % Samples of the exersice to be consider for training - % (sF*cT*rep) Number of samples that takes a contraction - % (sF*rT*rep) Number of samples that takes a relaxation - is = fix((sF*cT*rep) + (sF*rT*.5) + (sF*rT*(rep-1)) + 1); - fs = fix((sF*cT*rep) + (sF*rT*.75) + (sF*rT*(rep-1))); - tempdata = [tempdata ; tdata(is:fs,:,ex)]; - end - trData(:,:,ex) = tempdata; - end - - % Gather the required amount of data for a movement - % The rest data set is made with contributions from all movements rest - % period - totSamp = size(sigTreated.trData,1); - sampXmov = fix(totSamp / sigTreated.nM); - sd = totSamp - sampXmov * sigTreated.nM; %samples difference - -% if totSamp ~= sampXmov * sigTreated.nM; -% disp(['"Rest" not fitted with ' num2str(sd) ' samples']); -% errordlg(['"Rest" not fitted with ' num2str(sd) ' samples'],'Error'); -% end - - restData = []; - %Using the first samples of each movement - is = 1; - fs = sampXmov; - for ex = 1 : nM - restData = [restData ; trData(is:fs,:,ex)]; - end - - % If the rest data set wasn't completed from the information of all - % rest periods, then it willbe completed using the information from - % the last rest period of the 1st movement - if size(restData,1) ~= totSamp - restData = [restData ; trData(end-sd+1:end,:,1)]; - end - - %Random selection of the sets to be use - -% trDL = size(trData,1); -% while size(restData,1) ~= size(sigTreated.trData,1) -% ex = fix(1 + (nM-1).*rand); -% sOff = fix(0 + (trDL-sampXmov).*rand); -% is = sOff + 1; -% fs = sOff + sampXmov; -% restData = [restData ; trData(is:fs,:,ex)]; -% end - - sigTreated.nM = sigTreated.nM+1; - sigTreated.mov(sigTreated.nM) = {'Rest'}; - sigTreated.trData(:,:,sigTreated.nM) = restData; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% funtion to add information about rest or no movement as an actuall +% movemen for training +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-xx-xx / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + + +function sigTreated = AddRestAsMovement(sigTreated, recSession) + + sF = recSession.sF; + cT = recSession.cT; + rT = recSession.rT; + nR = recSession.nR; + nM = recSession.nM; + tdata = recSession.tdata; + + % Collect the 50% to 75% of rest in between each contraction per each + % movement + for ex = 1 : nM + tempdata =[]; + for rep = 1 : nR + % Samples of the exersice to be consider for training + % (sF*cT*rep) Number of samples that takes a contraction + % (sF*rT*rep) Number of samples that takes a relaxation + is = fix((sF*cT*rep) + (sF*rT*.5) + (sF*rT*(rep-1)) + 1); + fs = fix((sF*cT*rep) + (sF*rT*.75) + (sF*rT*(rep-1))); + tempdata = [tempdata ; tdata(is:fs,:,ex)]; + end + trData(:,:,ex) = tempdata; + end + + % Gather the required amount of data for a movement + % The rest data set is made with contributions from all movements rest + % period + totSamp = size(sigTreated.trData,1); + sampXmov = fix(totSamp / sigTreated.nM); + sd = totSamp - sampXmov * sigTreated.nM; %samples difference + +% if totSamp ~= sampXmov * sigTreated.nM; +% disp(['"Rest" not fitted with ' num2str(sd) ' samples']); +% errordlg(['"Rest" not fitted with ' num2str(sd) ' samples'],'Error'); +% end + + restData = []; + %Using the first samples of each movement + is = 1; + fs = sampXmov; + for ex = 1 : nM + restData = [restData ; trData(is:fs,:,ex)]; + end + + % If the rest data set wasn't completed from the information of all + % rest periods, then it willbe completed using the information from + % the last rest period of the 1st movement + if size(restData,1) ~= totSamp + restData = [restData ; trData(end-sd+1:end,:,1)]; + end + + %Random selection of the sets to be use + +% trDL = size(trData,1); +% while size(restData,1) ~= size(sigTreated.trData,1) +% ex = fix(1 + (nM-1).*rand); +% sOff = fix(0 + (trDL-sampXmov).*rand); +% is = sOff + 1; +% fs = sOff + sampXmov; +% restData = [restData ; trData(is:fs,:,ex)]; +% end + + sigTreated.nM = sigTreated.nM+1; + sigTreated.mov(sigTreated.nM) = {'Rest'}; + sigTreated.trData(:,:,sigTreated.nM) = restData; diff --git a/SigTreatment/ApplySignalSeparation.m b/SigTreatment/ApplySignalSeparation.m new file mode 100644 index 0000000..1627331 --- /dev/null +++ b/SigTreatment/ApplySignalSeparation.m @@ -0,0 +1,36 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This Function Projecting ICA Unmixing Matrix on Training,Validation and +% Testing sets on Segmented Data. +% +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-20 / Tanuj Kumar Aluru / Creation +% 20xx-xx-xx / Author / Comment on update + + +function [trData vData tData]=ApplySignalSeparation(sigTreated,trData, vData, tData) + + if strcmp(sigTreated.sigSeperation.Alg,'ICA') + [trData vData tData]=ICAPreprocess(sigTreated,trData,vData,tData); + + end + \ No newline at end of file diff --git a/SigTreatment/Compatibility_treated_data.m b/SigTreatment/Compatibility_treated_data.m index 0eb1354..f2b9855 100644 --- a/SigTreatment/Compatibility_treated_data.m +++ b/SigTreatment/Compatibility_treated_data.m @@ -1,56 +1,56 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to keep the compatibility with data treated in older versions of -% the software -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-06-30 / Max Ortiz / Creation -% 2012-05-17 / Max Ortiz / update to sigFeatures - - -function sigFeatures = Compatibility_treated_data(treated_data) - - tempF = fieldnames(treated_data); - if strcmp(tempF(1),'Fs') - - sigFeatures.sF = treated_data.Fs; - sigFeatures.tW = treated_data.tw; - sigFeatures.nCh = 1:4; - sigFeatures.dev = 'AD2/3'; - -% sigFeatures.nR = treated_data.Nr; -% sigFeatures.eTs = treated_data.eTs; -% sigFeatures.nw = treated_data.nw; - - sigFeatures.fFilter = treated_data.filters; - sigFeatures.sFilter = 1; - - - sigFeatures.trSets = treated_data.trN; - sigFeatures.vSets = treated_data.vN; - sigFeatures.tSets = treated_data.tN; - sigFeatures.trFeatures = treated_data.trdata; - sigFeatures.vFeatures = treated_data.vdata; - sigFeatures.tFeatures = treated_data.tdata; - sigFeatures.mov = treated_data.msg; - - else - sigFeatures = treated_data; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to keep the compatibility with data treated in older versions of +% the software +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-06-30 / Max Ortiz / Creation +% 2012-05-17 / Max Ortiz / update to sigFeatures + + +function sigFeatures = Compatibility_treated_data(treated_data) + + tempF = fieldnames(treated_data); + if strcmp(tempF(1),'Fs') + + sigFeatures.sF = treated_data.Fs; + sigFeatures.tW = treated_data.tw; + sigFeatures.nCh = 1:4; + sigFeatures.dev = 'AD2/3'; + +% sigFeatures.nR = treated_data.Nr; +% sigFeatures.eTs = treated_data.eTs; +% sigFeatures.nw = treated_data.nw; + + sigFeatures.fFilter = treated_data.filters; + sigFeatures.sFilter = 1; + + + sigFeatures.trSets = treated_data.trN; + sigFeatures.vSets = treated_data.vN; + sigFeatures.tSets = treated_data.tN; + sigFeatures.trFeatures = treated_data.trdata; + sigFeatures.vFeatures = treated_data.vdata; + sigFeatures.tFeatures = treated_data.tdata; + sigFeatures.mov = treated_data.msg; + + else + sigFeatures = treated_data; end \ No newline at end of file diff --git a/SigTreatment/ComputeSignalSeparation.m b/SigTreatment/ComputeSignalSeparation.m new file mode 100644 index 0000000..9c4d723 --- /dev/null +++ b/SigTreatment/ComputeSignalSeparation.m @@ -0,0 +1,36 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This Function Computing ICA on 60% of Raw Treated Data(40% Trainig and +% 20% validation Data) for All Movements +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-20 / Tanuj Kumar Aluru / Creation +% 20xx-xx-xx / Author / Comment on update + +function sigTreated=ComputeSignalSeparation(sigTreated) + + +if strcmp(sigTreated.sigSeperation.Alg,'ICA') + + ICAUnmixMat=ICA(sigTreated.trData(1:size(sigTreated.trData,1)*.6,:,:)); + + sigTreated.sigSeperation.ICAUnmixMat=ICAUnmixMat; +end \ No newline at end of file diff --git a/SigTreatment/Downsample_recSession.m b/SigTreatment/Downsample_recSession.m new file mode 100644 index 0000000..a8a7fdf --- /dev/null +++ b/SigTreatment/Downsample_recSession.m @@ -0,0 +1,41 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to downsampel the data in a recording session (recSession) +% +% input: recSession (struct) +% dS = downsample frequency or new frequency +% output: recSession (struct) +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2014-12-05 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function recSession = Downsample_recSession(recSession, dS) + +dsFactor = recSession.sF/dS; + +if ~mod(dsFactor,1) + recSession.tdata = downsample(recSession.tdata,dsFactor); + recSession.sF = dS; +else + errordlg('Downsample frequency is not a module of original frequency','Error'); +end + + diff --git a/SigTreatment/Frequency Filters/ApplyButterFilter.m b/SigTreatment/Frequency Filters/ApplyButterFilter.m index 2f0140b..ea002bc 100644 --- a/SigTreatment/Frequency Filters/ApplyButterFilter.m +++ b/SigTreatment/Frequency Filters/ApplyButterFilter.m @@ -1,32 +1,32 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Apply the requested BW filter to data -% -% ------------------------- Updates & Contributors ------------------------ -% 2009-04-16 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment - - -function [dataf] = ApplyButterFilter(Fs,N,Fc1,Fc2,data) - -[z,p,k] = butter(N/2, [Fc1 Fc2]/(Fs/2)); -[sos_var,g] = zp2sos(z, p, k); -Hd = dfilt.df2sos(sos_var, g); -dataf = filter(Hd,data); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Apply the requested BW filter to data +% +% ------------------------- Updates & Contributors ------------------------ +% 2009-04-16 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment + + +function [dataf] = ApplyButterFilter(Fs,N,Fc1,Fc2,data) + +[z,p,k] = butter(N/2, [Fc1 Fc2]/(Fs/2)); +[sos_var,g] = zp2sos(z, p, k); +Hd = dfilt.df2sos(sos_var, g); +dataf = filter(Hd,data); + diff --git a/SigTreatment/Frequency Filters/ApplyFilters.m b/SigTreatment/Frequency Filters/ApplyFilters.m index e471b8c..1c26a98 100644 --- a/SigTreatment/Frequency Filters/ApplyFilters.m +++ b/SigTreatment/Frequency Filters/ApplyFilters.m @@ -1,63 +1,63 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% General function to call other filter functions if selected -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-07-27 / Max Ortiz / Creation -% 2012-03-11 / Max Ortiz / Added DDF Abs - -function data = ApplyFilters(sigTreated, data) - - - %% Frequency filters - sF = sigTreated.sF; - -% disp('Frequency filtering data...'); - - if strcmp(sigTreated.fFilter,'None') - % Do nothing and exit if - elseif strcmp(sigTreated.fFilter,'PLH') - data = BSbutterPLHarmonics(sF, data); - elseif strcmp(sigTreated.fFilter,'BP 20-1k') - data = FilterBP(sF, data,20,1000); - elseif strcmp(sigTreated.fFilter,'BP 70-1k') - data = FilterBP(sF, data,70,1000); - end - -% disp('Frequency Filtering Done'); - - %% Spatial filters -% disp('Spatial filtering data...'); - - if strcmp(sigTreated.sFilter,'None') - % Do nothing and exit if - elseif strcmp(sigTreated.sFilter,'SDF') - data = SpatialFilterSDF(data); - elseif strcmp(sigTreated.sFilter,'DDF') - data = SpatialFilterDDF(data); - elseif strcmp(sigTreated.sFilter,'DDF Abs') - data = SpatialFilterDDFAbs(data); - disp('Warning: Signals have been converted to their absulute value'); - end - -% disp('Spatial Filtering Done'); - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% General function to call other filter functions if selected +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-27 / Max Ortiz / Creation +% 2012-03-11 / Max Ortiz / Added DDF Abs + +function data = ApplyFilters(sigTreated, data) + + + %% Frequency filters + sF = sigTreated.sF; + +% disp('Frequency filtering data...'); + + if strcmp(sigTreated.fFilter,'None') + % Do nothing and exit if + elseif strcmp(sigTreated.fFilter,'PLH') + data = BSbutterPLHarmonics(sF, data); + elseif strcmp(sigTreated.fFilter,'BP 20-1k') + data = FilterBP(sF, data,20,1000); + elseif strcmp(sigTreated.fFilter,'BP 70-1k') + data = FilterBP(sF, data,70,1000); + end + +% disp('Frequency Filtering Done'); + + %% Spatial filters +% disp('Spatial filtering data...'); + + if strcmp(sigTreated.sFilter,'None') + % Do nothing and exit if + elseif strcmp(sigTreated.sFilter,'SDF') + data = SpatialFilterSDF(data); + elseif strcmp(sigTreated.sFilter,'DDF') + data = SpatialFilterDDF(data); + elseif strcmp(sigTreated.sFilter,'DDF Abs') + data = SpatialFilterDDFAbs(data); + disp('Warning: Signals have been converted to their absulute value'); + end + +% disp('Spatial Filtering Done'); + + end \ No newline at end of file diff --git a/SigTreatment/Frequency Filters/BSbutterPLHarmonics.m b/SigTreatment/Frequency Filters/BSbutterPLHarmonics.m index 941ffab..ab846b5 100644 --- a/SigTreatment/Frequency Filters/BSbutterPLHarmonics.m +++ b/SigTreatment/Frequency Filters/BSbutterPLHarmonics.m @@ -1,38 +1,38 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% [Give a short summary about the principle of your function here.] -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function [data] = BSbutterPLHarmonics(Fs, data) - -N = 20; % Order -for i = 0 : 11 - Fc1 = 49 + 50*i; % First Cutoff Frequency - Fc2 = 51 + 50*i; % Second Cutoff Frequency - - [z,p,k] = butter(N/2, [Fc1 Fc2]/(Fs/2), 'stop'); - [sos_var,g] = zp2sos(z, p, k); - Hd = dfilt.df2sos(sos_var, g); - data = filter(Hd,data); -end - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% [Give a short summary about the principle of your function here.] +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function [data] = BSbutterPLHarmonics(Fs, data) + +N = 20; % Order +for i = 0 : 11 + Fc1 = 49 + 50*i; % First Cutoff Frequency + Fc2 = 51 + 50*i; % Second Cutoff Frequency + + [z,p,k] = butter(N/2, [Fc1 Fc2]/(Fs/2), 'stop'); + [sos_var,g] = zp2sos(z, p, k); + Hd = dfilt.df2sos(sos_var, g); + data = filter(Hd,data); +end + diff --git a/SigTreatment/Frequency Filters/Filter50hz.m b/SigTreatment/Frequency Filters/Filter50hz.m index 28ab01b..91660d7 100644 --- a/SigTreatment/Frequency Filters/Filter50hz.m +++ b/SigTreatment/Frequency Filters/Filter50hz.m @@ -1,37 +1,39 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% [Give a short summary about the principle of your function here.] -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function [dataf] = Filter50hz (Fs, data) - -N = 20; % Order -Fc1 = 40; % First Cutoff Frequency -Fc2 = 60; % Second Cutoff Frequency - -[z,p,k] = butter(N/2, [Fc1 Fc2]/(Fs/2), 'stop'); - -[sos_var,g] = zp2sos(z, p, k); -Hd50 = dfilt.df2sos(sos_var, g); - -dataf = filter(Hd50,data); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% [Give a short summary about the principle of your function here.] +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function [dataf] = Filter50hz (Fs, data) + +N = 6; % Order +Fc1 = 49; % First Cutoff Frequency +Fc2 = 51; % Second Cutoff Frequency + +[z,p,k] = butter(N/2, [Fc1 Fc2]/(Fs/2), 'stop'); + +[sos_var,g] = zp2sos(z, p, k); +Hd50 = dfilt.df2sos(sos_var, g); + +dataf = filter(Hd50,data); + + diff --git a/SigTreatment/Frequency Filters/FilterBP.m b/SigTreatment/Frequency Filters/FilterBP.m index 7ef96b3..b77d04a 100644 --- a/SigTreatment/Frequency Filters/FilterBP.m +++ b/SigTreatment/Frequency Filters/FilterBP.m @@ -1,36 +1,36 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Band-pass filter -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-07-18 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function [dataf] = FilterBP (Fs, data, cF1, cF2) - -N = 4; % Order - -% Calculate the zpk values using the BUTTER function. -[z,p,k] = butter(N/2, [cF1 cF2]/(Fs/2)); - -[sos_var,g] = zp2sos(z, p, k); -Hd = dfilt.df2sos(sos_var, g); - -dataf = filter(Hd,data); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Band-pass filter +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-18 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function [dataf] = FilterBP (Fs, data, cF1, cF2) + +N = 4; % Order + +% Calculate the zpk values using the BUTTER function. +[z,p,k] = butter(N/2, [cF1 cF2]/(Fs/2)); + +[sos_var,g] = zp2sos(z, p, k); +Hd = dfilt.df2sos(sos_var, g); + +dataf = filter(Hd,data); + diff --git a/SigTreatment/Frequency Filters/FilterBP_EMG.m b/SigTreatment/Frequency Filters/FilterBP_EMG.m index 150a383..01973d2 100644 --- a/SigTreatment/Frequency Filters/FilterBP_EMG.m +++ b/SigTreatment/Frequency Filters/FilterBP_EMG.m @@ -1,38 +1,38 @@ -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% [Give a short summary about the principle of your function here.] -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function [dataf] = FilterBP_EMG(Fs, data) - -N = 20; % Order -Fc1 = 70; % First Cutoff Frequency -Fc2 = 1000; % Second Cutoff Frequency - -% Calculate the zpk values using the BUTTER function. -[z,p,k] = butter(N/2, [Fc1 Fc2]/(Fs/2)); - -[sos_var,g] = zp2sos(z, p, k); -Hd = dfilt.df2sos(sos_var, g); - -dataf = filter(Hd,data); - +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% [Give a short summary about the principle of your function here.] +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function [dataf] = FilterBP_EMG(Fs, data) + +N = 20; % Order +Fc1 = 70; % First Cutoff Frequency +Fc2 = 1000; % Second Cutoff Frequency + +% Calculate the zpk values using the BUTTER function. +[z,p,k] = butter(N/2, [Fc1 Fc2]/(Fs/2)); + +[sos_var,g] = zp2sos(z, p, k); +Hd = dfilt.df2sos(sos_var, g); + +dataf = filter(Hd,data); + diff --git a/SigTreatment/Frequency Filters/FilterData.m b/SigTreatment/Frequency Filters/FilterData.m index c2efcd5..d72d284 100644 --- a/SigTreatment/Frequency Filters/FilterData.m +++ b/SigTreatment/Frequency Filters/FilterData.m @@ -1,42 +1,42 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Funtion to filter the received data according to filtering option at the -% GUI -% Input = Data and GUI handles -% Output = Filtered data -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-04-15 / Max Ortiz / Creation -% 2012-11-25 / Max Ortiz / Update to BioPatRec coding standard -% 20xx-xx-xx / Author / Comment on update - -function data = FilterData(data, handles, Fs) - - if get(handles.cb_filter50hz,'Value') - data = Filter50hz(Fs, data); - end - - if get(handles.cb_filterBP,'Value') - data = FilterBP(Fs, data); - end - - if get(handles.cb_filter80Hz,'Value') - data = FilterHP80hz(Fs, data); - end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Funtion to filter the received data according to filtering option at the +% GUI +% Input = Data and GUI handles +% Output = Filtered data +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-04-15 / Max Ortiz / Creation +% 2012-11-25 / Max Ortiz / Update to BioPatRec coding standard +% 20xx-xx-xx / Author / Comment on update + +function data = FilterData(data, handles, Fs) + + if get(handles.cb_filter50hz,'Value') + data = Filter50hz(Fs, data); + end + + if get(handles.cb_filterBP,'Value') + data = FilterBP(Fs, data); + end + + if get(handles.cb_filter80Hz,'Value') + data = FilterHP80hz(Fs, data); + end diff --git a/SigTreatment/Frequency Filters/FilterHP80hz.m b/SigTreatment/Frequency Filters/FilterHP80hz.m index 9cb93d2..c477423 100644 --- a/SigTreatment/Frequency Filters/FilterHP80hz.m +++ b/SigTreatment/Frequency Filters/FilterHP80hz.m @@ -1,39 +1,39 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% [Give a short summary about the principle of your function here.] -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function [dataf] = FilterHP80hz (Fs, data) - -N = 20; % Order -Fc = 80; % Cutoff Frequency - -% Calculate the zpk values using the BUTTER function. -[z,p,k] = butter(N, Fc/(Fs/2), 'high'); - -[sos_var,g] = zp2sos(z, p, k); -Hd = dfilt.df2sos(sos_var, g); - -dataf = filter(Hd,data); - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% [Give a short summary about the principle of your function here.] +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function [dataf] = FilterHP80hz (Fs, data) + +N = 20; % Order +Fc = 80; % Cutoff Frequency + +% Calculate the zpk values using the BUTTER function. +[z,p,k] = butter(N, Fc/(Fs/2), 'high'); + +[sos_var,g] = zp2sos(z, p, k); +Hd = dfilt.df2sos(sos_var, g); + +dataf = filter(Hd,data); + + diff --git a/SigTreatment/GUI_SigTreatment.fig b/SigTreatment/GUI_SigTreatment.fig index 38fd26dfb17bc220b04b7b07062cecde8cb885bb..18a1cc1a17c0d1a4830751a2ca07eb12f8be2b41 100644 GIT binary patch literal 18127 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 literal 15619 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 diff --git a/SigTreatment/GUI_SigTreatment.m b/SigTreatment/GUI_SigTreatment.m index 0ee8474..b25be8d 100644 --- a/SigTreatment/GUI_SigTreatment.m +++ b/SigTreatment/GUI_SigTreatment.m @@ -47,7 +47,7 @@ % Edit the above text to modify the response to help GUI_SigTreatment -% Last Modified by GUIDE v2.5 26-Jul-2012 16:53:37 +% Last Modified by GUIDE v2.5 03-Jan-2015 18:23:03 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; @@ -120,7 +120,7 @@ function pb_treat_Callback(hObject, eventdata, handles) set(handles.t_msg,'String','Extracting signal features'); drawnow; sigFeatures = GetAllSigFeatures(handles, sigTreated); - + sigFeatures.sigSeperation=sigTreated.sigSeperation; % Get back in the parent GUI ---------------------------------------- phandles = get(handles.t_mhandles,'UserData'); % get parent GUI handles @@ -448,7 +448,31 @@ function pb_preProcessing_Callback(hObject, eventdata, handles) sigTreated = PreProcessing(handles); %Computer numer of sequencially avialable windows -------------------- - nw = fix(sigTreated.cT * sigTreated.cTp * sigTreated.nR / str2double(get(handles.et_tw,'String'))); +% nw = fix(sigTreated.cT * sigTreated.cTp * sigTreated.nR / str2double(get(handles.et_tw,'String'))); +% set(handles.et_nw,'String',num2str(nw)); +% +% trP = str2double(get(handles.et_trP,'String')); +% trN = fix(trP * nw); +% set(handles.et_trN,'String',num2str(trN)); +% +% vP = str2double(get(handles.et_vP,'String')); +% vN = fix( vP* nw); +% set(handles.et_vN,'String',num2str(vN)); +% +% tP = str2double(get(handles.et_tP,'String')); +% tN = fix(tP * nw); +% set(handles.et_tN,'String',num2str(tN)); +% set(handles.t_totN,'String',num2str(trN+vN+tN)); +% set(handles.t_totP,'String',num2str(trP+vP+tP)); + + % Computer the number of windows using overlapping + set(handles.et_wOverlap,'Enable','on'); + overlap = str2double(get(handles.et_wOverlap,'String')); + + tT = sigTreated.cT * sigTreated.cTp * sigTreated.nR; + tw = str2double(get(handles.et_tw,'String')); + offset = ceil(tw/overlap); + nw = fix(tT / overlap) - offset; set(handles.et_nw,'String',num2str(nw)); trP = str2double(get(handles.et_trP,'String')); @@ -461,11 +485,24 @@ function pb_preProcessing_Callback(hObject, eventdata, handles) tP = str2double(get(handles.et_tP,'String')); tN = fix(tP * nw); + %add test time windows so that it matches the total amount of + %windows + while trN+vN+tN < nw + tN = tN + 1; + end set(handles.et_tN,'String',num2str(tN)); set(handles.t_totN,'String',num2str(trN+vN+tN)); set(handles.t_totP,'String',num2str(trP+vP+tP)); %Disable and enable bottons ------------------------------------------- + set(handles.lb_movements,'Enable','off'); + set(handles.lb_nCh,'Enable','off'); + set(handles.et_downsample,'Enable','off'); + set(handles.et_noise,'Enable','off'); + set(handles.et_nM,'Enable','off'); + set(handles.et_cTp,'Enable','off'); + set(handles.cb_rest,'Enable','off'); + set(handles.pm_scaling,'Enable','off'); set(handles.pb_preProcessing,'Enable','off'); set(handles.pb_treat,'Enable','on'); set(handles.pb_treatFolder,'Enable','on'); @@ -694,3 +731,95 @@ function et_cTp_Callback(hObject, eventdata, handles) % Hints: get(hObject,'String') returns contents of et_cTp as text % str2double(get(hObject,'String')) returns contents of et_cTp as a double + + +% --- Executes on selection change in pm_SignalSeparation. +function pm_SignalSeparation_Callback(hObject, eventdata, handles) +% hObject handle to pm_SignalSeparation (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns pm_SignalSeparation contents as cell array +% contents{get(hObject,'Value')} returns selected item from pm_SignalSeparation + + +% --- Executes during object creation, after setting all properties. +function pm_SignalSeparation_CreateFcn(hObject, eventdata, handles) +% hObject handle to pm_SignalSeparation (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_downsample_Callback(hObject, eventdata, handles) +% hObject handle to et_downsample (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_downsample as text +% str2double(get(hObject,'String')) returns contents of et_downsample as a double + + +% --- Executes during object creation, after setting all properties. +function et_downsample_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_downsample (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + +% --- Executes on selection change in pm_scaling. +function pm_scaling_Callback(hObject, eventdata, handles) +% hObject handle to pm_scaling (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = cellstr(get(hObject,'String')) returns pm_scaling contents as cell array +% contents{get(hObject,'Value')} returns selected item from pm_scaling + + +% --- Executes during object creation, after setting all properties. +function pm_scaling_CreateFcn(hObject, eventdata, handles) +% hObject handle to pm_scaling (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + + + +function et_noise_Callback(hObject, eventdata, handles) +% hObject handle to et_noise (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: get(hObject,'String') returns contents of et_noise as text +% str2double(get(hObject,'String')) returns contents of et_noise as a double + + +% --- Executes during object creation, after setting all properties. +function et_noise_CreateFcn(hObject, eventdata, handles) +% hObject handle to et_noise (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: edit controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end diff --git a/SigTreatment/GetData.m b/SigTreatment/GetData.m index 362e4dd..f65a2f9 100644 --- a/SigTreatment/GetData.m +++ b/SigTreatment/GetData.m @@ -1,117 +1,117 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% This function will create matrices of training, validation and testing data -% by splitting the original data -% It will also send the data to the routine to break down its -% characteristics -% -% input: Data is a Nsamples x Nchannels x Nexercises matrix -% sigTreated struct with the information required for the data -% treatment -% output: trdata are the split training data from the original recording time -% vdata are the split validation data -% tdata are the split testing data -% -% NOTE: Optimization could be implemented -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Ortiz / Creation -% 2009-07-15 / Max Ortiz / Include time splits -% 2009-07-15 / Max Ortiz / Adaptation to work with treated_data struct -% 2011-07-19 / Max Ortiz / Modification to only deliver the data of -% the time windows and no the signal features -% and use of the sigTreated struct -% 2012-06-13 / Max Ortiz / Addition of a tolerance in the verification of -% eCtc and eCt since matlab presision on the doubles operation was screwing -% the comparison - -function [trdata, vdata, tdata] = GetData (sigTreated) - - data = sigTreated.trData; % trData only contains information from the contraction - nM = length(data(1,1,:)); % Number of movements - - % Some validations - ssize = fix(length(data(:,1,1))/sigTreated.nR); % Samples Size of a repetition or Number of samples that makes a repetition - eCt = sigTreated.eCt; % Due to several issues with matlab this was separated - eCtc = ssize/sigTreated.sF; % to more lines of code - - %if eCtc ~= eCt - if abs(eCtc - eCt) > 0.0000001 % a tolerance had to be introduce due to matlab doubles inacuracy - disp('ERROR!!!! No match between samples and eCt'); - errordlg('No match between samples and eCt','Error'); - return; - end - - if sigTreated.trSets + sigTreated.vSets + sigTreated.tSets ~= sigTreated.nW % Verification - disp('ERROR!!!! No match in total number of sets'); - errordlg('No match in total number of sets','Error'); - return; - end - - % Get Data - if strcmp(sigTreated.twSegMethod,'Non Overlapped') - - nTw = sigTreated.eCt/sigTreated.tW; % Number of time windows per eCt - if fix(nTw*sigTreated.nR) ~= sigTreated.nW % Verification - disp('ERROR!!!! No match between number of time windows'); - errordlg('No match between number of time windows','Error'); - return; - end - - assize = fix(ssize /nTw); % Absolute number of samples that corresponde to the window time of a repetition - - for e = 1: nM - for i = 1 : sigTreated.nW - iidx = 1 + (assize*(i-1)); - eidx = assize+(assize*(i-1)); - %tempdata(i,e) = analyze_signal(data(iidx:eidx,:,e),sigTreated.sF); - tempdata(:,:,e,i) = data(iidx:eidx,:,e); - end - end - - elseif strcmp(sigTreated.twSegMethod,'Overlapped Cons') - - tWsamples = sigTreated.tW * sigTreated.sF; % Samples corresponding Time window - oS = sigTreated.wOverlap * sigTreated.sF; % Samples correcponding overlap - - for e = 1: nM - for i = 1 : sigTreated.nW - iidx = 1 + (oS * (i-1)); - eidx = tWsamples + (oS *(i-1)); - tempdata(:,:,e,i) = data(iidx:eidx,:,e); - end - end - - - - elseif strcmp(sigTreated.twSegMethod,'Overlapped Rand') - - end - - - trSets = sigTreated.trSets; - vSets = sigTreated.vSets; - tSets = sigTreated.tSets; - trdata = tempdata(:,:,:,1:trSets); - vdata = tempdata(:,:,:,trSets+1:trSets+vSets); - tdata = tempdata(:,:,:,trSets+vSets+1:trSets+vSets+tSets); - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function will create matrices of training, validation and testing data +% by splitting the original data +% It will also send the data to the routine to break down its +% characteristics +% +% input: Data is a Nsamples x Nchannels x Nexercises matrix +% sigTreated struct with the information required for the data +% treatment +% output: trdata are the split training data from the original recording time +% vdata are the split validation data +% tdata are the split testing data +% +% NOTE: Optimization could be implemented +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Ortiz / Creation +% 2009-07-15 / Max Ortiz / Include time splits +% 2009-07-15 / Max Ortiz / Adaptation to work with treated_data struct +% 2011-07-19 / Max Ortiz / Modification to only deliver the data of +% the time windows and no the signal features +% and use of the sigTreated struct +% 2012-06-13 / Max Ortiz / Addition of a tolerance in the verification of +% eCtc and eCt since matlab presision on the doubles operation was screwing +% the comparison + +function [trdata, vdata, tdata] = GetData (sigTreated) + + data = sigTreated.trData; % trData only contains information from the contraction + nM = length(data(1,1,:)); % Number of movements + + % Some validations + ssize = fix(length(data(:,1,1))/sigTreated.nR); % Samples Size of a repetition or Number of samples that makes a repetition + eCt = sigTreated.eCt; % Due to several issues with matlab this was separated + eCtc = ssize/sigTreated.sF; % to more lines of code + + %if eCtc ~= eCt + if abs(eCtc - eCt) > 0.0000001 % a tolerance had to be introduce due to matlab doubles inacuracy + disp('ERROR!!!! No match between samples and eCt'); + errordlg('No match between samples and eCt','Error'); + return; + end + + if sigTreated.trSets + sigTreated.vSets + sigTreated.tSets ~= sigTreated.nW % Verification + disp('ERROR!!!! No match in total number of sets'); + errordlg('No match in total number of sets','Error'); + return; + end + + % Get Data + if strcmp(sigTreated.twSegMethod,'Non Overlapped') + + nTw = sigTreated.eCt/sigTreated.tW; % Number of time windows per eCt + if fix(nTw*sigTreated.nR) ~= sigTreated.nW % Verification + disp('ERROR!!!! No match between number of time windows'); + errordlg('No match between number of time windows','Error'); + return; + end + + assize = fix(ssize /nTw); % Absolute number of samples that corresponde to the window time of a repetition + + for e = 1: nM + for i = 1 : sigTreated.nW + iidx = 1 + (assize*(i-1)); + eidx = assize+(assize*(i-1)); + %tempdata(i,e) = analyze_signal(data(iidx:eidx,:,e),sigTreated.sF); + tempdata(:,:,e,i) = data(iidx:eidx,:,e); + end + end + + elseif strcmp(sigTreated.twSegMethod,'Overlapped Cons') + + tWsamples = sigTreated.tW * sigTreated.sF; % Samples corresponding Time window + oS = sigTreated.wOverlap * sigTreated.sF; % Samples correcponding overlap + + for e = 1: nM + for i = 1 : sigTreated.nW + iidx = 1 + (oS * (i-1)); + eidx = tWsamples + (oS *(i-1)); + tempdata(:,:,e,i) = data(iidx:eidx,:,e); + end + end + + + + elseif strcmp(sigTreated.twSegMethod,'Overlapped Rand') + + end + + + trSets = sigTreated.trSets; + vSets = sigTreated.vSets; + tSets = sigTreated.tSets; + trdata = tempdata(:,:,:,1:trSets); + vdata = tempdata(:,:,:,trSets+1:trSets+vSets); + tdata = tempdata(:,:,:,trSets+vSets+1:trSets+vSets+tSets); + + end \ No newline at end of file diff --git a/SigTreatment/ICA/ICA.m b/SigTreatment/ICA/ICA.m new file mode 100644 index 0000000..b13513e --- /dev/null +++ b/SigTreatment/ICA/ICA.m @@ -0,0 +1,141 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function performs Independent Coponent Analysis for Treated +% as Prprocessing using Fast Fixed Point Algorithm . +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-08-01 / Tanuj Kumar Aluru / Creation +% 20xx-xx-xx / Author / Comment on update + + +function ICAUnmixMat = ICA(data) + +% Centering and PCA +allMovData=[]; +for i=1:size(data,3) + allMovData=[allMovData + data(:,:,i)]; +end + +m = mean(allMovData)'; +meanData = allMovData' - m*ones(1,size(allMovData,1)); +variance = cov(meanData'); +[E D] = eig(variance'); +[Val index] = sort(diag(D),'descend'); + +% Whitening + +whitenData = (sqrt (D)^-1) * E'; +dewhitenData = E * sqrt (D); +ProjData = whitenData*meanData; + + +% Computing Mixing and Demixing Matrix +X = ProjData; +[vectorSize, numSamples] = size(X); + +B = zeros(vectorSize); % Basis Vector + +% parameters +epsilon = 0.0001; % Stopping Criterion +maxNumIterations = 1000; +myy = 1; +stroke = 0; + +for l=1:vectorSize + + w=rand(vectorSize,1); + w = w - B * B' * w; + w = w / norm(w); + wOld = zeros(size(w)); + wOld2 = zeros(size(w)); + + % This Hyvarinen's fixed point algorithm from FASTICA Matlab Package. + for j=1:maxNumIterations + + if norm(w-wOld)< epsilon | norm(w+wOld)< epsilon + + break + + elseif (~stroke)& norm(w-wOld2) maxNumIterations/2; + % + myy = .5*myy; + wOld2 = wOld; + wOld = w; + % + EXGpow3 = (X * ((X' * w) .^ 3)) / numSamples; + Beta = w' * EXGpow3; + w = w - myy * (EXGpow3 - Beta * w) / (3 - Beta); + else + wOld2 = wOld; + wOld = w; + w = (X * ((X' * w) .^ 3)) / numSamples - 3 * w; + + end + w = w - B * B' * w; + w = w/norm(w); + end + + fprintf('Algoritm converged for [%d] iterations \n',j); + + + B(:, l) = w; + A(:,l) = dewhitenData * w; + W(l,:) = w' * whitenData; + + +end +ICAUnmixMat=W'; + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SigTreatment/ICA/ICAPreprocess.m b/SigTreatment/ICA/ICAPreprocess.m new file mode 100644 index 0000000..b1651cd --- /dev/null +++ b/SigTreatment/ICA/ICAPreprocess.m @@ -0,0 +1,64 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This Function Performs Independent component analysis for Training +% Validation and testing. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-10-20 / Tanuj Kumar Aluru / Creation +% 20xx-xx-xx / Author / Comment on update + +function [trData,vData,tData]=ICAPreprocess(sigTreated,trData,vData,tData) + + + + + W=sigTreated.sigSeperation.ICAUnmixMat; + + % Projecting ICA Unmixing Matrix on Training Sets +% + for i=1:size(trData,3) + for j=1:size(trData,4) + tempTrData(:,:,i,j)= trData(:,:,i,j)*W; + + end + end + +% +% % Projecting ICA Unmixing Matrix on Validation Sets + + for i=1:size(vData,3) + for j=1:size(vData,4) + tempVData(:,:,i,j)= vData(:,:,i,j)*W; + + end + end + +% % Projecting ICA Unmixing Matrix on Testing Sets + for i=1:size(tData,3) + for j=1:size(tData,4) + tempTData(:,:,i,j)= tData(:,:,i,j)*W; + + end + end + + trData=tempTrData; + vData=tempVData; + tData=tData; + \ No newline at end of file diff --git a/SigTreatment/PlotFFT.m b/SigTreatment/PlotFFT.m new file mode 100644 index 0000000..2e9d956 --- /dev/null +++ b/SigTreatment/PlotFFT.m @@ -0,0 +1,67 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Plot the FFT of a vector given in data. It has a routine to smooth the +% graphical representation using a sliding averaging window. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2013-08-01 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + + +function PlotFFT(data, sF, color) + + nS = length(data(:,1)); % It used to be sF*sT but due to change in lenght witht training data + sT = nS/sF; + tt = 0:1/sF:sT-1/sF; % Create vector of time + + if size(tt,2) ~= size(data,1) + nSd = size(data,1)- size(tt,2); + tt(1,end:end+nSd) = 0; + end + + %Fast Fourier Transform + NFFT = 2^nextpow2(nS); % Next power of 2 from number of samples +% NFFT = 1024; + dataf = fft(data,NFFT)/nS; + f = sF/2*linspace(0,1,NFFT/2+1); + m = 2*abs(dataf(1:NFFT/2+1)); + m(1:2) = 0; + + % Smooth + n = 3; + nn = 2; + for j = 1 :nn + clear mT; + for i = n+1: size(m,1)-n + mT(i,1) = mean(m(i-n:i+n)); + end + mT(end:end+n)=0; + m = mT; + end + + % Normalize + vMax = max(m); + m = m ./vMax; + +% figure(); + plot(f,m,color); + title('Single-Sided Amplitude Spectrum of y(t)') + xlabel('Frequency (Hz)') + ylabel('|Y(f)|') \ No newline at end of file diff --git a/SigTreatment/PreProcessing.m b/SigTreatment/PreProcessing.m index d25712d..fece001 100644 --- a/SigTreatment/PreProcessing.m +++ b/SigTreatment/PreProcessing.m @@ -1,74 +1,107 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to pre-process bioelectric recordings. It's call from the -% treatment GUI and requires the handles. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-07-07 / Max Ortiz / Moved out from the preProcessing botton -% 20xx-xx-xx / Author / Comment on update - -function sigTreated = PreProcessing(handles) - - % Get the recSession - set(handles.t_msg,'String','Loading recSession...'); - recSession = get(handles.t_recSession,'UserData'); - - %Remove movements if required %--------------------------------------- - movSel = get(handles.lb_movements,'Value'); - nM = str2double(get(handles.et_nM,'String')); - - if nM ~= length(movSel) - recSession = Split_recSession_Mov(movSel, recSession); - end - - %Remove channels if required %--------------------------------------- - chSel = get(handles.lb_nCh,'Value'); - - if length(recSession.nCh) ~= length(chSel) - allCh = get(handles.lb_nCh,'String'); - for i = 1 : length(chSel) - channels(i) = str2double(allCh(chSel(i))); - end - - recSession = Split_recSession_Ch(channels, recSession); - - end - - %Remove trasient %--------------------------------------------------- - cTp = str2double(get(handles.et_cTp,'String')); - sigTreated = RemoveTransient_cTp(recSession, cTp); - - %Add rest as a movement data %--------------------------------------- - if get(handles.cb_rest,'Value') - sigTreated = AddRestAsMovement(sigTreated, recSession); - % It informs the user of the changes made, however it conflics when - % a whole folder is treated -% set(handles.et_nM,'String',num2str(sigTreated.nM)); -% set(handles.lb_movements,'Value',1:sigTreated.nM); -% set(handles.lb_movements,'String',sigTreated.mov); - -% movSel = [movSel sigTreated.nM]; -% set(handles.lb_movements,'Value',movSel); - end - - % Upload sigtreated to the GUI---------------------------------------- - set(handles.t_sigTreated,'UserData',sigTreated); - set(handles.t_msg,'String','sigTreated uploaded'); - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to pre-process bioelectric recordings. It's called from the +% treatment GUI and requires the handles. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-07-07 / Max Ortiz / Moved out from the preProcessing botton +% 2014-12-06 / Max Ortiz / Added downsampling option +% 2014-12-28 / Max Ortiz / Added scaling option +% 20xx-xx-xx / Author / Comment on update + +function sigTreated = PreProcessing(handles) + + % Get the recSession + set(handles.t_msg,'String','Loading recSession...'); + recSession = get(handles.t_recSession,'UserData'); + + %Remove movements if required %--------------------------------------- + movSel = get(handles.lb_movements,'Value'); + nM = str2double(get(handles.et_nM,'String')); + + if nM ~= length(movSel) + recSession = Split_recSession_Mov(movSel, recSession); + end + + %Remove channels, if required %--------------------------------------- + chSel = get(handles.lb_nCh,'Value'); + + if length(recSession.nCh) ~= length(chSel) + allCh = get(handles.lb_nCh,'String'); + for i = 1 : length(chSel) + channels(i) = str2double(allCh(chSel(i))); + end + + recSession = Split_recSession_Ch(channels, recSession); + + end + + % Downsample, if required %--------------------------------------- + sF = str2double(get(handles.et_sF,'String')); + dS = str2double(get(handles.et_downsample,'String')); + if sF ~= dS + if recSession.sF < dS + errordlg('The downsample frequency is higher than the original sF','Error'); + set(handles.t_msg,'String','Error in downsample frequency'); + set(handles.et_downsample,'String',num2str(recSession.sF)); + else + recSession = Downsample_recSession(recSession, dS); + end + end + + % Add noise, if required %--------------------------------------- + pStd = str2double(get(handles.et_noise,'String')); + if pStd ~= 0 + recSession = AddNoise_recSession(recSession, pStd); + end + recSession.noise = pStd; + + % Scaling, if required %--------------------------------------- + scalingV = get(handles.pm_scaling,'Value'); + scalingS = get(handles.pm_scaling,'String'); + scalingBits = str2double(scalingS{scalingV}); + if scalingV ~= 1 % If other than None + recSession = Scale_recSession(recSession, scalingBits); + end + recSession.scaled = scalingBits; + + %% Change from recSession to sigTreated + + %Remove trasient %--------------------------------------------------- + cTp = str2double(get(handles.et_cTp,'String')); + sigTreated = RemoveTransient_cTp(recSession, cTp); + + %Add rest as a movement data %--------------------------------------- + if get(handles.cb_rest,'Value') + sigTreated = AddRestAsMovement(sigTreated, recSession); + % It informs the user of the changes made, however it conflics when + % a whole folder is treated +% set(handles.et_nM,'String',num2str(sigTreated.nM)); +% set(handles.lb_movements,'Value',1:sigTreated.nM); +% set(handles.lb_movements,'String',sigTreated.mov); + +% movSel = [movSel sigTreated.nM]; +% set(handles.lb_movements,'Value',movSel); + end + + % Upload sigtreated to the GUI---------------------------------------- + set(handles.t_sigTreated,'UserData',sigTreated); + set(handles.t_msg,'String','sigTreated uploaded'); + diff --git a/SigTreatment/RemoveTransient_cTp.m b/SigTreatment/RemoveTransient_cTp.m index 6ed9c0d..17e4be2 100644 --- a/SigTreatment/RemoveTransient_cTp.m +++ b/SigTreatment/RemoveTransient_cTp.m @@ -1,66 +1,66 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to compute Traning Data according to the contraction time -% percentage (cTp) -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 20xx-xx-xx / Max Ortiz / Creation -% 2011-07-19 / Max Ortiz / Updated to consider cTp before and after -% contraction -% 20xx-xx-xx / Author / Comment on update - -function sigTreated = RemoveTransient_cTp(recSession, cTp) - - sF = recSession.sF; - cT = recSession.cT; - rT = recSession.rT; - nR = recSession.nR; - nM = recSession.nM; - tdata = recSession.tdata; - - % New structured for the signal treated - sigTreated = recSession; - sigTreated.cTp = cTp; - eRed = (1-cTp)/2; % effective reduction at the begining and at the end of contraction - - % Removed useless fields for following operations - if isfield(sigTreated,'tdata') - sigTreated = rmfield(sigTreated,'tdata'); - end - if isfield(sigTreated,'trdata') - sigTreated = rmfield(sigTreated,'trdata'); - end - - for ex = 1 : nM - tempdata =[]; - for rep = 1 : nR - % Samples of the exersice to be consider for training - % (sF*cT*(cTp-1)) Number of the samples that wont be consider for training - % (sF*cT*rep) Number of samples that takes a contraction - % (sF*rT*rep) Number of samples that takes a relaxation - is = fix((sF*cT*(1-cTp-eRed)) + (sF*cT*(rep-1)) + (sF*rT*(rep-1)) + 1); - fs = fix((sF*cT*(cTp+eRed)) + (sF*cT*(rep-1)) + (sF*rT*(rep-1))); - tempdata = [tempdata ; tdata(is:fs,:,ex)]; - end - trData(:,:,ex) = tempdata; - end - - sigTreated.trData = trData; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to compute Traning Data according to the contraction time +% percentage (cTp) +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 20xx-xx-xx / Max Ortiz / Creation +% 2011-07-19 / Max Ortiz / Updated to consider cTp before and after +% contraction +% 20xx-xx-xx / Author / Comment on update + +function sigTreated = RemoveTransient_cTp(recSession, cTp) + + sF = recSession.sF; + cT = recSession.cT; + rT = recSession.rT; + nR = recSession.nR; + nM = recSession.nM; + tdata = recSession.tdata; + + % New structured for the signal treated + sigTreated = recSession; + sigTreated.cTp = cTp; + eRed = (1-cTp)/2; % effective reduction at the begining and at the end of contraction + + % Removed useless fields for following operations + if isfield(sigTreated,'tdata') + sigTreated = rmfield(sigTreated,'tdata'); + end + if isfield(sigTreated,'trdata') + sigTreated = rmfield(sigTreated,'trdata'); + end + + for ex = 1 : nM + tempdata =[]; + for rep = 1 : nR + % Samples of the exersice to be consider for training + % (sF*cT*(cTp-1)) Number of the samples that wont be consider for training + % (sF*cT*rep) Number of samples that takes a contraction + % (sF*rT*rep) Number of samples that takes a relaxation + is = fix((sF*cT*(1-cTp-eRed)) + (sF*cT*(rep-1)) + (sF*rT*(rep-1)) + 1); + fs = fix((sF*cT*(cTp+eRed)) + (sF*cT*(rep-1)) + (sF*rT*(rep-1))); + tempdata = [tempdata ; tdata(is:fs,:,ex)]; + end + trData(:,:,ex) = tempdata; + end + + sigTreated.trData = trData; end \ No newline at end of file diff --git a/SigTreatment/Scale_recSession.m b/SigTreatment/Scale_recSession.m new file mode 100644 index 0000000..1a3c005 --- /dev/null +++ b/SigTreatment/Scale_recSession.m @@ -0,0 +1,66 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to scale the data in a recording session (recSession) +% to integers as adquired by the ADC using 2^(n-1) n=bits +% (n-1) is used because one bit is required by the sign (+ or -) +% n (bits) = 8, 10, 12, 14, 16 +% 2^n = 256, 1024, 4096, 16384, 32768 +% 2^(n-1) = 128, 256, 1024, 4096, 16384 +% +% Given the signal x, make values of x fall on the interval [a,b] +% x_scaled = (x-min(x))*(b-a)/(max(x)-min(x)) + a; +% +% input: recSession (struct) +% scalingBits = number of bits to be fitted +% output: recSession (struct) +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2014-12-28 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function recSession = Scale_recSession(recSession, scalingBits) + +if scalingBits == 8 + a = 128; +elseif scalingBits == 10 + a = 256; +elseif scalingBits == 12 + a = 1024; +elseif scalingBits == 14 + a = 4096; +elseif scalingBits == 16 + a = 16384; +else + errordlg('Scaling failed','Error'); + return; +end +b = a; +a = a * -1; + +% Range of the original aquisition +minX = -5; +maxX = 5; + +% Scale +for i=1:length(recSession.tdata(1,1,:)) + recSession.tdata(:,:,i) = round((recSession.tdata(:,:,i) - minX) .* (b-a) ./ (maxX-minX) + a); +end + + diff --git a/SigTreatment/SignalSeparationRealtime.m b/SigTreatment/SignalSeparationRealtime.m new file mode 100644 index 0000000..bd34e34 --- /dev/null +++ b/SigTreatment/SignalSeparationRealtime.m @@ -0,0 +1,33 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% This function performs Independent Coponent Analysis in Realtime +% as Prprocessing using Fast Fixed Point Algorithm . +% +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-11-01 / Tanuj Kumar Aluru / Creation +% 20xx-xx-xx / Author / Comment on update + +function data = SignalSeparationRealtime(patRec, data) + + if strcmp(patRec.sigSeperation.Alg,'ICA') + data=data*patRec.sigSeperation.ICAUnmixMat; + end +end diff --git a/SigTreatment/Spatial Filters/SpatialFilterDDF.m b/SigTreatment/Spatial Filters/SpatialFilterDDF.m index 2c98b76..e4d6b9f 100644 --- a/SigTreatment/Spatial Filters/SpatialFilterDDF.m +++ b/SigTreatment/Spatial Filters/SpatialFilterDDF.m @@ -1,52 +1,52 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to apply the spatial filter 2A - B - C (double differential) -% conidering the electrodes circunferencial arrengment ...| C | A | B |... -% the number of channels can be grater than 3 but not less than 3 -% NOTE: Considerations must be made on how the electrodes were conected to -% the amplifiers inputs -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-07-18 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function data = SpatialFilterDDF(data) - - nCh = length(data(1,:,1)); - - % Compute the filter - for i = 1 : nCh - A = i; - % Compute B - if i == nCh - B = 1; - else - B = i + 1; - end - % Compute C - if i == 1 - C = nCh; - else - C = i - 1; - end - tempData(:,A,:) = (data(:,A,:) .* 2) - data(:,B,:) - data(:,C,:); - end - - data = tempData; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to apply the spatial filter 2A - B - C (double differential) +% conidering the electrodes circunferencial arrengment ...| C | A | B |... +% the number of channels can be grater than 3 but not less than 3 +% NOTE: Considerations must be made on how the electrodes were conected to +% the amplifiers inputs +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-18 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function data = SpatialFilterDDF(data) + + nCh = length(data(1,:,1)); + + % Compute the filter + for i = 1 : nCh + A = i; + % Compute B + if i == nCh + B = 1; + else + B = i + 1; + end + % Compute C + if i == 1 + C = nCh; + else + C = i - 1; + end + tempData(:,A,:) = (data(:,A,:) .* 2) - data(:,B,:) - data(:,C,:); + end + + data = tempData; diff --git a/SigTreatment/Spatial Filters/SpatialFilterDDFAbs.m b/SigTreatment/Spatial Filters/SpatialFilterDDFAbs.m index fba57dd..576263c 100644 --- a/SigTreatment/Spatial Filters/SpatialFilterDDFAbs.m +++ b/SigTreatment/Spatial Filters/SpatialFilterDDFAbs.m @@ -1,60 +1,60 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to apply the spatial filter abs(2A - B - C) double differential -% conidering the electrodes circunferencial arrengment ...| C | A | B |... -% the number of channels can be grater than 3 but not less than 3 -% It returns a ABS value as well -% NOTE: Considerations must be made on how the electrodes were conected to -% the amplifiers inputs -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-07-18 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function data = SpatialFilterDDFAbs(data) - - nCh = length(data(1,:,1)); - data = abs(data); - - % Compute the filter - for i = 1 : nCh - A = i; - % Compute B - if i == nCh - B = 1; - else - B = i + 1; - end - % Compute C - if i == 1 - C = nCh; - else - C = i - 1; - end - tempData(:,A,:) = (data(:,A,:) .* 2) - data(:,B,:) - data(:,C,:); - - % Eliminates negative values - for j = 1 : size(tempData,3) - [v vIdx] = find(tempData(:,A,j)' < 0); - tempData(vIdx,A,j) = 0; - end - end - - data = tempData; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to apply the spatial filter abs(2A - B - C) double differential +% conidering the electrodes circunferencial arrengment ...| C | A | B |... +% the number of channels can be grater than 3 but not less than 3 +% It returns a ABS value as well +% NOTE: Considerations must be made on how the electrodes were conected to +% the amplifiers inputs +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-18 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function data = SpatialFilterDDFAbs(data) + + nCh = length(data(1,:,1)); + data = abs(data); + + % Compute the filter + for i = 1 : nCh + A = i; + % Compute B + if i == nCh + B = 1; + else + B = i + 1; + end + % Compute C + if i == 1 + C = nCh; + else + C = i - 1; + end + tempData(:,A,:) = (data(:,A,:) .* 2) - data(:,B,:) - data(:,C,:); + + % Eliminates negative values + for j = 1 : size(tempData,3) + [v vIdx] = find(tempData(:,A,j)' < 0); + tempData(vIdx,A,j) = 0; + end + end + + data = tempData; diff --git a/SigTreatment/Spatial Filters/SpatialFilterSDF.m b/SigTreatment/Spatial Filters/SpatialFilterSDF.m index 094e591..fae606e 100644 --- a/SigTreatment/Spatial Filters/SpatialFilterSDF.m +++ b/SigTreatment/Spatial Filters/SpatialFilterSDF.m @@ -1,48 +1,48 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to apply the spatial filter A - B (single differential) conidering -% the electrodes circunferencial arrengment ... | A | B | C | ... -% the number of channels can be grater than 4 but not less than 2 -% NOTE: Considerations must be made on how the electrodes were conected to -% the amplifiers inputs -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-07-18 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function data = SpatialFilterSDF(data) - - nCh = length(data(1,:,1)); - - % Compute the filter - for i = 1 : nCh - A = i; - % Compute B - if i == nCh - B = 1; - else - B = i + 1; - end - tempData(:,A,:) = data(:,A,:) + data(:,B,:); - end - - data = tempData; - - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to apply the spatial filter A - B (single differential) conidering +% the electrodes circunferencial arrengment ... | A | B | C | ... +% the number of channels can be grater than 4 but not less than 2 +% NOTE: Considerations must be made on how the electrodes were conected to +% the amplifiers inputs +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-18 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function data = SpatialFilterSDF(data) + + nCh = length(data(1,:,1)); + + % Compute the filter + for i = 1 : nCh + A = i; + % Compute B + if i == nCh + B = 1; + else + B = i + 1; + end + tempData(:,A,:) = data(:,A,:) + data(:,B,:); + end + + data = tempData; + + diff --git a/SigTreatment/Spatial Filters/SpatialFilter_1.m b/SigTreatment/Spatial Filters/SpatialFilter_1.m new file mode 100644 index 0000000..360bfb7 --- /dev/null +++ b/SigTreatment/Spatial Filters/SpatialFilter_1.m @@ -0,0 +1,73 @@ +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to apply the spatial filter 2A-B-D considering a circunferencial +% arrengment of electrode A - B - C- D - ... +% The number of channels can be grater than 4 but not less than 3 +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-18 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + + +function sigTreated = SpatialFilter_1() + + [file, path] = uigetfile('*.mat'); + if ~isequal(file, 0) + load([path,file]); + if exist('recSession','var') + nCh = length(recSession.tdata(1,:,1)); + sigTreated = recSession; + + % Removed useless fields for following operations + if isfield(sigTreated,'tdata') + sigTreated = rmfield(sigTreated,'tdata'); + end + if isfield(sigTreated,'trdata') + sigTreated = rmfield(sigTreated,'trdata'); + end + + % Compute the filter + if sigTreated.nCh == nCh; + trdata = recSession.trdata; + for i = 1 : nCh + A = i; + % Compute B + if i == nCh + B = 1; + else + B = i + 1; + end + % Compute D + if i == 1 + D = nCh; + else + D = i - 1; + end + tempData(:,A,:) = (trdata(:,A,:) .* 2) - trdata(:,B,:) - trdata(:,D,:); + end + else + disp('nCh does not match tdata matrix'); + end + + sigTreated.trdata = tempdata; + + end + end +end + diff --git a/SigTreatment/Split_recSession_Ch.m b/SigTreatment/Split_recSession_Ch.m index b321c99..08b0742 100644 --- a/SigTreatment/Split_recSession_Ch.m +++ b/SigTreatment/Split_recSession_Ch.m @@ -1,29 +1,29 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function the slipt the information of recording sessions in different -% channels -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-07-21 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - -function recSession = Split_recSession_Ch(channels, recSession) - - recSession.tdata = recSession.tdata(:,channels,:); - recSession.nCh = channels; +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function the slipt the information of recording sessions in different +% channels +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-07-21 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + +function recSession = Split_recSession_Ch(channels, recSession) + + recSession.tdata = recSession.tdata(:,channels,:); + recSession.nCh = channels; diff --git a/SigTreatment/Split_recSession_Mov.m b/SigTreatment/Split_recSession_Mov.m index 84b46ed..cfc2002 100644 --- a/SigTreatment/Split_recSession_Mov.m +++ b/SigTreatment/Split_recSession_Mov.m @@ -1,32 +1,32 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to reduced the number of recorded movements -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2011-08-04 / Max Ortiz / Creation -% 20xx-xx-xx / Author / Comment on update - - - -function recSession = Split_recSession_Mov(mov, recSession) - - recSession.tdata = recSession.tdata(:,:,mov); - recSession.nM = length(mov); - recSession.mov = recSession.mov(mov); +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to reduced the number of recorded movements +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2011-08-04 / Max Ortiz / Creation +% 20xx-xx-xx / Author / Comment on update + + + +function recSession = Split_recSession_Mov(mov, recSession) + + recSession.tdata = recSession.tdata(:,:,mov); + recSession.nM = length(mov); + recSession.mov = recSession.mov(mov); diff --git a/SigTreatment/TreatData.m b/SigTreatment/TreatData.m index 98777a4..1330d1f 100644 --- a/SigTreatment/TreatData.m +++ b/SigTreatment/TreatData.m @@ -1,73 +1,87 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to treat the raw data -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2009-07-20 / Max Ortiz / Creation -% 2011-07-27 / Max Ortiz / Update to coding standard -% 2011-07-27 / Max Ortiz / Moved the "updated sigTreated" instructions out -% of the GUI push bottom -% 20xx-xx-xx / Author / Comment on update - -function sigTreated = TreatData(handles, sigTreated) - - %% Update sigTreated - fFilters = get(handles.pm_frequencyFilters,'String'); - fFiltersSel = get(handles.pm_frequencyFilters,'Value'); - sigTreated.fFilter = fFilters(fFiltersSel); - - sFilters = get(handles.pm_spatialFilters,'String'); - sFiltersSel = get(handles.pm_spatialFilters,'Value'); - sigTreated.sFilter = sFilters(sFiltersSel); - - sigTreated.eCt = sigTreated.cT * sigTreated.cTp; % Effective contraction time - sigTreated.wOverlap = str2double(get(handles.et_wOverlap,'String')); - sigTreated.tW = str2double(get(handles.et_tw,'String')); - sigTreated.nW = str2double(get(handles.et_nw,'String')); - sigTreated.trSets = str2double(get(handles.et_trN,'String')); - sigTreated.vSets = str2double(get(handles.et_vN,'String')); - sigTreated.tSets = str2double(get(handles.et_tN,'String')); - - twSegMethods = get(handles.pm_twSegMethod,'String'); - twSegMethodsSel = get(handles.pm_twSegMethod,'Value'); - sigTreated.twSegMethod = twSegMethods(twSegMethodsSel); - - - %% Filters - set(handles.t_msg,'String','Applying filters...'); - sigTreated.trData = ApplyFilters(sigTreated, sigTreated.trData); - set(handles.t_msg,'String','Filters applied'); - - - %% Split the data into the time windows - set(handles.t_msg,'String','Segmenting data...'); - disp('Segmenting data...'); - [trData, vData, tData] = GetData(sigTreated); - %Remove raw trated data - sigTreated = rmfield(sigTreated,'trData'); - set(handles.t_msg,'String','Data segmented'); - disp('Data segmented'); - - % add the new sets of tw for tr, v and t - sigTreated.trData = trData; - sigTreated.vData = vData; - sigTreated.tData = tData; - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to treat the raw data +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2009-07-20 / Max Ortiz / Creation +% 2011-07-27 / Max Ortiz / Update to coding standard +% 2011-07-27 / Max Ortiz / Moved the "updated sigTreated" instructions out +% of the GUI push bottom +% 20xx-xx-xx / Author / Comment on update + +function sigTreated = TreatData(handles, sigTreated) + + %% Update sigTreated + fFilters = get(handles.pm_frequencyFilters,'String'); + fFiltersSel = get(handles.pm_frequencyFilters,'Value'); + sigTreated.fFilter = fFilters(fFiltersSel); + + sFilters = get(handles.pm_spatialFilters,'String'); + sFiltersSel = get(handles.pm_spatialFilters,'Value'); + sigTreated.sFilter = sFilters(sFiltersSel); + + sigTreated.eCt = sigTreated.cT * sigTreated.cTp; % Effective contraction time + sigTreated.wOverlap = str2double(get(handles.et_wOverlap,'String')); + sigTreated.tW = str2double(get(handles.et_tw,'String')); + sigTreated.nW = str2double(get(handles.et_nw,'String')); + sigTreated.trSets = str2double(get(handles.et_trN,'String')); + sigTreated.vSets = str2double(get(handles.et_vN,'String')); + sigTreated.tSets = str2double(get(handles.et_tN,'String')); + + twSegMethods = get(handles.pm_twSegMethod,'String'); + twSegMethodsSel = get(handles.pm_twSegMethod,'Value'); + sigTreated.twSegMethod = twSegMethods(twSegMethodsSel); + + allSigSeperaAlg = get(handles.pm_SignalSeparation,'String'); + selSigSeperaAlg = allSigSeperaAlg(get(handles.pm_SignalSeparation,'Value')); + sigTreated.sigSeperation.Alg= selSigSeperaAlg; + + %% Filters + set(handles.t_msg,'String','Applying filters...'); + sigTreated.trData = ApplyFilters(sigTreated, sigTreated.trData); + set(handles.t_msg,'String','Filters applied'); + + %% Signal Separation Alg - Compute + + set(handles.t_msg,'String','Computing SP...'); + sigTreated=ComputeSignalSeparation(sigTreated); + set(handles.t_msg,'String','SP Computed'); + + + %% Split the data into the time windows + set(handles.t_msg,'String','Segmenting data...'); + disp('Segmenting data...'); + [trData, vData, tData] = GetData(sigTreated); + %Remove raw trated data + sigTreated = rmfield(sigTreated,'trData'); + set(handles.t_msg,'String','Data segmented'); + disp('Data segmented'); + + %% Sig Separation Algortihms - Apply + + [trData vData tData]=ApplySignalSeparation(sigTreated,trData, vData, tData); + + + % add the new sets of tw for tr, v and t + sigTreated.trData = trData; + sigTreated.vData = vData; + sigTreated.tData = tData; + end \ No newline at end of file diff --git a/VRE/CalculatePathLength.m b/VRE/CalculatePathLength.m index 184ca84..457a751 100644 --- a/VRE/CalculatePathLength.m +++ b/VRE/CalculatePathLength.m @@ -1,81 +1,81 @@ -% pathLength = CalculatePathLength(ssTraj,target,allowance) -% -% Calculates path length of a state space trajectory, removing any -% movements within the allowance space. -% -% INPUTS: -% -% ssTraj - Coordinates of the trajectrory in state space, each -% coordinate should be stored in the rows of the matrix ssTraj. -% -% target - Coordinates to the target in state space, should be a -% row vector. -% -% allowance - the number of degrees that are considered to be close -% enough to the target. The allowance space is contained by the box -% target (plus minus) allowance / 2. - -function pathLength = CalculatePathLength(ssTraj,target,allowance) - -pathLength = 0; - -posOld = ssTraj(1,:); -for iPos = 2:size(ssTraj,1) - - posNew = ssTraj(iPos,:); - - % Check if system jumped into allowance space - if InAllowanceSpace(posNew,target,allowance) && ~InAllowanceSpace(posOld,target,allowance) - - crossingPoint = FindCrossing(posOld,posNew,target,allowance); - pathLength = pathLength + norm(crossingPoint - posOld); - - % Check if system jumped out of allowance space - elseif InAllowanceSpace(posOld,target,allowance) && ~InAllowanceSpace(posNew,target,allowance) - - crossingPoint = FindCrossing( posNew, posOld,target,allowance ); - pathLength = pathLength + norm(crossingPoint - posNew); - - % Check if trajectory is inside allowance space - elseif InAllowanceSpace(posOld,target,allowance) && InAllowanceSpace(posNew,target,allowance) - - % Check if trajectory is outside of allowance space - elseif ~InAllowanceSpace(posOld,target,allowance) && ~InAllowanceSpace(posNew,target,allowance) - pathLength = pathLength + norm(posNew - posOld); - end - - posOld = posNew; - -end - -function crossingPoint = FindCrossing(posOut,posIn,target,allowance) - -len = norm( posIn - posOut ); % Calculate length between posOld and posNew -dir = (posIn - posOut)./len; % Calculate unity direction between pos1 and pos2 - -while len > 1e-5 - - posTmp = posOut+len/2.*dir; % Take half a step between out and in - - % If the new position is inside the allowance space we have stepped too far - if InAllowanceSpace(posTmp,target,allowance) - posIn = posTmp; % move the inside position to tmp position - - % else if it is outside we have stepped to short - else - posOut = posTmp; % move the outside position to tmp position - end - - len = norm( posIn - posOut ); % recalculate the length between the two points - -end - -crossingPoint = posIn; - -function inside = InAllowanceSpace(pos,target,allowance) - -inside = 0; - -if norm(pos-target) < allowance - inside = 1; +% pathLength = CalculatePathLength(ssTraj,target,allowance) +% +% Calculates path length of a state space trajectory, removing any +% movements within the allowance space. +% +% INPUTS: +% +% ssTraj - Coordinates of the trajectrory in state space, each +% coordinate should be stored in the rows of the matrix ssTraj. +% +% target - Coordinates to the target in state space, should be a +% row vector. +% +% allowance - the number of degrees that are considered to be close +% enough to the target. The allowance space is contained by the box +% target (plus minus) allowance / 2. + +function pathLength = CalculatePathLength(ssTraj,target,allowance) + +pathLength = 0; + +posOld = ssTraj(1,:); +for iPos = 2:size(ssTraj,1) + + posNew = ssTraj(iPos,:); + + % Check if system jumped into allowance space + if InAllowanceSpace(posNew,target,allowance) && ~InAllowanceSpace(posOld,target,allowance) + + crossingPoint = FindCrossing(posOld,posNew,target,allowance); + pathLength = pathLength + norm(crossingPoint - posOld); + + % Check if system jumped out of allowance space + elseif InAllowanceSpace(posOld,target,allowance) && ~InAllowanceSpace(posNew,target,allowance) + + crossingPoint = FindCrossing( posNew, posOld,target,allowance ); + pathLength = pathLength + norm(crossingPoint - posNew); + + % Check if trajectory is inside allowance space + elseif InAllowanceSpace(posOld,target,allowance) && InAllowanceSpace(posNew,target,allowance) + + % Check if trajectory is outside of allowance space + elseif ~InAllowanceSpace(posOld,target,allowance) && ~InAllowanceSpace(posNew,target,allowance) + pathLength = pathLength + norm(posNew - posOld); + end + + posOld = posNew; + +end + +function crossingPoint = FindCrossing(posOut,posIn,target,allowance) + +len = norm( posIn - posOut ); % Calculate length between posOld and posNew +dir = (posIn - posOut)./len; % Calculate unity direction between pos1 and pos2 + +while len > 1e-5 + + posTmp = posOut+len/2.*dir; % Take half a step between out and in + + % If the new position is inside the allowance space we have stepped too far + if InAllowanceSpace(posTmp,target,allowance) + posIn = posTmp; % move the inside position to tmp position + + % else if it is outside we have stepped to short + else + posOut = posTmp; % move the outside position to tmp position + end + + len = norm( posIn - posOut ); % recalculate the length between the two points + +end + +crossingPoint = posIn; + +function inside = InAllowanceSpace(pos,target,allowance) + +inside = 0; + +if norm(pos-target) < allowance + inside = 1; end \ No newline at end of file diff --git a/VRE/CreateMovMatrix.m b/VRE/CreateMovMatrix.m index 6c2455c..0de92c5 100644 --- a/VRE/CreateMovMatrix.m +++ b/VRE/CreateMovMatrix.m @@ -1,111 +1,111 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function used by the StateSpaceTracker, creates a matrix that corresponds -% to the different movements trained in the patRec. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-11-23 / Joel Falk-Dahlin / Creation -% 20xx-xx-xx / Author / Comment on update - -function [movMatrix, movMatrixNorm] = CreateMovMatrix(patRec) - - %indMovIdx = patRec.indMovIdx; % Read movement indexes - %movMatrix = zeros(length(indMovIdx),3); % Preset movMatrix for speed - - %for i = 1:length(indMovIdx) % Loop through all output movement patRec has - k = 1; - for i = patRec.indMovIdx - - %moves = regexp(patRec.mov{indMovIdx(i)},'\W+\W','split'); % Read current movement (could be individual or simultaneous) - moves = regexp(patRec.mov{i},'\W+\W','split'); % Read current movement (could be individual or simultaneous) - - outMovTmp = []; - - for j = 1:length(moves) % Loop through all movement that current output is (1 if individual several if simultaneous) - - if strcmp(moves{j},'Open Hand') % If read movement is Open hand, map to 1 - outMovTmp = [outMovTmp; 1]; - end - - if strcmp(moves{j},'Close Hand') % If read movement is Close Hand, map to 2 - outMovTmp = [outMovTmp; 2]; - end - - if strcmp(moves{j},'Flex Hand') % If read movement is Flex Hand, map to 3 - outMovTmp = [outMovTmp; 3]; - end - - if strcmp(moves{j},'Extend Hand') % If read movement is Extend Hand, map to 4 - outMovTmp = [outMovTmp; 4]; - end - - if strcmp(moves{j},'Pronation') % If read movement is Pronation, map to 5 - outMovTmp = [outMovTmp; 5]; - end - - if strcmp(moves{j},'Supination') % If read movement is Supination, map to 6 - outMovTmp = [outMovTmp; 6]; - end - - if strcmp(moves{j},'Rest') % If read movement is Rest, map to 7 - outMovTmp = [outMovTmp; 7]; - end - - end - - movement = [0,0,0]; - - for j = 1:length(outMovTmp) - - switch outMovTmp(j) - case 1 % Open Hand - dim = 1; - dir = 1; - case 2 % Close Hand - dim = 1; - dir = -1; - case 3 % Flex Hand - dim = 2; - dir = 1; - case 4 % Extend Hand - dim = 2; - dir = -1; - case 5 % Pronation - dim = 3; - dir = 1; - case 6 % Supination - dim = 3; - dir = -1; - case 7 - dim = []; - dir = 0; - end - - movement(dim) = movement(dim) + dir; - - end - - movMatrix(k,:) = movement; - k = k+1; - - end - - movMatrixNorm = movMatrix(1:end-1,:) ./ repmat( sqrt( diag(movMatrix(1:end-1,:)*movMatrix(1:end-1,:)') ), [1,3] ) ; - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function used by the StateSpaceTracker, creates a matrix that corresponds +% to the different movements trained in the patRec. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-11-23 / Joel Falk-Dahlin / Creation +% 20xx-xx-xx / Author / Comment on update + +function [movMatrix, movMatrixNorm] = CreateMovMatrix(patRec) + + %indMovIdx = patRec.indMovIdx; % Read movement indexes + %movMatrix = zeros(length(indMovIdx),3); % Preset movMatrix for speed + + %for i = 1:length(indMovIdx) % Loop through all output movement patRec has + k = 1; + for i = patRec.indMovIdx + + %moves = regexp(patRec.mov{indMovIdx(i)},'\W+\W','split'); % Read current movement (could be individual or simultaneous) + moves = regexp(patRec.mov{i},'\W+\W','split'); % Read current movement (could be individual or simultaneous) + + outMovTmp = []; + + for j = 1:length(moves) % Loop through all movement that current output is (1 if individual several if simultaneous) + + if strcmp(moves{j},'Open Hand') % If read movement is Open hand, map to 1 + outMovTmp = [outMovTmp; 1]; + end + + if strcmp(moves{j},'Close Hand') % If read movement is Close Hand, map to 2 + outMovTmp = [outMovTmp; 2]; + end + + if strcmp(moves{j},'Flex Hand') % If read movement is Flex Hand, map to 3 + outMovTmp = [outMovTmp; 3]; + end + + if strcmp(moves{j},'Extend Hand') % If read movement is Extend Hand, map to 4 + outMovTmp = [outMovTmp; 4]; + end + + if strcmp(moves{j},'Pronation') % If read movement is Pronation, map to 5 + outMovTmp = [outMovTmp; 5]; + end + + if strcmp(moves{j},'Supination') % If read movement is Supination, map to 6 + outMovTmp = [outMovTmp; 6]; + end + + if strcmp(moves{j},'Rest') % If read movement is Rest, map to 7 + outMovTmp = [outMovTmp; 7]; + end + + end + + movement = [0,0,0]; + + for j = 1:length(outMovTmp) + + switch outMovTmp(j) + case 1 % Open Hand + dim = 1; + dir = 1; + case 2 % Close Hand + dim = 1; + dir = -1; + case 3 % Flex Hand + dim = 2; + dir = 1; + case 4 % Extend Hand + dim = 2; + dir = -1; + case 5 % Pronation + dim = 3; + dir = 1; + case 6 % Supination + dim = 3; + dir = -1; + case 7 + dim = []; + dir = 0; + end + + movement(dim) = movement(dim) + dir; + + end + + movMatrix(k,:) = movement; + k = k+1; + + end + + movMatrixNorm = movMatrix(1:end-1,:) ./ repmat( sqrt( diag(movMatrix(1:end-1,:)*movMatrix(1:end-1,:)') ), [1,3] ) ; + diff --git a/VRE/CurrentPosition.m b/VRE/CurrentPosition.m new file mode 100644 index 0000000..eb06e1e --- /dev/null +++ b/VRE/CurrentPosition.m @@ -0,0 +1,88 @@ +function obj = CurrentPosition(varargin) + + persistent target allowance positions distance movements classifier tempdistance + + if(length(varargin) < 1) + return + end + obj = []; + if ischar(varargin{1}) + switch(varargin{1}) + case 'init' + classifier = varargin{2}; + allowance = varargin{3}; + movements = varargin{4}; + + max = 0; + for i = 1:length(movements) + if movements(i).idVRE > max + max = movements(i).idVRE; + end + end + + positions = zeros(1,max); + case 'target' + + distance = 0; + tempdistance = 0; + + index = varargin{2}; + dist = varargin{3}; + + movement = movements(index); + target = zeros(1,length(positions)); + + for i = 1:length(movement) + if movement(i).vreDir == 0 + target(movement(i).idVRE) = -1 * dist; + else + target(movement(i).idVRE) = dist; + end + end + + positions = zeros(1,length(positions)); + + case 'move' + %Make sure within limit and Check if inside, if not, move it. + newPos = positions; + + index = varargin{2}; + speeds = varargin{3}; + + movement = movements(index); + + for i = 1:length(movement) + id = movement(i).idVRE; + if id > 0 + if movement(i).vreDir == 0 + newPos(id) = newPos(id) - speeds(index(i)); + else + newPos(id) = newPos(id) + speeds(index(i)); + end + end + end + + movedDistance = abs(EuclidDist(newPos,positions)); + + if abs(EuclidDist(newPos,target)) < allowance + tempdistance = tempdistance + movedDistance; + else + distance = distance + movedDistance + tempdistance; + tempdistance = 0; + end + positions = newPos; + + case 'get' + start = zeros(1,length(target)); + + dofs = length(nonzeros(target)); + + l = sqrt((5^2)/dofs); + + finish = abs(target) - (target~=0)*l; %Get shortest distance moving from start. + %Perfect, incl Allowance. + perfect = EuclidDist(finish,start); + obj = perfect/distance; + end + end +end \ No newline at end of file diff --git a/VRE/MoveSS.m b/VRE/MoveSS.m index 86a2f55..2571ab1 100644 --- a/VRE/MoveSS.m +++ b/VRE/MoveSS.m @@ -1,64 +1,64 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function used by the StateSpaceTracker, moves the system in the state -% space using -% - Last Position -% - movement matrix -% - outMovIdx ( separate for all combined movements, used with movement matrix ) -% - outMov ( multiple indexes, one for each DoF, used with speeds ) -% - speeds -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-11-23 / Joel Falk-Dahlin / Creation -% 20xx-xx-xx / Author / Comment on update - -function ssPos = MoveSS(ssPos,movMatrix,outMov,speeds) - -% Only take the desired movements from movMatrix -%movMat = movMatrix(outMovIdx,:); -movMat = movMatrix(outMov,:); -% Take the desired speeds (1 for individual, several for simultaneous) -speeds = speeds(outMov); - -for i = 1:size(movMat,1) - %movMat(i,:) = movMat(i,:)./norm(movMat(i,:)); % Normalize each movement - movMat(i,:) = movMat(i,:).*speeds(i); -end - -% Sum up simultaneous movements to a single movement -movMat = sum(movMat,1); - - -ssPos = ssPos + movMat; % Move the position in state space - -% % Limit each dimension to the range [-50,50] (Nichlas percent units) -% ssPos( ssPos > 50 ) = 50; -% ssPos( ssPos < -50 ) = -50; - -% Create Boundary inside state space -ssPos( ssPos > 90 ) = 90; -ssPos( ssPos < -90 ) = -90; - -% Open / Close hand work differently, can only move a 100 degrees -if ssPos(1) > 50 - ssPos(1) = 50; -elseif ssPos(1) < -50 - ssPos(1) = -50; -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function used by the StateSpaceTracker, moves the system in the state +% space using +% - Last Position +% - movement matrix +% - outMovIdx ( separate for all combined movements, used with movement matrix ) +% - outMov ( multiple indexes, one for each DoF, used with speeds ) +% - speeds +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-11-23 / Joel Falk-Dahlin / Creation +% 20xx-xx-xx / Author / Comment on update + +function ssPos = MoveSS(ssPos,movMatrix,outMov,speeds) + +% Only take the desired movements from movMatrix +%movMat = movMatrix(outMovIdx,:); +movMat = movMatrix(outMov,:); +% Take the desired speeds (1 for individual, several for simultaneous) +speeds = speeds(outMov); + +for i = 1:size(movMat,1) + %movMat(i,:) = movMat(i,:)./norm(movMat(i,:)); % Normalize each movement + movMat(i,:) = movMat(i,:).*speeds(i); +end + +% Sum up simultaneous movements to a single movement +movMat = sum(movMat,1); + + +ssPos = ssPos + movMat; % Move the position in state space + +% % Limit each dimension to the range [-50,50] (Nichlas percent units) +% ssPos( ssPos > 50 ) = 50; +% ssPos( ssPos < -50 ) = -50; + +% Create Boundary inside state space +ssPos( ssPos > 90 ) = 90; +ssPos( ssPos < -90 ) = -90; + +% Open / Close hand work differently, can only move a 100 degrees +if ssPos(1) > 50 + ssPos(1) = 50; +elseif ssPos(1) < -50 + ssPos(1) = -50; +end diff --git a/VRE/TACTest.m b/VRE/TACTest.m index 09b4e7c..ff711de 100644 --- a/VRE/TACTest.m +++ b/VRE/TACTest.m @@ -1,388 +1,461 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% ------------------- Function Description ------------------ -% A TAC Test is performed using the VRE. It then uses the TacTestResult -% function to display the gathered data. -% -% The function is written in such a way that it requires the movement -% "rest" to run properly. -% --------------------------Updates-------------------------- -% [Contributors are welcome to add their email] -% 2012-06-07 / Nichlas Sander / Creation of TACTest -% 2012-08-08 / Nichlas Sander / Data is saved in correct order. -% 2012-08-08 / Nichlas Sander / Added calculations of path efficiency. -% 2012-10-05 / Joel Falk-Dahlin / Changed ApplyControl to work with new version -% Removed InitControl since is perfomed -% within the GUI -% 2012-10-05 / Joel Falk-Dahlin / Changed speed to be read from mainGUI -% handles. This way TAC-test works the same -% way as the realtime patRec. Changed so -% the predicted movement is outputed to -% mainGUI. -% 2012-10-26 / Joel Falk-Dahlin / Added ApplyProportionalControl -% 2012-11-23 / Joel Falk-Dahlin / Moved speeds to patRec from handles, -% added a tracker to keep track of the state space position during TAC-test -% trajectories can be plotted with -% GUI_SSPresentation -% Added ResetControl to make sure control -% is reinitialized between trials - -function success = TACTest(patRecX, handlesX) -clear global; - -%global speed -global time -global patRec; -global handles; -global tempData; - -global wantedMovementId; -global tacComplete; -global firstTacTime; -global startTimer; -global completionTime; -global selectionTime; -global selectionTimeA; -global nTW; -global recordedMovements; -global thresholdGUIData - -patRec = patRecX; -handles = handlesX; -pDiv = 2; %Event is fired every TW/value milliseconds, ie 100ms if TW = 200ms & pDiv = 2. -trials = str2double(get(handles.tb_trials,'String')); -reps = str2double(get(handles.tb_repetitions,'String')); -timeOut = str2double(get(handles.tb_executeTime,'String')); -allowance = str2double(get(handles.tb_allowance,'String')); -%speed = str2double(get(handles.tb_speed,'String')); -time = str2double(get(handles.tb_time,'String')); - -pause on; %Enable pausing - -tacComplete = 0; -firstTacTime = []; - -tacTest.patRec = patRec; -tacTest.sF = patRec.sF; -tacTest.tW = patRec.tW; -tacTest.trials = trials; -tacTest.nR = reps; -tacTest.timeOut = timeOut; - -% Is threshold (thOut) used? -if(isfield(patRec.patRecTrained,'thOut')); - %Threshold GUI init - thresholdGUI = GUI_Threshold; - thresholdGUIData = guidata(thresholdGUI); - set(GUI_Threshold,'CloseRequestFcn', 'set(GUI_Threshold, ''Visible'', ''off'')'); - xpatch = [1 1 0 0]; - ypatch = [0 0 0 0]; - for i=0:patRec.nOuts-1 - s = sprintf('movementSelector%d',i); - s0 = sprintf('thPatch%d',i); - s1 = sprintf('meter%d',i); - axes(thresholdGUIData.(s1)); - handles.patRecHandles.(s0) = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','normal','visible','on'); - ylim(thresholdGUIData.(s1), [0 1]); - xlim('auto'); - set(thresholdGUIData.(s),'String',patRec.mov(patRec.indMovIdx)); - if (size(patRec.mov(patRec.indMovIdx),1) < i+1); - set(thresholdGUIData.(s),'Value',size(patRec.indMovIdx,2)); - else - set(thresholdGUIData.(s),'Value',i+1); - end - end -end - -% Initialize DAQ card -% Note: A function for DAQ selection will be required when more cards are -% added -if strcmp(patRec.dev,'ADH') - -else % at this point everything else is SBI (e.g.NI-USB6009) - chAI = zeros(1,8); - chAI(patRec.nCh) = 1; - % create the SBI - s = InitSBI_NI(patRec.sF,timeOut,chAI); - % Change the peek time - s.NotifyWhenDataAvailableExceeds = (patRec.sF*patRec.tW)/pDiv; % Max 0.05, or 20 times per second - %Add listener - lh = s.addlistener('DataAvailable', @TACTest_OneShot); - %Test the DAQ by ploting the data - %lh = s.addlistener('DataAvailable', @plotDataTest); -end - -%Initialise the control algorithm. -%patRec = InitControl(patRec); % No longer needed, initialization is performed inside the GUI - -% Start the test -tempData = []; - -com = handles.vre_Com; - -%Sends a value to set the TAC hand to ON. -fwrite(com,sprintf('%c%c%c%c','c',char(2),char(1),char(0))); -fread(com,1); - -fwrite(com,sprintf('%c%c%c%c','c',char(3),char(allowance),char(0))); -fread(com,1); - -TrackStateSpace('initialize',patRec,allowance); - -%Run through all the trials -for t = 1 : trials - %Create a random order for the wanted movements. - indexOrder = GetMovementCombination(handles.dofs,patRec.nOuts-1); - tacTest.combinations = size(indexOrder,1); - for r = 1 : reps - set(handles.txt_status,'String',sprintf('Trial: %d , Rep: %d.',t,r)); - - %Loop through all of the movements - for i = 1 : size(indexOrder,1) - completionTime = NaN; - selectionTime = NaN; - selectionTimeA = NaN; - startTimer = []; - recordedMovements = []; - tacComplete = 0; - nTW = 1; - wantedMovementId = []; - printName = ''; - - - %Reset the TAC hand. Own function? - fwrite(com,sprintf('%c%c%c%c','r','t',char(0),char(0))); - fread(com,1); - - fwrite(com,sprintf('%c%c%c%c','r','1',char(0),char(0))); - fread(com,1); - - %Get all random movements for this set of movements. - index = indexOrder(i,:); - - set(handles.txt_status,'String','Wait!'); - pause(2); - name = 'Wanted: '; - %distance = randi(5,1) * 15 + 50; - distance = 40; - for j = 1:length(index) - movementIndex = patRec.movOutIdx{index(j)}; - listOfMovements = handles.movList; - movement = listOfMovements(movementIndex); - name = strcat(name,movement.name,','); - printName = strcat(printName,upper(movement.name{1}(regexp(movement.name{1}, '\<.'))),','); - for temp_index = 1:distance - VREActivation(com, 1, [], movement.idVRE, movement.vreDir, 1); - end - wantedMovementId = [wantedMovementId; movementIndex]; - end - %Remove the last comma. Other way of solving it? - name{1}(end) = []; - - set(handles.txt_status,'String',name); - - TrackStateSpace('target',index, distance); % ONLY WORKS IF DISTANCE IS THE SAME IN ALL DOFS - - % Reset the controller between trials - patRec = ReInitControl(patRec); - - %Start the listener, to allow for movements. - s.startBackground(); - %Wait until the background firing is finished. - s.wait(); - - test.recordedMovements = recordedMovements; - test.completionTime = completionTime; - test.selectionTime = selectionTime; - test.selectionTimeA = selectionTimeA; - - %Record data, present it. - test.fail = isnan(completionTime); - - if(test.fail) - test.pathEfficiency = NaN; - else - test.pathEfficiency = TrackStateSpace('single'); - end - - test.movement = movement; - test.name = printName(1:end-1); %Use something else? - - %Save the data to the trialResult in each trial, repetitition - %and movement. - tacTest.trialResult(t,r,i) = test; - - tempData = []; - end - end -end - -tacTest.ssTracker = TrackStateSpace('read'); - - - -% Save test -[filename, pathname] = uiputfile({'*.mat','MAT-files (*.mat)'},'Save as', 'Untitled.mat'); -if isequal(filename,0) || isequal(pathname,0) - disp('User pressed cancel') -else - save([pathname,filename],'tacTest'); -end - -%Display results -tacTest = TacTestResults(tacTest); -end - -function TACTest_OneShot(src,event) -pTime = tic; -global patRec; -global handles; -global nTW; % Number of time windows evaluated -global fpTW; % First time window with any movement. -global dataTW; % Raw data from each time windows -global tempData; -%global speed; -global time; - -global wantedMovement; -global wantedMovementId; - -global completionTime; -global selectionTime; -global selectionTimeA; -global tacComplete; -global processingTime; -global firstTacTime; -global startTimer; -global recordedMovements; -global thresholdGUIData - -tempData = [tempData; event.Data]; - -if size(tempData,1) >= (patRec.sF * patRec.tW) - tData = tempData(end-patRec.sF*patRec.tW+1:end,:); %Copy the temporal data to the test data - dataTW(:,:,nTW) = tData; % Save data for future analisys - - %Start counting processing time - processingTimeTic = tic; - - % General routine for RealtimePatRec - [outMov, outVector, patRec, handles.patRecHandles] = OneShotRealtimePatRec(tData, patRec, handles.patRecHandles, thresholdGUIData); - TrackStateSpace('move',outMov, patRec.control.currentDegPerMov); - -% %Signal processing -% tSet = SignalProcessing_RealtimePatRec(tData, patRec); -% -% % Only predict when signal is over floor noise? -% meanFeature1 = mean(tSet(1:size(patRec.nCh,2))); -% if meanFeature1 < (patRec.floorNoise(1)/1) -% outMov = patRec.nOuts; -% outVector = zeros(patRec.nOuts,1); -% else -% % One shoot PatRec -% [outMov, outVector] = OneShotPatRecClassifier(patRec, tSet); -% if outMov == 0 -% outMov = patRec.nOuts; -% end -% end -% -% -% % Apply Proportional control -% handles = 1ApplyProportionalControl(tData,patRec,handles); -% -% %Apply control algorithms. -% [patRec, outMov, handles] = ApplyControl(patRec, outMov, outVector, handles); -% -% % Update MainGUI to show outputed Mov -% set(handles.patRecHandles.lb_movements,'Value',outMov); -% drawnow; - - processingTime(nTW) = toc(processingTimeTic); - %Apply Majority Vote filtering of movements - %[patRec outMov] = MajorityVote(patRec,outMov); - - %Check whether to start timer for completionTime and selectionTime. - if isempty(startTimer) && isempty(find(outMov == patRec.nM)) - startTimer = tic; - fpTW = nTW; - end - %Ensure that on movement or recording of time is done after the TAC has - %completed. - - if ~tacComplete - %Get movement from the list of movements. - movement = handles.movList(outMov); - speed = patRec.control.currentDegPerMov(outMov); - - %Temporary time - if ~isempty(startTimer) - tempTime = toc(startTimer); - if isnan(selectionTime) && sum(ismember(outMov,wantedMovementId)) - selectionTime = tempTime+patRec.tW+processingTime(fpTW); - end - - if isnan(selectionTimeA) && sum(ismember(wantedMovementId, outMov)) - selectionTimeA = tempTime+patRec.tW+processingTime(fpTW); - end - - end - %Only add the recorded movement if we are not yet done. - - if(length(movement)>1) - name = movement(1).name; - for i = 2:length(movement) - name = strcat(name,',',movement(i).name); - end - else - name = movement.name; - end - set(handles.txt_status2,'String',name); - - for i = 1:length(outMov) - recordedMovements = [recordedMovements; movement(i).name, speed(i)]; - dof = movement(i).idVRE; - dir = movement(i).vreDir; - - if (VREActivation(handles.vre_Com,speed(i),[],dof,dir,0)) - if (isempty(firstTacTime)) - firstTacTime = tic; - else - heldTime = toc(firstTacTime); - if(heldTime > time) - set(handles.txt_status2,'String','Movement Completed!'); - completionTime = toc(startTimer); - completionTime = completionTime - time; - tacComplete = 1; - %Stop the acquisition and move on to the next movement. - src.stop(); - %Pause 1 second once the movement is completed. - pause(1); - %This means that the TAC-test is completed. This value - %is set so no more motion is completed. - else - set(handles.txt_status2,'String',sprintf('Movement reached. Hold for %0.02f more seconds.',time-heldTime)); - end - end - else - firstTacTime = []; - end - end - end - nTW = nTW + 1; -end +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% ------------------- Function Description ------------------ +% A TAC Test is performed using the VRE. It then uses the TacTestResult +% function to display the gathered data. +% +% The function is written in such a way that it requires the movement +% "rest" to run properly. +% --------------------------Updates-------------------------- +% [Contributors are welcome to add their email] +% 2012-06-07 / Nichlas Sander / Creation of TACTest +% 2012-08-08 / Nichlas Sander / Data is saved in correct order. +% 2012-08-08 / Nichlas Sander / Added calculations of path efficiency. +% 2012-10-05 / Joel Falk-Dahlin / Changed ApplyControl to work with new version +% Removed InitControl since is perfomed +% within the GUI +% 2012-10-05 / Joel Falk-Dahlin / Changed speed to be read from mainGUI +% handles. This way TAC-test works the same +% way as the realtime patRec. Changed so +% the predicted movement is outputed to +% mainGUI. +% 2012-10-26 / Joel Falk-Dahlin / Added ApplyProportionalControl +% 2012-11-23 / Joel Falk-Dahlin / Moved speeds to patRec from handles, +% added a tracker to keep track of the state space position during TAC-test +% trajectories can be plotted with +% GUI_SSPresentation +% Added ResetControl to make sure control +% is reinitialized between trials +% 2013-06-10 / Nichlas Sander / Changed the tracker used for state space +% tracking to one that is not limited to +% a set number of movements. +% 2015-02-02 / Enzo Mastinu / All this function has been re-organizated +% to be compatible with the functions +% placed into COMM/AFE folder. For more +% details read RecordingSession.m log. +% 2015-02-27 / Enzo Mastinu / Fix some bugs with the custom devices TAC +% test. + +% 20xx-xx-xx / Author / Comment on update + + + +function success = TACTest(patRecX, handlesX) + clear global; + + %global speed + global time + global patRec; + global handles; + global tempData; + + global wantedMovementId; + global tacComplete; + global firstTacTime; + global startTimer; + global completionTime; + global selectionTime; + global selectionTimeA; + global nTW; + global recordedMovements; + global thresholdGUIData + + patRec = patRecX; + handles = handlesX; + + pDiv = 2; %Event is fired every TW/value milliseconds, ie 100ms if TW = 200ms & pDiv = 2. + trials = str2double(get(handles.tb_trials,'String')); + reps = str2double(get(handles.tb_repetitions,'String')); + timeOut = str2double(get(handles.tb_executeTime,'String')); + allowance = str2double(get(handles.tb_allowance,'String')); + %speed = str2double(get(handles.tb_speed,'String')); + time = str2double(get(handles.tb_time,'String')); + + pause on; %Enable pausing + + tacComplete = 0; + firstTacTime = []; + + % Get needed info from patRec structure + tacTest.patRec = patRec; + tacTest.sF = patRec.sF; + % tacTest.tW = patRec.tW; + tacTest.trials = trials; + tacTest.nR = reps; + tacTest.timeOut = timeOut; + + sF = tacTest.sF; + nCh = length(patRec.nCh); + ComPortType = patRec.comm; + deviceName = patRec.dev; + + % Get sampling time + sT = tacTest.timeOut; + tW = sT/100; % Time window size + tWs = tW*sF; % Time window samples + + % Is threshold (thOut) used? + if(isfield(patRec.patRecTrained,'thOut')); + %Threshold GUI init + thresholdGUI = GUI_Threshold; + thresholdGUIData = guidata(thresholdGUI); + set(GUI_Threshold,'CloseRequestFcn', 'set(GUI_Threshold, ''Visible'', ''off'')'); + xpatch = [1 1 0 0]; + ypatch = [0 0 0 0]; + for i=0:patRec.nOuts-1 + s = sprintf('movementSelector%d',i); + s0 = sprintf('thPatch%d',i); + s1 = sprintf('meter%d',i); + axes(thresholdGUIData.(s1)); + handles.patRecHandles.(s0) = patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','normal','visible','on'); + ylim(thresholdGUIData.(s1), [0 1]); + xlim('auto'); + set(thresholdGUIData.(s),'String',patRec.mov(patRec.indMovIdx)); + if (size(patRec.mov(patRec.indMovIdx),1) < i+1); + set(thresholdGUIData.(s),'Value',size(patRec.indMovIdx,2)); + else + set(thresholdGUIData.(s),'Value',i+1); + end + end + end + + %Initialise the control algorithm. + %patRec = InitControl(patRec); % No longer needed, initialization is performed inside the GUI + + % Start the test + tempData = []; + + com = handles.vre_Com; + + %Sends a value to set the TAC hand to ON. + fwrite(com,sprintf('%c%c%c%c','c',char(2),char(1),char(0))); + fread(com,1); + + fwrite(com,sprintf('%c%c%c%c','c',char(3),char(allowance),char(0))); + fread(com,1); + + CurrentPosition('init',patRec,allowance, handles.movList); + TrackStateSpace('initialize',patRec,allowance); + + %Run through all the trials + for t = 1 : trials + %Create a random order for the wanted movements. + indexOrder = GetMovementCombination(handles.dofs,patRec.nOuts-1); + tacTest.combinations = size(indexOrder,1); + for r = 1 : reps + set(handles.txt_status,'String',sprintf('Trial: %d , Rep: %d.',t,r)); + + %Loop through all of the movements + for i = 1 : size(indexOrder,1) + completionTime = NaN; + selectionTime = NaN; + selectionTimeA = NaN; + startTimer = []; + recordedMovements = []; + tacComplete = 0; + nTW = 1; + wantedMovementId = []; + printName = ''; + + + %Reset the TAC hand. Own function? + fwrite(com,sprintf('%c%c%c%c','r','t',char(0),char(0))); + fread(com,1); + + fwrite(com,sprintf('%c%c%c%c','r','1',char(0),char(0))); + fread(com,1); + + %Get all random movements for this set of movements. + index = indexOrder(i,:); + + set(handles.txt_status,'String','Wait!'); + pause(2); + name = 'Wanted: '; + %distance = randi(5,1) * 15 + 50; + distance = 40; + for j = 1:length(index) + movementIndex = patRec.movOutIdx{index(j)}; + listOfMovements = handles.movList; + movement = listOfMovements(movementIndex); + name = strcat(name,movement.name,','); + printName = strcat(printName,upper(movement.name{1}(regexp(movement.name{1}, '\<.'))),','); + for temp_index = 1:distance + VREActivation(com, 1, [], movement.idVRE, movement.vreDir, 1); + end + wantedMovementId = [wantedMovementId; movementIndex]; + end + %Remove the last comma. Other way of solving it? + name{1}(end) = []; + + set(handles.txt_status,'String',name); + + CurrentPosition('target',index, distance); + TrackStateSpace('target',index, distance); % ONLY WORKS IF DISTANCE IS THE SAME IN ALL DOFS + + + % Reset the controller between trials + patRec = ReInitControl(patRec); + + cData = zeros(tWs,nCh); + if strcmp (ComPortType, 'NI') + + % Init SBI + sCh = 1:nCh; + s = InitSBI_NI(sF,sT,sCh); + s.NotifyWhenDataAvailableExceeds = tWs; % PEEK time + lh = s.addlistener('DataAvailable', @TACTest_OneShot); + + % Start DAQ + s.startBackground(); % Run in the backgroud + + if ~s.IsDone % check if is done + s.wait(); + end + delete(lh); + + %%%%% Real Time PatRec with other custom device %%%%% + else + + % Prepare handles for next function calls + handles.deviceName = deviceName; + handles.ComPortType = ComPortType; + if strcmp (ComPortType, 'COM') + handles.ComPortName = patRec.comn; + end + handles.sF = sF; + handles.sT = sT; + handles.nCh = nCh; + handles.sTall = sT; + handles.t_msg = handles.txt_status; + + % Connect the chosen device, it returns the connection object + obj = ConnectDevice(handles); + + % Set the selected device and Start the acquisition + SetDeviceStartAcquisition(handles, obj); + handles.CommObj = obj; + handles.success = 0; + + for timeWindowNr = 1:sT/tW + cData = Acquire_tWs(deviceName, obj, nCh, tWs); % acquire a new time window of samples + acquireEvent.Data = cData; + TACTest_OneShot(0, acquireEvent); + if handles.success + % if the previous movement was "success" we can + % stop this acquisition and pass to the next + break + end + end + % Stop acquisition + StopAcquisition(deviceName, obj); + end + + + test.recordedMovements = recordedMovements; + test.completionTime = completionTime; + test.selectionTime = selectionTime; + test.selectionTimeA = selectionTimeA; + + %Record data, present it. + test.fail = isnan(completionTime); + + test.allowance = allowance; + test.distance = distance; + + if(test.fail) + test.pathEfficiency = NaN; + else + %This is not used for path efficiency any longer + %test.pathEfficiency = TrackStateSpace('single'); + test.pathEfficiency = CurrentPosition('get'); + end + + test.movement = listOfMovements(wantedMovementId); + test.name = printName(1:end-1); %Use something else? + + %Save the data to the trialResult in each trial, repetitition + %and movement. + tacTest.trialResult(t,r,i) = test; + + tempData = []; + end + end + end + + tacTest.ssTracker = TrackStateSpace('read'); + + % Save test + [filename, pathname] = uiputfile({'*.mat','MAT-files (*.mat)'},'Save as', 'Untitled.mat'); + if isequal(filename,0) || isequal(pathname,0) + disp('User pressed cancel') + else + save([pathname,filename],'tacTest'); + end + + %Display results + tacTest = TacTestResults(tacTest); +end + + + +function TACTest_OneShot(src,event) + pTime = tic; + global patRec; + global handles; + global nTW; % Number of time windows evaluated + global fpTW; % First time window with any movement. + global dataTW; % Raw data from each time windows + global tempData; + %global speed; + global time; + + global wantedMovement; + global wantedMovementId; + + global completionTime; + global selectionTime; + global selectionTimeA; + global tacComplete; + global processingTime; + global firstTacTime; + global startTimer; + global recordedMovements; + global thresholdGUIData + + tempData = [tempData; event.Data]; + + if size(tempData,1) >= (patRec.sF * patRec.tW) + tData = tempData(end-patRec.sF*patRec.tW+1:end,:); %Copy the temporal data to the test data + dataTW(:,:,nTW) = tData; % Save data for future analisys + + %Start counting processing time + processingTimeTic = tic; + + % General routine for RealtimePatRec + [outMov, outVector, patRec, handles.patRecHandles] = OneShotRealtimePatRec(tData, patRec, handles.patRecHandles, thresholdGUIData); + TrackStateSpace('move',outMov, patRec.control.currentDegPerMov); + CurrentPosition('move',outMov, patRec.control.currentDegPerMov); + + % %Signal processing + % tSet = SignalProcessing_RealtimePatRec(tData, patRec); + % + % % Only predict when signal is over floor noise? + % meanFeature1 = mean(tSet(1:size(patRec.nCh,2))); + % if meanFeature1 < (patRec.floorNoise(1)/1) + % outMov = patRec.nOuts; + % outVector = zeros(patRec.nOuts,1); + % else + % % One shoot PatRec + % [outMov, outVector] = OneShotPatRecClassifier(patRec, tSet); + % if outMov == 0 + % outMov = patRec.nOuts; + % end + % end + % + % + % % Apply Proportional control + % handles = 1ApplyProportionalControl(tData,patRec,handles); + % + % %Apply control algorithms. + % [patRec, outMov, handles] = ApplyControl(patRec, outMov, outVector, handles); + % + % % Update MainGUI to show outputed Mov + % set(handles.patRecHandles.lb_movements,'Value',outMov); + % drawnow; + + processingTime(nTW) = toc(processingTimeTic); + %Apply Majority Vote filtering of movements + %[patRec outMov] = MajorityVote(patRec,outMov); + + %Check whether to start timer for completionTime and selectionTime. + if isempty(startTimer) && isempty(find(outMov == patRec.nM)) + startTimer = tic; + fpTW = nTW; + end + %Ensure that on movement or recording of time is done after the TAC has + %completed. + + if ~tacComplete + %Get movement from the list of movements. + movement = handles.movList(outMov); + speed = patRec.control.currentDegPerMov(outMov); + + %Temporary time + if ~isempty(startTimer) + tempTime = toc(startTimer); + if isnan(selectionTime) && sum(ismember(outMov,wantedMovementId)) + selectionTime = tempTime+patRec.tW+processingTime(fpTW); + end + + if isnan(selectionTimeA) && sum(ismember(wantedMovementId, outMov)) + selectionTimeA = tempTime+patRec.tW+processingTime(fpTW); + end + + end + %Only add the recorded movement if we are not yet done. + + if(length(movement)>1) + name = movement(1).name; + for i = 2:length(movement) + name = strcat(name,',',movement(i).name); + end + else + name = movement.name; + end + set(handles.txt_status2,'String',name); + + performedMovements = []; + + for i = 1:length(outMov) + performedMovements = [performedMovements, {movement(i), speed(i)}]; + dof = movement(i).idVRE; + dir = movement(i).vreDir; + + if (VREActivation(handles.vre_Com,speed(i),[],dof,dir,0)) + if (isempty(firstTacTime)) + firstTacTime = tic; + else + heldTime = toc(firstTacTime); + if(heldTime > time) + set(handles.txt_status2,'String','Movement Completed!'); + completionTime = toc(startTimer); + completionTime = completionTime - time; + tacComplete = 1; + %Stop the acquisition and move on to the next movement. + if strcmp (handles.ComPortName, 'NI') + src.stop(); + else + handles.success = 1; + end + %Pause 1 second once the movement is completed. + pause(1); + %This means that the TAC-test is completed. This value + %is set so no more motion is completed. + else + set(handles.txt_status2,'String',sprintf('Movement reached. Hold for %0.02f more seconds.',time-heldTime)); + end + end + else + firstTacTime = []; + end + end + recordedMovements = [recordedMovements; {performedMovements}]; + end + nTW = nTW + 1; + end end \ No newline at end of file diff --git a/VRE/TrackStateSpace.m b/VRE/TrackStateSpace.m index b1acdd5..a4b5e14 100644 --- a/VRE/TrackStateSpace.m +++ b/VRE/TrackStateSpace.m @@ -1,146 +1,146 @@ -% ---------------------------- Copyright Notice --------------------------- -% This file is part of BioPatRec © which is open and free software under -% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for -% the full license governing this code and copyrights. -% -% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and -% Chalmers University of Technology. All authors’ contributions must be kept -% acknowledged below in the section "Updates % Contributors". -% -% Would you like to contribute to science and sum efforts to improve -% amputees’ quality of life? Join this project! or, send your comments to: -% maxo@chalmers.se. -% -% The entire copyright notice must be kept in this or any source file -% linked to BioPatRec. This will ensure communication with all authors and -% acknowledge contributions here and in the project web page (optional). -% -% -------------------------- Function Description ------------------------- -% Function to track the state space position of the system during TAC-test -% The results can be viewed in GUI_SSPresentation later. -% -% TrackStateSpace('initialize',patRec,allowance) - Initializes all -% parameters needed to keep track of the state space system. Should be -% executed before TAC-test starts -% -% TrackStateSpace('target', movIndex, distance) - Creates a target position -% in state space. movIndex should be the patRec.movOutIdx, that -% corresponds to the targeted motion, distance a number that sets the -% distance to the target in all DoFs. -% -% TrackStateSpace('move', outMov, speeds) - Moves the system in state space -% -% ssTracker = TrackStateSpace('read') - Return a structure containing all -% the state space information. -% -% ------------------------- Updates & Contributors ------------------------ -% [Contributors are welcome to add their email] -% 2012-11-23 / Joel Falk-Dahlin / Creation -% 20xx-xx-xx / Author / Comment on update - -function ssObj = TrackStateSpace(varargin) - -persistent classifiers controllers controlParams movMatrix ssTargets ssTrajectories allowance - -if ischar(varargin{1}) - - if strcmpi(varargin{1},'initialize') - - patRec = varargin{2}; - allowance = varargin{3}; - - movMatrix = CreateMovMatrix(patRec); - ssTargets = []; - ssTrajectories = []; - - classifiers = {patRec}; - - if isfield(patRec,'controlAlg') - - controllers = patRec.controlAlg.name; - controlParams = {cell(1,1)}; - params = fieldnames(patRec.controlAlg.parameters); - paramVec = []; - for i = 1:length(params) - paramName = params(i); - paramValue = patRec.controlAlg.parameters.(params{i}); - paramVec = [paramVec; paramName, {paramValue}]; - end - controlParams{1}{1} = paramVec; - - else - controllers = {'None'}; - controlParams = {{[]}}; - end - - elseif strcmpi(varargin{1},'target') - - index = varargin{2}; - distance = varargin{3}; - - if length(index) > 1 - movement = sum( movMatrix(index,:) ); - else - movement = movMatrix(index,:); - end - - newTarget = movement.*distance; - ssTargets = [ssTargets, {newTarget}]; % Save the new target - - ssTrajectories = [ssTrajectories; {[0, 0, 0]}]; % Create new position - - elseif strcmpi(varargin{1},'move') - - ssTrajectory = ssTrajectories{end}; - ssPos = ssTrajectory(end,:); - outMov = varargin{2}; - speeds = varargin{3}; - -% outMovIdx = []; - -% for i = 1:length(classifiers{1}.movOutIdx) -% if length(outMov) == length(classifiers{1}.movOutIdx{i}) -% if classifiers{1}.movOutIdx{i} == outMov' -% outMovIdx = i; -% break; -% end -% end -% end -% -% if isempty(outMovIdx) -% outMovIdx = length(classifiers{1}.movOutIdx); -% end - - newPos = MoveSS(ssPos,movMatrix,outMov,speeds); % Calculate new position in state space - - newTrajectory = [ssTrajectory; newPos]; % Update the trajectory - ssTrajectories{end} = newTrajectory; % Save the trajectory - - elseif strcmpi(varargin{1},'read') - - ssObj.classifiers = classifiers; - ssObj.controllers = controllers; - ssObj.controlParams = controlParams; - - ssObj.ssTrajectories = ssTrajectories; - - ssObj.ssTargets = ssTargets; - ssObj.nReps = 1; - ssObj.allowance = allowance; - - elseif strcmpi(varargin{1},'single') - ssTrajectory = ssTrajectories{end}; - ssTarget = ssTargets{end}; - - - userPath = CalculatePathLength(ssTrajectory, ssTarget, allowance); - perfectPath = CalculatePathLength([0 0 0; ssTarget], ssTarget, allowance); - - %Will round the value to nearest value with accuracy of 3-decimal - %places. - ssObj = round((perfectPath / userPath)*1000) / 1000; - end - -else - +% ---------------------------- Copyright Notice --------------------------- +% This file is part of BioPatRec © which is open and free software under +% the GNU Lesser General Public License (LGPL). See the file "LICENSE" for +% the full license governing this code and copyrights. +% +% BioPatRec was initially developed by Max J. Ortiz C. at Integrum AB and +% Chalmers University of Technology. All authors’ contributions must be kept +% acknowledged below in the section "Updates % Contributors". +% +% Would you like to contribute to science and sum efforts to improve +% amputees’ quality of life? Join this project! or, send your comments to: +% maxo@chalmers.se. +% +% The entire copyright notice must be kept in this or any source file +% linked to BioPatRec. This will ensure communication with all authors and +% acknowledge contributions here and in the project web page (optional). +% +% -------------------------- Function Description ------------------------- +% Function to track the state space position of the system during TAC-test +% The results can be viewed in GUI_SSPresentation later. +% +% TrackStateSpace('initialize',patRec,allowance) - Initializes all +% parameters needed to keep track of the state space system. Should be +% executed before TAC-test starts +% +% TrackStateSpace('target', movIndex, distance) - Creates a target position +% in state space. movIndex should be the patRec.movOutIdx, that +% corresponds to the targeted motion, distance a number that sets the +% distance to the target in all DoFs. +% +% TrackStateSpace('move', outMov, speeds) - Moves the system in state space +% +% ssTracker = TrackStateSpace('read') - Return a structure containing all +% the state space information. +% +% ------------------------- Updates & Contributors ------------------------ +% [Contributors are welcome to add their email] +% 2012-11-23 / Joel Falk-Dahlin / Creation +% 20xx-xx-xx / Author / Comment on update + +function ssObj = TrackStateSpace(varargin) + +persistent classifiers controllers controlParams movMatrix ssTargets ssTrajectories allowance + +if ischar(varargin{1}) + + if strcmpi(varargin{1},'initialize') + + patRec = varargin{2}; + allowance = varargin{3}; + + movMatrix = CreateMovMatrix(patRec); + ssTargets = []; + ssTrajectories = []; + + classifiers = {patRec}; + + if isfield(patRec,'controlAlg') + + controllers = patRec.controlAlg.name; + controlParams = {cell(1,1)}; + params = fieldnames(patRec.controlAlg.parameters); + paramVec = []; + for i = 1:length(params) + paramName = params(i); + paramValue = patRec.controlAlg.parameters.(params{i}); + paramVec = [paramVec; paramName, {paramValue}]; + end + controlParams{1}{1} = paramVec; + + else + controllers = {'None'}; + controlParams = {{[]}}; + end + + elseif strcmpi(varargin{1},'target') + + index = varargin{2}; + distance = varargin{3}; + + if length(index) > 1 + movement = sum( movMatrix(index,:) ); + else + movement = movMatrix(index,:); + end + + newTarget = movement.*distance; + ssTargets = [ssTargets, {newTarget}]; % Save the new target + + ssTrajectories = [ssTrajectories; {[0, 0, 0]}]; % Create new position + + elseif strcmpi(varargin{1},'move') + + ssTrajectory = ssTrajectories{end}; + ssPos = ssTrajectory(end,:); + outMov = varargin{2}; + speeds = varargin{3}; + +% outMovIdx = []; + +% for i = 1:length(classifiers{1}.movOutIdx) +% if length(outMov) == length(classifiers{1}.movOutIdx{i}) +% if classifiers{1}.movOutIdx{i} == outMov' +% outMovIdx = i; +% break; +% end +% end +% end +% +% if isempty(outMovIdx) +% outMovIdx = length(classifiers{1}.movOutIdx); +% end + + newPos = MoveSS(ssPos,movMatrix,outMov,speeds); % Calculate new position in state space + + newTrajectory = [ssTrajectory; newPos]; % Update the trajectory + ssTrajectories{end} = newTrajectory; % Save the trajectory + + elseif strcmpi(varargin{1},'read') + + ssObj.classifiers = classifiers; + ssObj.controllers = controllers; + ssObj.controlParams = controlParams; + + ssObj.ssTrajectories = ssTrajectories; + + ssObj.ssTargets = ssTargets; + ssObj.nReps = 1; + ssObj.allowance = allowance; + + elseif strcmpi(varargin{1},'single') + ssTrajectory = ssTrajectories{end}; + ssTarget = ssTargets{end}; + + + userPath = CalculatePathLength(ssTrajectory, ssTarget, allowance); + perfectPath = CalculatePathLength([0 0 0; ssTarget], ssTarget, allowance); + + %Will round the value to nearest value with accuracy of 3-decimal + %places. + ssObj = round((perfectPath / userPath)*1000) / 1000; + end + +else + end \ No newline at end of file diff --git a/VRE/UpdateTAC/CalculateTacPath.m b/VRE/UpdateTAC/CalculateTacPath.m new file mode 100644 index 0000000..1e61580 --- /dev/null +++ b/VRE/UpdateTAC/CalculateTacPath.m @@ -0,0 +1,47 @@ +function tacTest = CalculateTacPath(tacTest) + s = size(tacTest.trialResult); + tacTest.allowance = 5; + tacTest.distance = 40; + + movements = InitMovements(); + + for trial = 1:s(1) %loop over each trial + for rep = 1:s(2) %loop over each repetition + for comb = 1:s(3) %loop over each combination + result = tacTest.trialResult(trial,rep,comb); + + %Set the correct movements from names. + names = regexp(result.name,',','split'); + movement = []; + for i = 1:length(names) + movement = [movement, GetMovementFromName(names(i),movements)]; + end + result.movement = movement; + + % Set recordedMovements correctly + maximumMovement = tacTest.patRec.control.maxDegPerMov(1); %Only get the first since they are all the same. + movementJumpSize = tacTest.patRec.control.controlAlg.parameters.rampLength; + movementJump = maximumMovement / movementJumpSize; + movementDecrease = tacTest.patRec.control.controlAlg.parameters.downCount * movementJump; + + maxId = 0; + for i = 1:length(movements) + if(movements(i).idVRE > maxId) + maxId = movements(i).idVRE; + end + end + + lastKnownSpeed = zeros(1,maxId); %An array that says last known speed of movements. + + for record = 1:length(result.recordedMovements) + newSpeeds = lastKnownSpeed; + currentMovement = GetMovementFromName(result.recordedMovements(record,1),movements); + currentSpeed = result.recordedMovements(record,2); + newSpeeds(currentMovement.idVRE) = currentSpeed; + end + + tacTest.trialResult(trial,rep,comb) = result; + end + end + end +end \ No newline at end of file diff --git a/VRE/UpdateTAC/GetMovementFromName.m b/VRE/UpdateTAC/GetMovementFromName.m new file mode 100644 index 0000000..bb86f8d --- /dev/null +++ b/VRE/UpdateTAC/GetMovementFromName.m @@ -0,0 +1,11 @@ +function movement = GetMovementFromName(name,movements) + + movement = movements(1); + name = ShortToName(name); + + for i = 1:length(movements) + if strcmp(movements(i).name,name) + movement = movements(i); + end + end +end \ No newline at end of file diff --git a/VRE/UpdateTAC/ShortToName.m b/VRE/UpdateTAC/ShortToName.m new file mode 100644 index 0000000..06738a3 --- /dev/null +++ b/VRE/UpdateTAC/ShortToName.m @@ -0,0 +1,23 @@ +function name = ShortToName(name) +if iscell(name) + name = name{1}; +end + switch name + case 'CH' + name = 'Close Hand'; + case 'OH' + name = 'Open Hand'; + case 'FH' + name = 'Flex Hand'; + case 'EH' + name = 'Extend Hand'; + case 'FE' + name = 'Flex Elbow'; + case 'EE' + name = 'Extend Elbow'; + case 'P' + name = 'Pronation'; + case 'S' + name = 'Supination'; + end +end \ No newline at end of file diff --git a/VRE/bin/Release/ogre.cfg b/VRE/bin/Release/ogre.cfg index 49fe7dc..b9c305c 100644 --- a/VRE/bin/Release/ogre.cfg +++ b/VRE/bin/Release/ogre.cfg @@ -1,13 +1,13 @@ -Render System=OpenGL Rendering Subsystem - -[OpenGL Rendering Subsystem] -Colour Depth=32 -Display Frequency=N/A -FSAA=0 -Fixed Pipeline Enabled=Yes -Full Screen=No -RTT Preferred Mode=FBO -VSync=No -VSync Interval=1 -Video Mode=1024 x 768 -sRGB Gamma Conversion=No +Render System=OpenGL Rendering Subsystem + +[OpenGL Rendering Subsystem] +Colour Depth=32 +Display Frequency=N/A +FSAA=0 +Fixed Pipeline Enabled=Yes +Full Screen=No +RTT Preferred Mode=FBO +VSync=No +VSync Interval=1 +Video Mode=1024 x 768 +sRGB Gamma Conversion=No diff --git a/VRE/bin/Release/plugins.cfg b/VRE/bin/Release/plugins.cfg index 6e61c34..7573b1a 100644 --- a/VRE/bin/Release/plugins.cfg +++ b/VRE/bin/Release/plugins.cfg @@ -1,17 +1,17 @@ -# Defines plugins to load - -# Define plugin folder -PluginFolder=. - -# Define plugins -# Plugin=RenderSystem_Direct3D9 -# Plugin=RenderSystem_Direct3D11 -Plugin=RenderSystem_GL -# Plugin=RenderSystem_GLES -# Plugin=RenderSystem_GLES2 -# Plugin=Plugin_ParticleFX -# Plugin=Plugin_BSPSceneManager -# Plugin=Plugin_CgProgramManager -# Plugin=Plugin_PCZSceneManager -# Plugin=Plugin_OctreeZone -# Plugin=Plugin_OctreeSceneManager +# Defines plugins to load + +# Define plugin folder +PluginFolder=. + +# Define plugins +# Plugin=RenderSystem_Direct3D9 +# Plugin=RenderSystem_Direct3D11 +Plugin=RenderSystem_GL +# Plugin=RenderSystem_GLES +# Plugin=RenderSystem_GLES2 +# Plugin=Plugin_ParticleFX +# Plugin=Plugin_BSPSceneManager +# Plugin=Plugin_CgProgramManager +# Plugin=Plugin_PCZSceneManager +# Plugin=Plugin_OctreeZone +# Plugin=Plugin_OctreeSceneManager diff --git a/VRE/bin/Release/quakemap.cfg b/VRE/bin/Release/quakemap.cfg index f98c7d2..f76ad15 100644 --- a/VRE/bin/Release/quakemap.cfg +++ b/VRE/bin/Release/quakemap.cfg @@ -1,2 +1,2 @@ -Archive: ../../media/packs/chiropteraDM.pk3 -Map: maps/chiropteradm.bsp +Archive: ../../media/packs/chiropteraDM.pk3 +Map: maps/chiropteradm.bsp diff --git a/VRE/bin/Release/resources.cfg b/VRE/bin/Release/resources.cfg index a4218ae..39397e8 100644 --- a/VRE/bin/Release/resources.cfg +++ b/VRE/bin/Release/resources.cfg @@ -1,16 +1,16 @@ -# Resources required by the sample browser and most samples. -[Essential] - -# Common sample resources needed by many of the samples. -# Rarely used resources should be separately loaded by the -# samples which require them. -[Popular] -FileSystem=../media/materials/scripts -FileSystem=../media/materials/textures -FileSystem=../media/models - -[General] -FileSystem=../media - -# Materials for visual tests -[Tests] +# Resources required by the sample browser and most samples. +[Essential] + +# Common sample resources needed by many of the samples. +# Rarely used resources should be separately loaded by the +# samples which require them. +[Popular] +FileSystem=../media/materials/scripts +FileSystem=../media/materials/textures +FileSystem=../media/models + +[General] +FileSystem=../media + +# Materials for visual tests +[Tests] diff --git a/VRE/bin/Release/samples.cfg b/VRE/bin/Release/samples.cfg index 7eb97f7..e2879ed 100644 --- a/VRE/bin/Release/samples.cfg +++ b/VRE/bin/Release/samples.cfg @@ -1,37 +1,37 @@ -SampleFolder=. -SamplePlugin=Sample_BezierPatch -SamplePlugin=Sample_BSP -SamplePlugin=Sample_CameraTrack -SamplePlugin=Sample_CelShading -SamplePlugin=Sample_Character -SamplePlugin=Sample_Compositor -SamplePlugin=Sample_CubeMapping -SamplePlugin=Sample_DeferredShading -SamplePlugin=Sample_Dot3Bump -SamplePlugin=Sample_DynTex -SamplePlugin=Sample_DualQuaternion -SamplePlugin=Sample_FacialAnimation -SamplePlugin=Sample_Fresnel -SamplePlugin=Sample_Grass -SamplePlugin=Sample_Instancing -SamplePlugin=Sample_Isosurf -SamplePlugin=Sample_Lighting -SamplePlugin=Sample_NewInstancing -SamplePlugin=Sample_Ocean -SamplePlugin=Sample_ParticleGS -SamplePlugin=Sample_ParticleFX -SamplePlugin=Sample_ShaderSystem -SamplePlugin=Sample_Shadows -SamplePlugin=Sample_SkeletalAnimation -SamplePlugin=Sample_SkyBox -SamplePlugin=Sample_SkyDome -SamplePlugin=Sample_SkyPlane -SamplePlugin=Sample_Smoke -SamplePlugin=Sample_SphereMapping -SamplePlugin=Sample_SSAO -SamplePlugin=Sample_Terrain -SamplePlugin=Sample_TextureArray -SamplePlugin=Sample_TextureFX -SamplePlugin=Sample_Transparency -SamplePlugin=Sample_VolumeTex -SamplePlugin=Sample_Water +SampleFolder=. +SamplePlugin=Sample_BezierPatch +SamplePlugin=Sample_BSP +SamplePlugin=Sample_CameraTrack +SamplePlugin=Sample_CelShading +SamplePlugin=Sample_Character +SamplePlugin=Sample_Compositor +SamplePlugin=Sample_CubeMapping +SamplePlugin=Sample_DeferredShading +SamplePlugin=Sample_Dot3Bump +SamplePlugin=Sample_DynTex +SamplePlugin=Sample_DualQuaternion +SamplePlugin=Sample_FacialAnimation +SamplePlugin=Sample_Fresnel +SamplePlugin=Sample_Grass +SamplePlugin=Sample_Instancing +SamplePlugin=Sample_Isosurf +SamplePlugin=Sample_Lighting +SamplePlugin=Sample_NewInstancing +SamplePlugin=Sample_Ocean +SamplePlugin=Sample_ParticleGS +SamplePlugin=Sample_ParticleFX +SamplePlugin=Sample_ShaderSystem +SamplePlugin=Sample_Shadows +SamplePlugin=Sample_SkeletalAnimation +SamplePlugin=Sample_SkyBox +SamplePlugin=Sample_SkyDome +SamplePlugin=Sample_SkyPlane +SamplePlugin=Sample_Smoke +SamplePlugin=Sample_SphereMapping +SamplePlugin=Sample_SSAO +SamplePlugin=Sample_Terrain +SamplePlugin=Sample_TextureArray +SamplePlugin=Sample_TextureFX +SamplePlugin=Sample_Transparency +SamplePlugin=Sample_VolumeTex +SamplePlugin=Sample_Water diff --git a/VRE/bin/media/materials/scripts/Arm.material b/VRE/bin/media/materials/scripts/Arm.material index 438dd43..f0cc8df 100644 --- a/VRE/bin/media/materials/scripts/Arm.material +++ b/VRE/bin/media/materials/scripts/Arm.material @@ -1,16 +1,16 @@ -material Grey -{ - technique - { - pass - { - depth_bias 1 - diffuse 0.800000 0.800000 0.800000 - specular 0.500000 0.500000 0.500000 12.500000 - texture_unit - { - texture grey_image.jpg - } - } - } -} +material Grey +{ + technique + { + pass + { + depth_bias 1 + diffuse 0.800000 0.800000 0.800000 + specular 0.500000 0.500000 0.500000 12.500000 + texture_unit + { + texture grey_image.jpg + } + } + } +} diff --git a/VRE/bin/media/materials/scripts/tacArm.material b/VRE/bin/media/materials/scripts/tacArm.material index 5b14bbe..6e6fbcc 100644 --- a/VRE/bin/media/materials/scripts/tacArm.material +++ b/VRE/bin/media/materials/scripts/tacArm.material @@ -1,18 +1,18 @@ -material Green -{ - technique - { - pass - { - scene_blend alpha_blend - depth_bias 0 - diffuse vertexcolour - specular 0.500000 0.500000 0.500000 12.500000 - texture_unit - { - texture green_image.jpg - alpha_op_ex source1 src_manual src_current 0.5 - } - } - } -} +material Green +{ + technique + { + pass + { + scene_blend alpha_blend + depth_bias 0 + diffuse vertexcolour + specular 0.500000 0.500000 0.500000 12.500000 + texture_unit + { + texture green_image.jpg + alpha_op_ex source1 src_manual src_current 0.5 + } + } + } +}