Derive peak value from two points on a sinewave?

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
Hi, I'm doing a DSP application on quite limited microcontroller hardware, and need a fast crude process to find a sinewave peak value based on two points on the sinewave which are guaranteed to be 90 degrees apart.

The two points are already full-wave rectified, so there are no negative values. Both values are already in the range 0-127 (7bit binary).

Normally this is found as the "square root of the sum of the two squares", which obviously works fine but I need to do this fast with no squares or roots, hopefully just with some additions/subtractions, and binary mult/divide (left/right shifts on a micro).

I also prefer not to use a lookup table. The good news is that the final accuracy only needs to be +/- 3%.

As an example I am currently using this calc;
peak = biggest - dif/4
(dif = biggest-smallest, where biggest and smallest are the two values), and it gets pretty close;

Rich (BB code):
Big	Small	RMS	Mine
0	100	71	75
36	90	71	76
71	71	71	71
This gives a result within about 8% of the real RMS value, so it's almost accurate enough but leaves me with a problem of turning the RMS result into a peak result (ie *1.41).

If anyone has a fast simple way of getting a peak value within +/-3% or so it would be much appreciated! :)
 

MrChips

Joined Oct 2, 2009
30,806
No.

To multiply by 1.414, multiply by 362 and divide by 256 (i.e. use the upper byte).

If the multiplication has the potential to exceed 16 bits, then there are ways of dealing with this,

such as...

multiply by 106 and divide by 256, then add the original number.

(In your case, the number is 7 bits and hence the product will not exceed 16 bits).
 
Last edited:

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
Thanks guys, I can do simplified mucltiplications and divisions.

What I'm really after is some input from math guys if there is a simple way to do the calc to find the peak sine value based on the 2 points at 90 degrees apart on the sine wave.

It's not a big rush, i've been checking out the possibility of using a lookup table which can do the job but is not the method I would prefer. Maybe someone has some type of simple transfer function etc to do the job?
 

MrChips

Joined Oct 2, 2009
30,806
Why not go the square root of the sum of the squares route?
I am sure we can come up with an efficient way to calculate the square root.
I once did it for logarithm.
 

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
Because I'd like to do it in about 5 to 10 assembler cycles... ;)

Something like the old 2D graphics trick for fast hypotenuse; h = a + (b/2) where you add half the smaller number to the larger number, instead of; 2 squares an additon and a square root.

I've checked my other code and can work with either a peak result or a RMS result, so I can work with;
0 + 100 -> 100 (result is the sine peak)
71 + 71 -> 100
or alternatively
0 + 100 -> 71 (result is the RMS)
71 + 71 -> 71

What I have so far (for example) is;
big + (small * 0.375)

Rich (BB code):
Small	Big	PEAK	Mine
0	100	100	100
36	90	100	103
71	71	100	98
That gets me within about 3-4% of accurate, and is not too bad as *0.375 is *3/8, which is easy to do in binary as *3 then >> 3.

But I'd like it more accurate if possible. :)
 

Ron H

Joined Apr 14, 2005
7,063
I don't get it. Do you know the phase of the two samples? If not, how can you calculate the peak? Samples at 45° and 135° will be at the same level.
 

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
Hi Ron, sorry I probably didn't explain well enough. The data corresponds to a sine. The two samples are always exactly 90 degrees apart in relation to the (known) sine frequency.

I needs a very fast simpel way to get the peak value from the height of the two samples (which are already "full wave rectified" so heights are all positive).
 

Ron H

Joined Apr 14, 2005
7,063
Hi Ron, sorry I probably didn't explain well enough. The data corresponds to a sine. The two samples are always exactly 90 degrees apart in relation to the (known) sine frequency.

I needs a very fast simpel way to get the peak value from the height of the two samples (which are already "full wave rectified" so heights are all positive).
But what are the phases of the samples, relative to the sine?
 

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
But what are the phases of the samples, relative to the sine?
That is unknown but irrelevant, there is always 90 degrees between the two samples. It's irrelevant because the full wave rectification gives two identical 180 degree half sines, and then each of those is two identical quadrants (mirrored). So only two points at 90 degrees apart are needed to calc the sine peak.

To MrChips; thanks for the CORDIC reference, it was a good read but even though using relatively simple code it's similar to successive approximation which can require numerous cycles, so it's very slow compared to my present solution which is only one iteration of a few assember instructions.

MrChips said:
...
If you can do a multiply to find the square, try

if x > y

y1 = (y * y) / 256

y2 = y1 / 2

r = x + y1 + y2
Interesting, and thank you. I'll have a play with that and see how it goes for time and accuracy. :)
 

Ron H

Joined Apr 14, 2005
7,063
That is unknown but irrelevant, there is always 90 degrees between the two samples. It's irrelevant because the full wave rectification gives two identical 180 degree half sines, and then each of those is two identical quadrants (mirrored). So only two points at 90 degrees apart are needed to calc the sine peak.
Yeah, I finally figured that out on my own. Sometimes I'm a little dense.:(
 

Tesla23

Joined May 10, 2009
542
What I have so far (for example) is;
big + (small * 0.375)

Rich (BB code):
Small    Big    PEAK    Mine
0    100    100    100
36    90    100    103
71    71    100    98
That gets me within about 3-4% of accurate, and is not too bad as *0.375 is *3/8, which is easy to do in binary as *3 then >> 3.

But I'd like it more accurate if possible. :)
The approach you are trying is best shown graphically:


where you are doing a linear approximation, breaking at x=y.

A slightly CORDIC inspired approximation is:
0 <= y < x/2 : amp = x + 0.236*y
x/2 < y < x : amp = 1.414*x + 1.019*y - 0.7208

This gives an error between 0 and +2.7% (it's always positive so there is further tweaking you can do). You may get something reasonable by playing around with binary inspired approximations to this.
 

Attachments

MrChips

Joined Oct 2, 2009
30,806
I have fixed the algorithm but now it requires one multiply and one divide and some shifts:

if x > y

y1 = y * y / x

y2 = y1 / 2

y3 = y2 / 8

r = x + y2 - y3
 
Last edited:

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
...
A slightly CORDIC inspired approximation is:
0 <= y < x/2 : amp = x + 0.236*y
x/2 < y < x : amp = 1.414*x + 1.019*y - 0.7208
...
Thanks Tesla23 that has given me something to think about. At first glance I'm not favoring splitting the calc into 1 of 2 processes, it already has the time cost of the first split if(x>y) and then the second split and dual multiplications is going to make it slow I think...

Thanks MrChips. :) The new algo is not as bad as it looks time wise;

if x > y
y1 = y * y / x // this is still a bit unpleasant
y2 = y1 / 2 // fast, can be >>1
y3 = y2 / 8 // fast, can be another >>3
r = x + y2 - y3 // fast, can be done on the fly

I have not had time to check it out just yet but should get some time over the next few days. :)
 

Tesla23

Joined May 10, 2009
542
A slight refinement on Mr Chips algorithm:

for y < x

\(amp = (1 - 2^{-5} - 2^{-6})x + (2^{-1} - 2^{-4})y\)

or

\(amp = 0.953*x + 0.4375*y\)

gives max error < 5%
 
Top