Program to output amplitude values of a Sine wave, but not getting expected result

Hello, first post been doing some programming as part of my university course and decided to make a program that gives me the value of amplitude of a sin wave depending on frequency, peak voltage, DC offset and phase angle as per the equation: V(t) = D+Vsin(2*pi(V*t+(phase angle/360)))

Where V is peak voltage, D is DC offset, phase angle in radians is just that, t is time

I wrote this program as I got fed up of calculating the values one instance at a time. It does work, however on the two instances where the Sin wave would cross the X axis, e.g. where t = 1ms V should = 0 instead of returning a value of 0 I get an infintesimally small number. However if I work out the equation using a calculator I get the correct value.

I think it could be how I'm doing the value of t, which has to be in milliseconds. In my for loop I increment at .125 ms, which at 1ms should return an integer. Below is my code. If anyone can point me in the right direction it will be much appreciated. This isn't coursework, this is something off my own back to help with another module.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <cmath>
#include <limits>
#include <iomanip>


using namespace std;

int main()

{
    double D;        // value for dc offset
    double f;        // frequency
    double V;        // peak amplitude
 //   double t;        // time in milliseconds
    double vT;       // result
    double theta;    // phase shift (radians)

    cout << "This program calculates amplitutde values for a sine wave"  << endl;
    cout << "Enter Frequency Value" << endl;
    cin >> f;
    cout << "Enter peak amplitude" << endl;
    cin >> V;
    cout << "Enter DC offset" << endl;
    cin >> D;
    cout << "Enter phase shift" << endl;
    cin >> theta;
    cout << endl;


    for (double t = 0; t <= 2; t=t + .125)
    {

        vT = D+V*sin(2*M_PI*((f*(t/1000)+(theta/360))));
        cout << vT << endl;
    }

    return 0;
}


Last edited on
@Vostok1,

Please put any future code snippets in code tags (the first item, <>, in the format menu).

I can't see anything major wrong with your code, except that to see any very significant output you will have to enter quite a high frequency. (Because you have gone up to t = 2ms or 0.002 s, you would need a frequency of 500 Hz to get a single cycle out of that loop; that's ten times the mains frequency in Britain).

Without a voltage offset or phase shift I get the correct single cycle.

Note that, although you might think the nodal values in the cycle should be exactly 0.0, it is quite likely that you will see numbers like 1.2246e-016. This is VERY small:
0.00000000000000012246
which you will appreciate is immeasurably different from 0. The difference is simply because floating-point numbers can only be stored to a finite accuracy (about the 14th sig fig for doubles).

So, I see nothing particularly wrong with your code, but you may need to choose realistic input values - especially for frequency.

To improve it, I suggest:
- working in SECONDS, rather than milliseconds, if possible;
- reducing the number of brackets in that mathematical expression;
- using an integer as a for-loop variable; so if i is the for-loop variable then t = i * dt, where dt is a timestep between outputs;
- if you are going to plot it, then the best idea would be to output two columns - time and voltage - by changing your output line to
cout << t << '\t' << vT << endl;
Then, if you redirect output to file you can plot it with a graph-plotting package like gnuplot. This is a gnuplot output for f=500, V = 2, D = 2, theta = 90:
https://imgur.com/a/zIVnaNZ
and that is correct for those parameters.
Last edited on
Hello. Thanks for reviewing my code,

I did try to use the code tag but for some reason the forum was timing out and playing up.

The program came about because of my telecommunications class so frequencies will be high. so 500Hz would be about right.

Funnily enough with the example problem at university we used the following:

F - 500Hz
D - 0
Vpk - 2V
Phase angle - 0 degrees(radians)

Now if I put in the time as 1x10^-3 in the equation (1ms) using a calculator

V(t) = D+Vsin(2*pi(V*t+(phase angle/360)))

v(t) = 0+2*sin(2*pi(500*1x10^-3+(0/360)) the answer is 0

Because 2sine(2pi(.5) is the same as sine(2pi) which equals 0 therefore I'm struggling to see how I'm getting a different result in the program.

You say the phase is in radians. If the phase is calculated as 2 pi * theta / 360, in radians this gives a value between 0 and (4 pi^2 / 360). Something smells fishy.

I also don't usually see the 2pi factored out, but I supposed it's OK as long as you're consistent.
I usually see it written A cos(2 pi f t + phase). In this form, the phase is from in range [0, 2Pi] and not [0, 4 pi^2].

Edit: Erased stupid math mistake.
Last edited on
vT = D+V*sin(2*M_PI*((f*(t/1000)+(theta/360))));

v(t) = 0+2*sin(2*pi(500*1x10^-3+(0/360))

2sine(2pi(.5)

Mathematically these may be the same, but on a computer they may not be.

1/1000 does not have an exact binary representation. Because of that, 500 * (1/1000) will not be exactly .5.

Instead, (f*t)/1000, should get you 0.5 exactly.

M_PI cannot be exact (it's an irrational number), so 2 * M_PI * [almost 0.5] is not the same as M_PI. The sine of that number will be a miniscule value, but not 0.


@Vostok,
Your phase angle is in degrees (within your program). It's actually quite hard to put angles in in radians.

For your input:
frequency is in Hertz (cycles per second)
dc offset is in volts
amplitude is in volts
phase angle is in degrees.

I don't think there is anything wrong with your program. BTW, you can go back and edit it and put it in code tags, so that we can run in CPP shell.

vostok wrote:
I'm struggling to see how I'm getting a different result in the program.
This is what your program is giving. IT IS CORRECT!
2.4e-16 is 2.4 x 10-16; i.e. effectively 0!! Are you familiar with scientific notation?
This program calculates amplitutde values for a sine wave
Enter Frequency Value
500
Enter peak amplitude
2
Enter DC offset
0
Enter phase shift
0

0
0.765367
1.41421
1.84776
2
1.84776
1.41421
0.765367
2.44921e-016       <===== This is 0.000000000000000244921,   i.e. about 0
-0.765367
-1.41421
-1.84776
-2
-1.84776
-1.41421
-0.765367
-4.89843e-016      <===== This is -0.000000000000000489843,   i.e. about 0
Last edited on
True, he did just mean to say degrees instead of radians.
Hello, yes I am familiar with scientific notation.

I do appreciate how the computer and the program will be more accurate than my calculator.

So since this is a program that is pumping out numerical values rather than plotting a sine wave, if I were to include an if statement within the loop to output a 0 if the value is close to 0?

BTW I do really appreciate your responses, I'm just trying to reconcile what I get on the calculator when manually doing the calculation.

I mentioned radians purely because when doing it mathematically the calculator needs to be set to radians to get the correct output, even though you enter the phase in degrees...



Last edited on
So since this is a program that is pumping out numerical values rather than plotting a sine wave, if I were to include an if statement within the loop to output a 0 if the value is close to 0

You can "round" it to a precision that you care about, and also print a fixed number of digits if you wish.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <iomanip>
#include <cmath>

int main()
{
    const int Precision = 10;
    
    for (int i = 0; i < 100; i++)
    {
        double t = 2 * 3.14159265358979 * static_cast<double>(i) / 100;
        
        std::cout << std::fixed << std::setprecision(Precision) << std::sin(t) << std::endl;
    }
}


To print without the trailing zeros is more complicated.
https://stackoverflow.com/a/277810
Last edited on
Try this @vostok17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;

int main()

{
    double D;        // dc offset (Volts)
    double f;        // frequency (Hertz)
    double V;        // amplitude (Volts)
    double vT;       // output (Volts)
    double theta;    // phase lead (degrees)

    cout << "Enter frequency (Hertz)" << endl;
    cin >> f;
    cout << "Enter amplitude (Volts)" << endl;
    cin >> V;
    cout << "Enter DC offset (Volts)" << endl;
    cin >> D;
    cout << "Enter phase lead (degrees)" << endl;
    cin >> theta;
    cout << endl;


    double dt = 0.125;
    cout << fixed << setprecision( 6 );
    for ( int i = 0; i <= 16; i++)
    {
        double t = i * dt;
        vT = D + V * sin ( 2 * M_PI * ( f * t / 1000 + theta / 360 ) );
        cout << t << '\t' << vT << endl;
    }
}


Enter frequency (Hertz)
500
Enter amplitude (Volts)
2
Enter DC offset (Volts)
0
Enter phase lead (degrees)
0

0.000000	0.000000
0.125000	0.765367
0.250000	1.414214
0.375000	1.847759
0.500000	2.000000
0.625000	1.847759
0.750000	1.414214
0.875000	0.765367
1.000000	0.000000
1.125000	-0.765367
1.250000	-1.414214
1.375000	-1.847759
1.500000	-2.000000
1.625000	-1.847759
1.750000	-1.414214
1.875000	-0.765367
2.000000	-0.000000



vostok17 wrote:
I mentioned radians purely because when doing it mathematically the calculator needs to be set to radians to get the correct output, even though you enter the phase in degrees...

No, that's not true.
Last edited on
I think he's just referring to functions in the standard library.
Last edited on
Cheers guys,

Thanks for the help, learned a lot here too.
Hello again.

Rather than starting a new thread I though I would extend this one.
I'm starting to practice functions and thought this would be a good program to start with.

I'm tackling it one function at a time, so sine model first.

As you can see in my code, I have taken the formula for the sine model and put it in a function and created a function that calls all the inputs. However when I compile I get the "the function will always be true" and when I run then sine model it out puts a series of results but they are all the same. So I'm trying to figure out what is best to do.

Have it so when you choose which model you want, it selects the function and all the input code is also in the function, or have it as it at the moment.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <iostream>
#include <cmath>
#include <limits>
#include <iomanip>

using namespace std;

double getSineValue(double D, double f, double V, double theta, double dt, double sampleT);

int main()

{
    double D;           // value for dc offset (Volts)
    double f;           // frequency (Herz)
    double V;           // peak amplitude (Volts)
    double vT;          // result (Volts(t)
    double theta;       // phase shift (radians)
    double dt;          // sample length (milliseconds)
    double sampleT;     // Sample rate (samples per second)
    int choice;         // User choice for sine model or cosine model

    //Ask user to choose between two options
    cout << "This program calculates amplitutde values for an analogue wave"  << endl;
    cout << endl;
    cout << "Enter 1 for Sine model, 2 for Cosine Model" << endl;
    cin >> choice;

    //Check user has entered an valid choice
    while (choice < 1 || choice > 2)
    {
        cout << "Enter 1 for Sine model, 2 for Cosine Model" << endl;
        cin >> choice;
    }
    if (choice == 1)
    {
        cout << "Sine model selected" << endl;
    }
    if (choice == 2)
    {
        cout << "Cosine model selected" << endl;
    }
    //User enters parameters to be execute calculation
    cout << "Enter Frequency Value (Herz):" << endl;
    cin >> f;
    cout << "Enter peak Voltage (Volts):" << endl;
    cin >> V;
    cout << "Enter DC offset (Volts):" << endl;
    cin >> D;
    cout << "Enter phase shift (degrees):" << endl;
    cin >> theta;
    cout << "Enter the sample length (milliseconds):" << endl;
    cin >> dt;
    cout << "Enter sample rate (samples per second):" << endl;
    cin >> sampleT;

    //Since we are looking at sample length in milliseconds, we need to make sure the entered value is converted
    //and sample rate has to be adjusted
    dt = dt/1000;
    sampleT = 1/sampleT;
    cout << endl;

    cout << "Time(ms)" << "\t" "\t" << "Volts" << endl;

    cout << fixed << setprecision( 3 );
    // This is the formula for the Sine model
    if (choice == 1)
    {
        getSineValue(f, V, D, theta, dt, sampleT);
    }
    cout << getSineValue << endl;

    // This is the formula for the Cosine model
    if (choice == 2)
    {
        cout << "Cosine modle selected" << endl;
        for (double t = 0; t <= dt; t=t + sampleT)
        {
            vT = D+V*cos(2*M_PI*((f*t)+(theta/360)));
            cout << t << "\t" "\t" << vT << endl;
        }
    }
    return 0;
}


double getSineValue(double D, double f, double V, double theta, double dt, double sampleT)
{

    {
        double sineValue;
        for (double t = 0; t <= dt; t=t + sampleT)
        {
            sineValue = D+V*sin(2*M_PI*((f*t)+(theta/360)));
            cout << t << "\t" "\t" << sineValue << endl;
        }
        return sineValue;
    }
}
getSineValue is the name of the function. You call it correctly on line 68.
Your call on line 70 is not calling the function -- it's printing 1 because that's what it interprets the name of the function as (the name of a function can be used as a pointer to that function, you don't need to understand this yet).

In other words, you need to store the value from the call to getSineValue, and print that instead.

Something like,
1
2
3
4
5
    if (choice == 1)
    {
        double value = getSineValue(f, V, D, theta, dt, sampleT);
        cout << value << endl;
    }
Last edited on
Hello Ganado,

Thank you very much for helping.
Topic archived. No new replies allowed.