Well as other s mentioned already your data contains noise, and distortion in amplitude. However its more than likely your data also contains temporal distortion (angle parameter of sinwave is non linear nor in exact/constant phase shift) That would make use of atan,acos,asin very inaccurate and problematic.
I would:
FIR filter the data
so use sliding average centered around value so the output will not phase shift. This is O(n) and one only once. Do not forget to use temp array...
detect monotonic intervals
simply detect the intervals where your signals are only decreasing or only increasing... This is also O(n) and done one only once.
use binary search
simply go through all intervals and if your measured voltages are inside their range (first and last value of the interval) use binary search on the interval. The selection of interval is O(1) as number of intervals is always 4 or 5 depending on the starting phases.
binary search itself is O(log(n))
Here a small C++/VCL example:
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
//---------------------------------------------------------------------------
// LUT data
const int n=1317;
double atan2_u0[n]; // u0 [V] red
double atan2_u1[n]; // u1 [V] blue
// ix = ang*183/50 // index -> [deg]
// ang = ix*50/183 // [deg] -> index
// monotonic intervals
const int m=10; // max interval count
int atan2_n=0; // intervals count
int atan2_ix[m]; // interval start index
int atan2_s0[m]; // direction of atan2_u0
int atan2_s1[m]; // direction of atan2_u1
//---------------------------------------------------------------------------
double LUT_atan2(double _u0,double _u1)
{
int i,i0,i1,m;
double u,u0,u1,U0,U1,s0,s1;
// process interals
for (i=0;i<atan2_n;i++)
{
i0=atan2_ix[i];
i1=atan2_ix[i+1]-1;
// test BBOX
u0=atan2_u0[i0]; U0=atan2_u0[i1]; s0=atan2_s0[i];
u1=atan2_u1[i0]; U1=atan2_u1[i1]; s1=atan2_s1[i];
if (s0<0.0){ u=u0; u0=U0; U0=u; }
if (s1<0.0){ u=u1; u1=U1; U1=u; }
if ((_u0<u0)||(_u0>U0)) continue;
if ((_u1<u1)||(_u1>U1)) continue;
// binary search
for (m=1;m<=(i1-i0);m<<=1); m>>=1; if (!m) m=1; // m = exp2(log2(i1-i0))
for (;m;m>>=1)
{
i0+=m;
if (i0>i1){ i0-=m; continue; }
u0=atan2_u0[i0];
u1=atan2_u1[i0];
if (((_u0-u0)*s0<0.0)||((_u1-u1)*s1<0.0)) i0-=m;
}
return double(i0)*50.0/183.0;
}
return 0.0;
}
//---------------------------------------------------------------------------
void LUT_atan2_init() // filter u0,u1 and compute intervals
{
int i,j,k,r;
double u0,u1,du0,du1,U0,U1,tmp0[n],tmp1[n];
// centered sliding avg, window is <-r,+r>
r=25; du0=1.0/double(r+r+1);
for (i=0;i<n;i++) // all samples
{
u0=0.0; u1=0.0;
for (j=i-r;j<=i+r;j++)
{
k=j;
if (k< 0) k+=n;
if (k>=n) k-=n;
u0+=atan2_u0[k];
u1+=atan2_u1[k];
}
tmp0[i]=u0*du0;
tmp1[i]=u1*du0;
}
for (i=0;i<n;i++)
{
atan2_u0[i]=tmp0[i];
atan2_u1[i]=tmp1[i];
}
// find monotonic intervals
atan2_n=0; atan2_ix[0]=0;
u0=atan2_u0[0]; U0=atan2_u0[1]; atan2_s0[0]=0.0;
u1=atan2_u1[0]; U1=atan2_u1[1]; atan2_s1[0]=0.0;
for (i=2;i<n;i++)
{
// actual delta du0,du1
u0=U0; U0=atan2_u0[i]; du0=U0-u0; if (du0<0.0) du0=-1.0; else if (du0>0.0) du0=+1.0;
u1=U1; U1=atan2_u1[i]; du1=U1-u1; if (du1<0.0) du1=-1.0; else if (du1>0.0) du1=+1.0;
if (fabs(atan2_s0[atan2_n])<1e-3) atan2_s0[atan2_n]=du0;
if (fabs(atan2_s1[atan2_n])<1e-3) atan2_s1[atan2_n]=du1;
// if sign changed add new interval
if ((du0*atan2_s0[atan2_n]<-0.1)||(du1*atan2_s1[atan2_n]<-0.1))
{
if (atan2_n>=m-1) break;
atan2_n++;
atan2_ix[atan2_n]=i;
atan2_s0[atan2_n]=du0;
atan2_s1[atan2_n]=du1;
}
}
// add end of table as end of intervals
atan2_n++;
atan2_ix[atan2_n]=n;
atan2_s0[atan2_n]=0.0;
atan2_s1[atan2_n]=0.0;
}
//---------------------------------------------------------------------------
void data_generate() // extract mesurede values from image
{
int x,y,x0=44,y0=478,dx=227-x0,dy=y0-424,r,g,b,i;
double u0,u1,du=1.0/double(dy);
BYTE *c; DWORD *q;
// convert bmp -> pnt[]
Graphics::TBitmap *bmp=new Graphics::TBitmap;
bmp->LoadFromFile("in.bmp");
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
for (x=x0;x<bmp->Width;x++)
{
u0=u1=-1.0;
for (y=0;y<=y0;y++)
{
q=(DWORD*)bmp->ScanLine[y];
c=(BYTE*)(q+x);
b=c[0]; g=c[1]; r=c[2]; i=r+g+b;
if ((r>200)&&(i<500)){ u0=y0-y; u0*=du; break; } // [V]
}
for (y=0;y<=y0;y++)
{
q=(DWORD*)bmp->ScanLine[y];
c=(BYTE*)(q+x);
b=c[0]; g=c[1]; r=c[2]; i=r+g+b;
if ((b>200)&&(i<500)){ u1=y0-y; u1*=du; break; } // [V]
}
if (u0<-0.5) u0=u1;
if (u1<-0.5) u1=u0;
i=x-x0;
atan2_u0[i]=u0+1.0;
atan2_u1[i]=u1+1.0;
}
delete bmp;
}
//---------------------------------------------------------------------------
void TMain::draw()
{
if (!_redraw) return;
// clear buffer
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
int i;
double x,y,dy=ys/10.0,r=5.0;
// LUT
bmp->Canvas->Pen->Color=clRed;
for (i=0;i<n;i++)
{
x=(i*xs)/n;
y=ys-(atan2_u0[i]*dy);
if (!i) bmp->Canvas->MoveTo(x,y);
else bmp->Canvas->LineTo(x,y);
}
bmp->Canvas->Pen->Color=clBlue;
for (i=0;i<n;i++)
{
x=(i*xs)/n;
y=ys-(atan2_u1[i]*dy);
if (!i) bmp->Canvas->MoveTo(x,y);
else bmp->Canvas->LineTo(x,y);
}
// intervals
bmp->Canvas->Pen->Color=clDkGray;
for (i=0;i<=atan2_n;i++)
{
x=(atan2_ix[i]*xs)/n;
bmp->Canvas->MoveTo(x,0);
bmp->Canvas->LineTo(x,ys);
}
// atan2
double u0=8.0,u1=1.7,a;
a=LUT_atan2(u0,u1); // [V,V] -> [deg]
x=(183.0*a*xs)/(50.0*n); // [deg] -> [pixel]
bmp->Canvas->Pen->Color=clWhite;
bmp->Canvas->MoveTo(x,0);
bmp->Canvas->LineTo(x,ys);
y=ys-(u0*dy);
bmp->Canvas->Pen->Color=clWhite;
bmp->Canvas->Brush->Color=clRed;
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
y=ys-(u1*dy);
bmp->Canvas->Pen->Color=clWhite;
bmp->Canvas->Brush->Color=clBlue;
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
// render backbuffer
Main->Canvas->Draw(0,0,bmp);
_redraw=false;
}
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
{
data_generate();
LUT_atan2_init();
bmp=new Graphics::TBitmap;
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
pyx=NULL;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
{
if (pyx) delete[] pyx;
delete bmp;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender)
{
xs=ClientWidth; xs2=xs>>1;
ys=ClientHeight; ys2=ys>>1;
bmp->Width=xs;
bmp->Height=ys;
if (pyx) delete[] pyx;
pyx=new int*[ys];
for (int y=0;y<ys;y++) pyx[y]=(int*) bmp->ScanLine[y];
_redraw=true;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
{
_redraw=true;
}
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
{
draw();
}
//---------------------------------------------------------------------------
I extracted the input data from image of yours and converted to [V] and [deg]. You can ignore the VCL rendering and window stuff. The only important thing here are these functions:
void LUT_atan2_init();
double LUT_atan2(double _u0,double _u1);
the first precomputes all the stuff needed (call just once) and the second returns angle in [deg] for voltages u0,u1.
This might however have slight accuracy problems if your u0,u1 are near interval edge where one voltage is in one interval and the other in the next which might happen if noise is added. In such case you can do bianry search of u0 and u1 separately remebering the 2 locations for both and then chech the 4 combinations which one is the correct one. After that just use mid position between the found two indexes. For this you woul dhave the intervals separate too (but it would be just 2 or 3 per signal).
Also you can add "sub pixel" precision by adding linear interpolation after the binary search (I was too lazy for that so its not in my example)
Here preview:

Red,Blue curves are your u0,u1 signals, the gray lines are the found interval edges, dots are tested (measured) voltages and white is the found angle