[chronojump] ForceSensor elastic calcule and plot repetitions



commit 573fcf3e0d6845b9c2622e90944820039764baa1
Author: Xavier de Blas <xaviblas gmail com>
Date:   Fri Nov 22 17:46:26 2019 +0100

    ForceSensor elastic calcule and plot repetitions

 src/forceSensor.cs            |  13 ++--
 src/forceSensorDynamics.cs    | 136 +++++++++++++++++++++++++++++++++++++++---
 src/gui/forceSensorAnalyze.cs |  30 ++++++++--
 3 files changed, 161 insertions(+), 18 deletions(-)
---
diff --git a/src/forceSensor.cs b/src/forceSensor.cs
index 920e52b6..c2f4c1a9 100644
--- a/src/forceSensor.cs
+++ b/src/forceSensor.cs
@@ -1313,6 +1313,7 @@ public class ForceSensorAnalyzeInstant
        public List<double> Speed_l;
        public List<double> Accel_l;
        public List<double> Power_l;
+       public List<ForceSensorRepetition> ForceSensorRepetition_l;
 
        private ForceSensorCapturePoints fscAIPoints; //Analyze Instant
        private ForceSensorValues forceSensorValues;
@@ -1406,6 +1407,7 @@ public class ForceSensorAnalyzeInstant
                        Speed_l = fsd.GetSpeeds();
                        Accel_l = fsd.GetAccels();
                        Power_l = fsd.GetPowers();
+                       ForceSensorRepetition_l = fsd.GetRepetitions();
                        CalculedElasticPSAP = true;
                }
 
@@ -1453,9 +1455,10 @@ public class ForceSensorAnalyzeInstant
                return fscAIPoints.GetLength();
        }
 
-       public int GetXFromSampleCount(int currentPos, int totalPos)
+       //public int GetXFromSampleCount(int currentPos, int totalPos)
+       public int GetXFromSampleCount(int currentPos)
        {
-               LogB.Information(string.Format("currentPos: {0}, totalPos: {1}", currentPos, totalPos));
+               //LogB.Information(string.Format("currentPos: {0}, totalPos: {1}", currentPos, totalPos));
                //this can be called on expose event before calculating needed parameters
                if(graphWidth == 0)
                        return 0;
@@ -1600,8 +1603,8 @@ public class ForceSensorAnalyzeInstant
                LogB.Information(string.Format("CalculateRFDTangentLine: {0}" , countRFDMax));
 
                // 1) calculate X and Y of points before and after RFD
-               int pointXBefore = GetXFromSampleCount(countRFDMax -1, GetLength());
-               int pointXAfter = GetXFromSampleCount(countRFDMax +1, GetLength());
+               int pointXBefore = GetXFromSampleCount(countRFDMax -1);
+               int pointXAfter = GetXFromSampleCount(countRFDMax +1);
                int pointYBefore = GetPxAtForce(GetForceAtCount(countRFDMax -1));
                int pointYAfter = GetPxAtForce(GetForceAtCount(countRFDMax +1));
 
@@ -1610,7 +1613,7 @@ public class ForceSensorAnalyzeInstant
                                        (1.0 * (pointXAfter- pointXBefore)) ) );
 
                // 3) get the RFD point
-               int pointXRFD = GetXFromSampleCount(countRFDMax, GetLength());
+               int pointXRFD = GetXFromSampleCount(countRFDMax);
                int pointYRFD = GetPxAtForce(GetForceAtCount(countRFDMax));
 
                // 4) calculate line that cross RFD point with calculated slope
diff --git a/src/forceSensorDynamics.cs b/src/forceSensorDynamics.cs
index 55b21460..ec9f2181 100644
--- a/src/forceSensorDynamics.cs
+++ b/src/forceSensorDynamics.cs
@@ -87,7 +87,8 @@ public abstract class ForceSensorDynamics
                return force_l;
        }
 
-       //this 4 are only implemented on elastic
+       //----- start of: only implemented on elastic ----->
+
        public virtual List<double> GetPositions()
        {
                return new List<double>();
@@ -107,15 +108,18 @@ public abstract class ForceSensorDynamics
        {
                return new List<double>();
        }
+
+       public virtual List<ForceSensorRepetition> GetRepetitions()
+       {
+               return new List<ForceSensorRepetition>();
+       }
+
+       //<----- end of: only implemented on elastic -----
+
 }
        
 public class ForceSensorDynamicsNotElastic : ForceSensorDynamics
 {
-       List<double> position_l;
-       List<double> speed_l;
-       List<double> accel_l;
-       List<double> power_l;
-
        public ForceSensorDynamicsNotElastic (List<int> time_micros_l, List<double> force_l, 
                        ForceSensor.CaptureOptions fsco, ForceSensorExercise fse,
                        double personMass, double stiffness)
@@ -150,10 +154,14 @@ public class ForceSensorDynamicsNotElastic : ForceSensorDynamics
 
 public class ForceSensorDynamicsElastic : ForceSensorDynamics
 {
+       //this used to calcule the repetitions. All the values on this class are smoothed except this.
+       List<double> position_not_smoothed_l;
        List<double> position_l;
+
        List<double> speed_l;
        List<double> accel_l;
        List<double> power_l;
+       List<ForceSensorRepetition> forceSensorRepetition_l;
 
        public ForceSensorDynamicsElastic (List<int> time_micros_l, List<double> force_l, 
                        ForceSensor.CaptureOptions fsco, ForceSensorExercise fse,
@@ -194,16 +202,19 @@ public class ForceSensorDynamicsElastic : ForceSensorDynamics
        {
                //TODO: check minimum length of forces
 
+               position_not_smoothed_l = new List<double>();
                position_l = new List<double>();
                speed_l = new List<double>();
                accel_l = new List<double>();
                power_l = new List<double>();
+               forceSensorRepetition_l = new List<ForceSensorRepetition>();
 
                calculePositions();
                calculeSpeeds();
                calculeAccels();
                calculeForces();
                calculePowers();
+               calculeRepetitions();
        }
 
        private int smoothFactor = 5; //use odd (impar) values like 5, 7, 9
@@ -219,7 +230,7 @@ public class ForceSensorDynamicsElastic : ForceSensorDynamics
                for(int i = 0; i < Math.Floor(smoothFactor /2.0); i ++)
                        smoothed_l.Add(0);
 
-               for(int i = 2; i < position_l.Count -2; i ++)
+               for(int i = 2; i < original_l.Count -2; i ++)
                        smoothed_l.Add( (original_l[i-2] + original_l[i-1] + original_l[i] + original_l[i+1] 
+ original_l[i+2]) / 5.0 );
 
                for(int i = 0; i < Math.Floor(smoothFactor /2.0); i ++)
@@ -232,9 +243,9 @@ public class ForceSensorDynamicsElastic : ForceSensorDynamics
        private void calculePositions()
        {
                for (int i = 0 ; i < force_l.Count; i ++)
-                       position_l.Add(force_l[i] / stiffness);
+                       position_not_smoothed_l.Add(force_l[i] / stiffness);
 
-               position_l = smoothVariable(position_l);
+               position_l = smoothVariable(position_not_smoothed_l);
        }
 
        private void calculeSpeeds()
@@ -300,6 +311,89 @@ public class ForceSensorDynamicsElastic : ForceSensorDynamics
                }
        }
 
+       //adapted from r-scripts/forcePosition.R
+       private void calculeRepetitions()
+       {
+               //uses time_l (seconds), position_not_smoothed, force (also raw)
+               List<double> posList = position_not_smoothed_l;
+
+               //TODO: put this on exercise or on preferences for all (like on encoder)
+               double conMinDisplacement = .1;
+               double eccMinDisplacement = .1;
+               double minDisplacement;
+
+               //The comments supposes that the current phase is concentric. In the case that the phase is 
eccentric
+               //the signal is inverted by multiplying it by -1.
+
+               //for each phase, stores the sample number of the biggest current sample.
+               int possibleExtremeSample = 0;
+
+               //Stores the sample of the last actual maximum of the phase
+               int lastExtremeSample = 0;
+
+               int currentSample = 1;
+
+               //The firstPhase is treated different
+               bool firstPhase = true;
+
+               int concentricFlag; //1: concentric; -1: excentric
+
+               //Detecting the first phase type
+               if(posList[currentSample] > posList[possibleExtremeSample])
+               {
+                       concentricFlag = 1;
+                       minDisplacement = eccMinDisplacement;
+               } else {
+                       concentricFlag = -1;
+                       minDisplacement = conMinDisplacement;
+               }
+
+               while(currentSample < posList.Count)
+               {
+                       //Checking if the current position is greater than the previous possilble maximum
+                       if(concentricFlag * posList[currentSample] > concentricFlag * 
posList[possibleExtremeSample])
+                       {
+                               //The current sample is the new candidate to be a maximum
+                               //LogB.Information(string.Format("updated possibleExtremeSample to: {0}; 
position: {1}", currentSample, posList[currentSample]));
+                               possibleExtremeSample = currentSample;
+                       }
+
+                       //Checking if the current position is at minDisplacement below the last possible 
extreme
+                       if( concentricFlag * posList[currentSample] - concentricFlag * 
posList[possibleExtremeSample] < - minDisplacement
+                                       //For the first phase the minDisplacement is considered much smaller 
in order to detect an extreme in small oscillations
+                                       || ( firstPhase
+                                               && (concentricFlag * posList[currentSample] - concentricFlag 
* posList[possibleExtremeSample] < - minDisplacement / 10) ) )
+                       {
+                               if(firstPhase)
+                                       firstPhase = false;               //End of the first phase special 
treatment
+
+                               //LogB.Information(string.Format("-----------minDisplacement detected at: 
{0}", currentSample));
+                               //LogB.Information(string.Format("Extreme added at: {0}", 
possibleExtremeSample));
+
+                               //Save the sample of the last extrme in order to compare new samples with it
+                               lastExtremeSample = possibleExtremeSample;
+
+                               //Changing the phase from concentric to eccentril or viceversa
+                               concentricFlag *= -1;
+                               if (concentricFlag > 0)
+                                       minDisplacement = eccMinDisplacement;
+                               else
+                                       minDisplacement = conMinDisplacement;
+
+                               //Calculate mean RFD and mean speed of the phase
+                               double lastRFD = (force_l[currentSample] - force_l[lastExtremeSample]) / 
(time_l[currentSample] - time_l[lastExtremeSample]);
+                               double lastMeanSpeed = (posList[currentSample] - posList[lastExtremeSample]) 
/ (time_l[currentSample] - time_l[lastExtremeSample]);
+
+                               int possibleExtremeSampleSend = possibleExtremeSample - (RemoveNValues -1);
+                               if(possibleExtremeSampleSend < 0)
+                                       possibleExtremeSampleSend = 0;
+                               forceSensorRepetition_l.Add(new 
ForceSensorRepetition(possibleExtremeSampleSend, lastMeanSpeed, lastRFD));
+                       }
+
+                       currentSample += 1;
+               }
+       }
+
        private List<double> stripStartEnd(List<double> l)
        {
                LogB.Information(string.Format("removeN: {0}, l.Count: {1}", RemoveNValues, l.Count));
@@ -330,4 +424,28 @@ public class ForceSensorDynamicsElastic : ForceSensorDynamics
        {
                return stripStartEnd(power_l);
        }
+
+       public override List<ForceSensorRepetition> GetRepetitions()
+       {
+               return forceSensorRepetition_l;
+       }
+}
+
+public class ForceSensorRepetition
+{
+       public int posX;
+       public double meanSpeed;
+       public double RFD;
+
+       public ForceSensorRepetition(int posX, double meanSpeed, double RFD)
+       {
+               this.posX = posX;
+               this.meanSpeed = meanSpeed;
+               this.RFD = RFD;
+       }
+
+       public override string ToString()
+       {
+               return string.Format("posx:{0}; meanSpeed:{1}; RFD:{2}", posX, meanSpeed, RFD);
+       }
 }
diff --git a/src/gui/forceSensorAnalyze.cs b/src/gui/forceSensorAnalyze.cs
index c50e29e9..e6393106 100644
--- a/src/gui/forceSensorAnalyze.cs
+++ b/src/gui/forceSensorAnalyze.cs
@@ -625,6 +625,7 @@ public partial class ChronoJumpWindow
        Gdk.GC pen_yellow_force_ai;             //0 force
        Gdk.GC pen_yellow_light_force_ai;       //feedback rectangle on analyze to differentiate from yellow 
AB lines
        Gdk.GC pen_white_force_ai;              //white box to ensure yellow text is not overlapped
+       Gdk.GC pen_green_force_ai;              //repetitions (vertical lines)
 
        private void forceSensorAIPlot()
        {
@@ -653,6 +654,7 @@ public partial class ChronoJumpWindow
                colormapForceAI.AllocColor (ref UtilGtk.BLUE_PLOTS,true,true);
                colormapForceAI.AllocColor (ref UtilGtk.RED_PLOTS,true,true);
                colormapForceAI.AllocColor (ref UtilGtk.GRAY,true,true);
+               colormapForceAI.AllocColor (ref UtilGtk.GREEN_PLOTS,true,true);
                bool success = colormapForceAI.AllocColor (ref UtilGtk.YELLOW,true,true);
                colormapForceAI.AllocColor (ref UtilGtk.YELLOW_LIGHT,true,true);
                LogB.Information("Yellow success!: " + success.ToString()); //sempre dona success
@@ -671,6 +673,7 @@ public partial class ChronoJumpWindow
                pen_yellow_light_force_ai = new Gdk.GC(force_sensor_ai_drawingarea.GdkWindow);
                pen_white_force_ai = new Gdk.GC(force_sensor_ai_drawingarea.GdkWindow);
                pen_gray_discont_force_ai = new Gdk.GC(force_sensor_ai_drawingarea.GdkWindow);
+               pen_green_force_ai = new Gdk.GC(force_sensor_ai_drawingarea.GdkWindow);
 
                pen_black_force_ai.Foreground = UtilGtk.BLACK;
                pen_blue_force_ai.Foreground = UtilGtk.BLUE_PLOTS;
@@ -679,6 +682,7 @@ public partial class ChronoJumpWindow
                pen_yellow_light_force_ai.Foreground = UtilGtk.YELLOW_LIGHT;
                pen_white_force_ai.Foreground = UtilGtk.WHITE;
                pen_gray_discont_force_ai.Foreground = UtilGtk.GRAY;
+               pen_green_force_ai.Foreground = UtilGtk.GREEN_PLOTS;
 
                //pen_black_force_ai.SetLineAttributes (2, Gdk.LineStyle.Solid, Gdk.CapStyle.NotLast, 
Gdk.JoinStyle.Miter);
                //this makes the lines less spiky:
@@ -691,6 +695,7 @@ public partial class ChronoJumpWindow
                pen_yellow_light_force_ai.SetLineAttributes (2, Gdk.LineStyle.Solid, Gdk.CapStyle.Round, 
Gdk.JoinStyle.Round);
                pen_white_force_ai.SetLineAttributes (1, Gdk.LineStyle.Solid, Gdk.CapStyle.Round, 
Gdk.JoinStyle.Round);
                pen_gray_discont_force_ai.SetLineAttributes(1, Gdk.LineStyle.OnOffDash, Gdk.CapStyle.Butt, 
Gdk.JoinStyle.Round);
+               pen_green_force_ai.SetLineAttributes (1, Gdk.LineStyle.Solid, Gdk.CapStyle.Round, 
Gdk.JoinStyle.Round);
 
                layout_force_ai_text = new Pango.Layout (force_sensor_ai_drawingarea.PangoContext);
                layout_force_ai_text.FontDescription = Pango.FontDescription.FromString ("Courier 10");
@@ -894,7 +899,7 @@ public partial class ChronoJumpWindow
                int hscaleHigher = Convert.ToInt32(hscale_force_sensor_ai_b.Value);
 
                // 5) paint vertical yellow lines A, B and write letter
-               int xposA = fsAI.GetXFromSampleCount(hscaleLower, fsAI.GetLength());
+               int xposA = fsAI.GetXFromSampleCount(hscaleLower);
                force_sensor_ai_pixmap.DrawLine(pen_yellow_force_ai,
                                xposA, 0, xposA, allocation.Height -20);
 
@@ -914,7 +919,7 @@ public partial class ChronoJumpWindow
                int xposB = 0;
                if(checkbutton_force_sensor_ai_b.Active && hscaleLower != hscaleHigher)
                {
-                       xposB = fsAI.GetXFromSampleCount(hscaleHigher, fsAI.GetLength());
+                       xposB = fsAI.GetXFromSampleCount(hscaleHigher);
                        force_sensor_ai_pixmap.DrawLine(pen_yellow_force_ai,
                                        xposB, 0, xposB, allocation.Height -20);
 
@@ -945,6 +950,16 @@ public partial class ChronoJumpWindow
                        hbox_force_sensor_ai_power.Visible = false;
                }
 
+               if(fsAI.CalculedElasticPSAP)
+                       foreach(ForceSensorRepetition fsr in fsAI.ForceSensorRepetition_l)
+                       {
+                               // paint vertical line for each rep
+                               int xposRep = fsAI.GetXFromSampleCount(fsr.posX);
+                               force_sensor_ai_pixmap.DrawLine(pen_green_force_ai,
+                                               xposRep, 0, xposRep, allocation.Height -20);
+                       }
+
+
                // 6) if only A calculate exit
                if(! checkbutton_force_sensor_ai_b.Active)
                        return;
@@ -980,7 +995,7 @@ public partial class ChronoJumpWindow
 
                        int countRFDMax = fsAI.LastRFDMaxCount;
 
-                       int rfdX = fsAI.GetXFromSampleCount(countRFDMax, fsAI.GetLength());
+                       int rfdX = fsAI.GetXFromSampleCount(countRFDMax);
                        int rfdY = fsAI.GetPxAtForce(fsAI.GetForceAtCount(countRFDMax));
 
                        // draw a circle of 12 points width/length, move it 6 points top/left to have it 
centered
@@ -1017,7 +1032,7 @@ public partial class ChronoJumpWindow
                        if(i < 0 || i > fsAI.GetLength() -1)
                                continue;
 
-                       int segXDebug = fsAI.GetXFromSampleCount(i, fsAI.GetLength());
+                       int segXDebug = fsAI.GetXFromSampleCount(i);
                        int segYDebug = fsAI.GetPxAtForce(fsAI.GetForceAtCount(i));
                        force_sensor_ai_pixmap.DrawArc(pen_black_force_ai, false,
                                        segXDebug -3, segYDebug -3,
@@ -1325,6 +1340,13 @@ public partial class ChronoJumpWindow
                        double powerA = fsAI.Power_l[countA];
                        double powerB = fsAI.Power_l[countB];
                        label_force_sensor_ai_power_diff.Text = Math.Round(powerB - powerA, 3).ToString();
+
+                       /*
+                       //print the repetitions stuff
+                       LogB.Information("Printing repetitions:");
+                       foreach(ForceSensorRepetition fsr in fsAI.ForceSensorRepetition_l)
+                               LogB.Information(fsr.ToString());
+                       */
                }
 
                double rfdA = 0;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]