I have extracted a CSV file with accelerometer data (in m/s2) from GoPro metadata file (github library).
One second of accelerometer contains ~200 samples of data on 3 axis. A sample of this file looks like this:
In PHP, for each instantaneous value on X axis, I convert m/s2 like this:
function convert_meters_per_second_squared_to_g($ms2) {
// 1g = 9.80665 m/s2
return $ms2 * 0.101971621297793; // 1 / 9.80665 == 0.101971621297793
}
Sample code for 200 rows (1 second) of CSV file:
$acc_x_summed_up = 0;
if (($handle = fopen($filepath, "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
list ($millis, $acc_x, $acc_y, $acc_z) = $data;
$acc_x_summed_up += $acc_x;
}
}
$g_force = convert_meters_per_second_squared_to_g($acc_x_summed_up);
But how do I show the g-force value for each second on X axis? I tried to sum up the values and convert them, but the result is clearly wrong, as I get values up to 63 G.
[ UPDATE: ]
The instant g-force values (all 3 axis, separated) are displayed on a graph (using highcharts). The gopro video file is displayed (using YouTube javascript API) side-by-side with the graph and played real time.
The graph and video are already working fine side by side. Only the g-force values are wrong.
Note: The video file has a g-force overlay (embeded in it) showing 2 axis (x,y).
I have rewarded #Joseph_J just because it seemed a good solution and because I'm forced to give the reward (over the weekend) by SO system. Thanks everyone for your answers!
I believe you are treating each instantaneous value as if it has occurred over 1 second, rather than instantaneously.
I'd say your best bet is to do each calculation by multiplying $acc_x by the resolution of your data divided by gravity's acceleration. So in your case, the resolution of your data is 5ms or one two-hundredth of a second, meaning your calculation should be $acc_x * 0.005/9.80665.
Using the information you provided, the 63G result that you got should be more like 0.315G. This seems more appropriate, though I'm not sure the context of the data.
EDIT: I forgot to mention that you should still sum all values that you receive from $acc_x * 0.005/9.80665 over 200 values, (you can choose to do this in blocks, or do it in running, doing in blocks will be less taxing on the system, but running will be more accurate). Pointed out by #Joseph_J
EDIT 2: As per your request of a source, I could not find much from calculating the average acceleration (and therefore g-force), but you can use the same principal behind average velocity from velocity over time graphs, however, I did find a scenario similar to yours here: Source and Source 2
Hope this helps!
As per my comment, summing it up doesn't work because force is not additive over time. What you want is to calculate the average acceleration:
function convert_meters_per_second_squared_to_g($acc_array) {
$acc_average = array_sum($acc_array)/count($acc_array);
return $acc_average * 0.101971621297793;
}
$acc_x_array = [];
if (($handle = fopen($filepath, "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
list ($millis, $acc_x, $acc_y, $acc_z) = $data;
$acc_x_array[] = $acc_x;
}
}
$g_force = convert_meters_per_second_squared_to_g($acc_x_array);
Maybe your question can be seen as equivalent to asking for the net change in velocity between samples at one-second intervals?
In that sense, what you need to do is to integrate-up all the small accelerations in your 5ms intervals, so as to compute the net change in velocity over a period of one second (i.e. 200 samples). That change in velocity, divided by the 1-second interval, represents an average acceleration during that 1-second period.
So, in your case, what you'd need to do is to add up all the AcclX, AcclY & AcclZ values over a one-second period, and multiply by 0.005 to get the vector representing the change in velocity (in units of metres per second). If you then divide that by the one-second total extent of the time window, and by 9.80665m/s^2, you'll end up with the (vector) acceleration in units of G. If you want the (scalar) acceleration you can then just compute the magnitude of that vector, as sqrt(ax^2+ay^2+az^2).
You could apply the same principle to get an average acceleration over a different time-window, so long as you divide the sum of AcclX,AcclY,AcclY (after multiplying by the 0.005s inter-sample time) by the duration of the time window over which you've integrated. This is just like approximating the time-derivative of a function f(t) by (f(t+d) - f(t))/d. In fact, this is a better approximation to the derivative at the midpoint of the time-interval, namely t+d/2. For example, you could sum up the values over a 2s window, to get an average value at the centre of that 2s timespan. There's no need to just report these average accelerations every two seconds; instead you could simply move the window along 0.5s to get the next reported average acceleration 0.5s later.
THE UPDATED UPDATED SOLUTION
This solution will take your CSV and create an array containing your time, Ax, Ay, & Az values after they have been converted to G's. You should be able to take this array and feed it right into your graph.
The value displayed at each interval will be the average acceleration "at" the interval no before or after it.
I added a parameter to the function to allow for you to define how many intervals per second that you want to display on your graph. This will help smooth out your graph a bit.
I also set the initial and final values. Since this finds the average acceleration at the interval it needs data on both sides of the interval. Obviously at 0 we are missing the left hand side and on the last interval we are missing the right hand side.
I chose to use all the data from one interval to the next, this overlaps half the values from one interval to the next. This will smooth out(reduce the noise) of the averages instead of pickup up from one interval where the other left off. I added a parameter where you can toggle the overlap on and off.
Hope it works for you!
function formatAccelData($data, $split, $scale, $overlap = TRUE){
if(!$data || !$split || !$scale || !is_int($split) || !is_int($scale)){
return FALSE;
}
$g = 9.80665;
$round = 3;
$value1 = 1;
$value2 = 2;
if(!$overlap){ //Toggle overlapping data.
$value1 = 2;
$value2 = 1;
}
//Set the initial condition at t=0;
$results = array();
$results[0]['seconds'] = 0;
$results[0]['Ax'] = round(($data[0][1])/$g, $round);
$results[0]['Ay'] = round(($data[0][2])/$g, $round);
$results[0]['Az'] = round(($data[0][3])/$g, $round);
$count = 1;
$interval = (int)(1000/$split)/$scale;
for($i = $interval; $i < count($data); $i += $interval){
$Ax = $Ay = $Az = 0;
for($j = $i - ($interval/$value1); $j < $i + ($interval/$value1); $j++){
$Ax += $data[$j][1];
$Ay += $data[$j][2];
$Az += $data[$j][3];
}
$results[$count]['seconds'] = round($count/$scale, $round);
$results[$count]['Ax'] = round(($Ax/($interval * $value2))/$g, $round);
$results[$count]['Ay'] = round(($Ay/($interval * $value2))/$g, $round);
$results[$count]['Az'] = round(($Az/($interval * $value2))/$g, $round);
$count++;
}
array_pop($results); //We do this because the last interval
//will not have enought data to be calculated.
//Set the final condition with the data from the end of the last complete interval.
$results[$count - 1]['seconds'] = round(($count - 1)/$scale, $round);
$results[$count - 1]['Ax'] = round(($data[$i - $interval][1])/$g, $round);
$results[$count - 1]['Ay'] = round(($data[$i - $interval][2])/$g, $round);
$results[$count - 1]['Az'] = round(($data[$i - $interval][3])/$g, $round);
return $results;
}
To use:
$data = array_map('str_getcsv', file($path));
$split = 5; //(int) - # of milliseconds inbetween datapoints.
$scale = 4; // (int) # of data points per second you want to display.
$overlap = TRUE; //(Bool) - Overlap data from one interval to the next.
$results = formatAccelData($data, $split, $scale, $overlap);
print_r($results);
THE OLD UPDATED SOLUTION
Remember, this function takes the average leading up to the interval. So it's really a half an interval behind.
function formatAccelData($data, $step){
$fps = 1000/$step;
$second = 1;
$frame = 0;
$count = 0;
for($i = 0; $i < count($data); $i += $fps){
$Ax = $Ay = $Az = 0;
for($j = 0; $j < $fps; $j++){
$Ax += $data[$frame][1];
$Ay += $data[$frame][2];
$Az += $data[$frame][3];
$frame++;
}
$results[$count]['seconds'] = $second;
$results[$count]['Ax'] = ($Ax/$fps) * 0.101971621297793;
$results[$count]['Ay'] = ($Ay/$fps) * 0.101971621297793;
$results[$count]['Az'] = ($Az/$fps) * 0.101971621297793;
$second++;
$count++;
}
return $results;
}
How to use:
$data = array_map('str_getcsv', file($path));
$step = 5; //milliseconds
$results = formatAccelData($data, $step);
print_r($results);
How to solve nth degree equations in PHP
Example:
1/(1+i)+1/(1+i)2+...1/(1+i)n=k
While k is the constant,I'd like to find value of i.
How can I achieve this in PHP?
First of all, your expression on the left is a geometric sum, so you can rewrite it as (using x=1+i)
1/x*(1+...+1/x^(n-1)) = 1/x * (1-1/x^n)/(1-1/x) = (1-x^(-n))/(x-1)
and consequently the equation can be rewritten as
(1 - pow( 1+i, -n))/i = k
Now from the original expression one knows that the left side as a sum of convex monotonically decreasing functions is equally so, thus any of bisection, regula falsi variants or secant method will work sufficiently well.
Use
(1+i)^(-n)=1 - n*i + (n*(n+1))/2*i^2 +...
to get the approximative equation and first approximation
1-(n+1)/2*i = k/n <=> i = (1-k/n)*2/(n+1)
so that you can start bracketing method with the interval from 0 to twice this i.
Try something like this....
$n = 5;
$i = 2;
$k = null;
for ($x = 1; $x <= $n; $x++) {
$k += 1 / pow((1 + $i), $x);
}
echo $k; //Answer --> 0.49794238683128
I'm trying to distribute 100% to total numbers (not equally), it can be done manually but I'm looking for a automatically way in PHP. I had to open calculator and get it done for manual.
What I'm trying to achieve is the result similar to this:
$value = 10000;
$total_numbers = 9
$a1 = $value*0.2;
$a2 = $value*0.175;
$a3 = $value*0.15;
$a4 = $value*0.125;
$a5 = $value*0.1;
$a6 = $value*0.08;
$a7 = $value*0.07;
$a8 = $value*0.05;
$a9 = $value*0.04;
So as you can see, the first variables have more quantity than the later ones, but if you add these, it will be 1 which is 100%. So lets say I have total_numbers=20 then I'll have to re-write it and get a calculator and do it the hard way to accomplish my goal. Is there any way this can be done automatically with a function where I can just tell the total number and it can distribute it to proportions or something?
The first one will always be bigger than rest, then second one bigger than rest but smaller than first, third one being greater than rest but small than first and second, and so on.
function distributeValue($value, $num) {
$parts = $num * ($num + 1) / 2;
$values = [];
for ($i = $num; $i > 1; --$i) {
$values[] = round($value * $i / $parts);
}
$values[] = $value - array_sum($values);
return $values;
}
var_dump(distributeValue(10000, 9));
This works by calculating the $numth triangle number (the number you get by adding all the numbers from 1 to $num) and dividing the total value up into this number of parts.
It then starts by taking $num parts, then $num-1 parts and so on.
Since it's rounding the numbers, the last step is to take the total minus all the other values which is around one part. If you are fine with getting floats instead of ints out, then you can remove the $values[] = $value - array_sum($values); line and change the condition of the for loop to $i > 0.
Hey tried to search for similar but guess my english fail me lol, well here is what i need help with, im trying to make a vote system with up/down vote and wanna show it like this
5.3/10
but have no idea how to make total "5.3" not go over 100% = 10 here is my code so far
<?php
$Vote_up = 804;
$Vote_down = 942;
$total = $Vote_up + $Vote_down;
$result = 100;
echo number_format($total/$result,1,",",".") . "/10";
?>
result is 17,5/10
ps. new to php so be easy on me ^^
i am really bad at maths but i think this should be work
$Vote_up = 555;
$Vote_down = 555;
$total = $Vote_up + $Vote_down;
if( $total <= 0 ){
$score = -11;
} else {
$score = (($Vote_up / $total) + ($Vote_down / $total) * 10) * -1;
}
echo floor( $score + 11 ) . '/10';
if someone have a better solution, please i would like to know it - thanks!
i feel a little bit stupid, but let's imagine, that i have a set of unix timestamps:
1375110404
1374660925
1374482694
1374242337
1373793867
1373632889
1373187141
1373021668
1372754021
1372599890
What i'm trying to achieve is simple: I just want to calculate the average time difference between these 10 timestamps. I just can't find the proper way for the calculation.
What i just tried was
1375110404 - 1374660925 = 449479
1374482694 - 1374242337 = 240357
1373793867 - 1373632889 = 160978
1373187141 - 1373021668 = 165473
1372754021 - 1372599890 = 154131
449479 + 240357 + 160978 + 165473 + 154131 = 1170418
1170418 / 5 = 234083,6
but that looks illogical to me. Any advice is greatly appreciated.
EDIT:
All these stamps come from a php array.
EDIT:
Thanks to Orangepill for pointing me to the right direction. Here's the final solution:
for($cnt = count($array), $res = 0, $i = 1; $i < $cnt; $i++) {
$res += $array[$i-1] - $array[$i];
}
echo $res/$cnt;
This calculates
1375110404 - 1374660925 = 449479
1374660925 - 1374482694 = 178231
1374482694 - 1374242337 = 240357
1374242337 - 1373793867 = 448470
1373793867 - 1373632889 = 160978
1373632889 - 1373187141 = 445748
1373187141 - 1373021668 = 165473
1373021668 - 1372754021 = 267647
1372754021 - 1372599890 = 154131
449479 + 178231 + 240357 + 448470 + 160978 + 445748 + 165473 + 267647 + 154131 = 2510514
2510514 / 10 = 251051.4
which looks correct to me.
The most straight forward way it to do it like you described.
$res =0;
for($x = 1, $num = count($array); $x < $num; $x++){
$res =+ $array[$x] - $array[$x-1];
}
echo $res/($num-1);
The current accepted answer will give incorrect results if the the timestamps are not in strict chronological order. That is, negative values will skew your average. I may be wrong, but I imagine that you don't want to count any time difference as a negative value, after all, you can't run 100m in -12 seconds!
I provide this answer as an alternative that will always give an average based on positive time differences regardless of the order of the times in the passed array:-
function array_average_diff(array $array)
{
$diff = 0;
for($i = 1; $i < count($array); $i++){
$diff += abs($array[$i] - $array[$i - 1]);
}
return $diff/count($array);
}
See it working