[chronojump] ForceSensor elastic calcule and plot repetitions
- From: Xavier de Blas <xaviblas src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [chronojump] ForceSensor elastic calcule and plot repetitions
- Date: Fri, 22 Nov 2019 16:47:48 +0000 (UTC)
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]