Reputation: 45
I'm writing a program that calculates the distance traveled when an object is thrown straight up with a given speed.
Now I need to implement angles to see how that affects the height loss per second compared to when it is thrown straight up.
I was told to use the math library but I have no idea from where to go from here.
//The following calculation can be used to calculate the correlation between time and height
//h(t )=−0.5⋅G⋅t^2+V⋅T+H
#include <iostream>
#include <limits>
using namespace std;
int main() {
//Falling speed
const double G = 9.81;
//Starting height
double H = 0;
//Velocity/Speed
double V = 0;
//End product
double Ht = 0;
//Seconds that result is displayed on the screen
double T = 0;
//t^2
double Tt = 0;
int S;
int s = 1;
cout << "\nGive me the starting height: ";
while(!(cin >> H)){
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "False input. Try again: ";
}
cout << "\nGive me the starting speed: ";
while(!(cin >> V)){
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "False input. Try again: ";
}
cout << "\nHow often do you want the current height to be displayed (In seconds)\n";
while(!(cin >> S)){
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "False input. Try again: ";
}
for (int i = 0; i < 10; i = i + S) {
Tt = i*i;
Ht = -0.5 * G * Tt + V * i + H;
if (Ht >= 0){
cout << "Second " << s << " the height is " << Ht << " Meters" << endl;
s += 1;
} else {
Ht = 0;
cout << "Second " << s << " the height is " << Ht << " Meters" << endl;
i = 9;
}
}
}
Any help is appreciated.
Upvotes: 0
Views: 164
Reputation: 84599
You are starting out in the right direction, but you are stumbling into a few pitfalls trying to implement "special cases" to keep your arithmetic sign correct. Don't do that. In kinematics and dynamics every position, velocity or acceleration is relative to the coordinate system you choose. If you are consistent with your vector direction -- you never have to worry about a special case again.
Huh? What do you mean?
Take your gravity term, const double G = 9.81;
. Unless you have defined the Y coordinate as positive in the down direction, your sign on G
is wrong (pitfall...). Most normal cartesian coordinates systems have Y as positive up (screen coordinates and aircraft body coordinates are exceptions). With the Y axis positive-up, what direction does gravity act in? (if confused, "drop a rock" -- advise from one of my old profs :)
Gravity acts in the downward direction and therefore should be declared as a negative value with Y positive-up. Since as your code grows, virtually all calculation functions will need that term, so you may as well make it global:
static const double G = -9.81; /* global constant is fine, pay attention to sign */
Then for every position calculation you make based on acceleration and time simply uses the standard formula:
X = Xo + Vo * t + 0.5 * a * t * t;
No need to go inserting spurious '-'
signs in the calculation or splitting out special cases for output. As you add forces that act on your projectile keeping your force directions straight becomes critical. All forces are vector quantities and have a direction. If you always attribute the correct direction to each force, you never have to adjust a formula or worry about a special case again.
For your calculations that means you can simply use:
for (int i = 0; i < 15; i = i + S) {
double Tt = i * i, /* seconds squared */
Ht = H + V * i + 0.5 * G * Tt, /* pos based on acceleration & time */
Vt = V + G * i; /* vel based on acceleration & time */
...
(note: the base number of iterations was increased to 15
which is what is needed to adequately see the height reversal for objects fired upward at 100 m/s or less -- any faster and atmospheric compressability becomes a factor)
Consider a Continual Loop for Input
When you want to require the user to provide valid input, it is more convenient to use a continual loop and break the read-loop when your conditions are satisfied. (This allows you to fully contain the prompt and error reporting within the loop). For example, you could do:
while (true) { /* loop continually until valid input (prompt within) */
std::cout << "\nStarting height : ";
if (std::cin >> H)
break;
std::cin.clear();
std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
std::cerr << "False input. Try again: "; /* error output on cerr */
}
(note: Good job on properly using std::cin.ignore()
, you may also want to distinguish between eofbit
and failbit
being set later on, but you do a good job of validation)
After the final input, you can add a std::cout.put ('\n');
to provide a newline separating the input and output values.
Putting that altogether for your example, and saving the formatting flags for std::cout
and then setting fixed
output with a precision of 2
you could do:
#include <iostream>
#include <iomanip>
#include <limits>
static const double G = -9.81; /* global constant is fine, pay attention to sign */
int main () {
double H = 0; /* starting height */
double V = 0; /* initial velocity */
int S; /* time step (seconds) */
while (true) { /* loop continually until valid input (prompt within) */
std::cout << "\nStarting height : ";
if (std::cin >> H)
break;
std::cin.clear();
std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
std::cerr << "False input. Try again: "; /* error output on cerr */
}
while (true) {
std::cout << "Starting speed : ";
if (std::cin >> V)
break;
std::cin.clear();
std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
std::cerr << "False input. Try again: ";
}
while (true) {
std::cout << "Time Step (In seconds) : ";
if (std::cin >> S) {
std::cout.put ('\n'); /* tidy up with additional newline */
break;
}
std::cin.clear();
std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
std::cerr << "False input. Try again: ";
}
std::ios_base::fmtflags f = std::cout.flags(); /* save format flags */
std::cout << std::fixed; /* set fixed precision */
std::cout.precision(2); /* of 2 */
for (int i = 0; i < 15; i = i + S) {
double Tt = i * i, /* seconds squared */
Ht = H + V * i + 0.5 * G * Tt, /* pos based on acceleration & time */
Vt = V + G * i; /* vel based on acceleration & time */
std::cout << "Second: " << std::setw(3) << i
<< " Height: " << std::setw(6) << Ht
<< " Velocity: " << std::setw(6) << Vt << '\n';
}
std::cout.flags(f); /* restore default precision */
}
(note: there are no "special cases" in the computations or in the output formatting. Also consider making S
type double
so you can enter, e.g. .2
or .1
as the time step for finer granularity in your output. You can also add input for StartTime
and StopTime
which would allow extremely fine-grained examination of output over short (or long) time periods)
Example Use/Output
If you take for example a projectile with an initial height of 10
meters and an initial upward velocity of 100 m/s
, you can observe where the velocity decreases to zero and then beginning increasing as it falls back to earth. You will see the like reversal in height.
$ ./bin/kinematic_h+v
Starting height : 10
Starting speed : 100
Time Step (In seconds) : 1
Second: 0 Height: 10.00 Velocity: 100.00
Second: 1 Height: 105.09 Velocity: 90.19
Second: 2 Height: 190.38 Velocity: 80.38
Second: 3 Height: 265.86 Velocity: 70.57
Second: 4 Height: 331.52 Velocity: 60.76
Second: 5 Height: 387.38 Velocity: 50.95
Second: 6 Height: 433.42 Velocity: 41.14
Second: 7 Height: 469.65 Velocity: 31.33
Second: 8 Height: 496.08 Velocity: 21.52
Second: 9 Height: 512.69 Velocity: 11.71
Second: 10 Height: 519.50 Velocity: 1.90
Second: 11 Height: 516.50 Velocity: -7.91
Second: 12 Height: 503.68 Velocity: -17.72
Second: 13 Height: 481.05 Velocity: -27.53
Second: 14 Height: 448.62 Velocity: -37.34
If you wanted a time step of two, you would do:
$ ./bin/kinematic_h+v
Starting height : 10
Starting speed : 100
Time Step (In seconds) : 2
Second: 0 Height: 10.00 Velocity: 100.00
Second: 2 Height: 190.38 Velocity: 80.38
Second: 4 Height: 331.52 Velocity: 60.76
Second: 6 Height: 433.42 Velocity: 41.14
Second: 8 Height: 496.08 Velocity: 21.52
Second: 10 Height: 519.50 Velocity: 1.90
Second: 12 Height: 503.68 Velocity: -17.72
Second: 14 Height: 448.62 Velocity: -37.34
(note: to solve for the exact time when the projectile reaches its maximum height, simply set the final velocity to 0
in your normal V = Vo + a * t
equation and solve for t
, e.g. t = -Vo / a
. Here is a nice Kinematics calculator - Physics Catalyst that also provides the nuts and bolts equations of motion for your use.)
Lastly, have a look at Why is “using namespace std;” considered bad practice?. Building good habits early is a lot easier than breaking bad ones later...
Moving to 3-Dimensional Space
There is virtually no difference in how you approach a 3-Dimensional problem. Instead of using H
and V
is becomes easier to use a struct
that holds the double x, y, z;
coordinates and velocities. Then your functions simply operates on the x
, y
and z
members independently using the very same formulas. Then for the final values just square-root of the sum of the squares and atan2()
for resulting angles.
Additionally, when working in 3-Dimensions, you can make use of the vector Dot and Cross products, which become increasingly useful as you beginning working with angular rates and measures.
Look things over and let me know if you have further questions.
Upvotes: 1