<?xml version="1.0" encoding="UTF-8"?>

<UserFunctionMgr>
   <UserFunction
       Expression="(*&#10;&#9;X:=CRV_INTERP_X(CRV_ID,Y,E_CODE);&#13;&#10;&#9;Returns X for a given y.&#13;&#10;&#9;CRV_INTERP_X() assumes the values are always increasing or always decressing.&#13;&#10;&#9;There should be no &apos;flat spots&apos; or 0 slopes in the curve&#13;&#10;&#9;The function does 3 bi-section iterations to get into the ball park and then the&#13;&#10;&#9;search is &apos;polished&apos; using 3 iterations of Newton-Raphson.&#13;&#10;&#9;The returned error code has the following values&#13;&#10;&#9;0 OK&#13;&#10;&#9;-1 y is too low for the curve&#13;&#10;&#9;-2 y is too high for the curve&#13;&#10;&#9;-3 Divide by 0 error. &#32;The curve as a zero slope&#13;&#10;&#9;-4 x is less than the first x&#13;&#10;&#9;-5 x is greater than the last x&#13;&#10;*)&#10;&#10;FUNCTION CRV_INTERP_X : REAL&#10;VAR_INPUT&#13;&#10;&#9;CRV_ID: DINT;&#13;&#10;&#9;Y: REAL;&#13;&#10;END_VAR&#13;&#10;VAR&#13;&#10;&#9;X_MIN:REAL;&#13;&#10;&#9;X_MID:REAL;&#13;&#10;&#9;X_MAX:REAL;&#13;&#10;&#9;Y_DOT:REAL;&#13;&#10;&#9;TEMP:REAL;&#13;&#10;END_VAR&#13;&#10;VAR_OUTPUT&#13;&#10;&#9;E_CODE: DINT;&#13;&#10;END_VAR&#13;&#10;&#13;&#10;&#9;X_MIN := CRV_FIRST_X(CRV_ID);&#13;&#10;&#9;X_MAX:= CRV_LAST_X(CRV_ID);&#13;&#10;&#13;&#10;&#9;IF CRV_INTERP_Y(CRV_ID, X_MIN) &lt; CRV_INTERP_Y(CRV_ID, X_MAX) THEN&#9;&#13;&#10;&#9;&#9;IF &#32;Y &lt; CRV_INTERP_Y(CRV_ID, X_MIN) THEN&#9;&#9;&#9;&#9;// The curve is increasing&#13;&#10;&#9;&#9;&#9;E_CODE:=-1;&#13;&#10;&#9;&#9;&#9;CRV_INTERP_X := X_MIN;&#13;&#10;&#9;&#9;END_IF&#13;&#10;&#9;&#9;IF &#32;CRV_INTERP_Y(CRV_ID, X_MAX)&lt;Y &#32;THEN&#13;&#10;&#9;&#9;&#9;E_CODE:=-2;&#13;&#10;&#9;&#9;&#9;CRV_INTERP_X := X_MAX;&#13;&#10;&#9;&#9;END_IF&#13;&#10;&#9;&#9;X_MID:=(X_MIN+X_MAX)/2.0;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;// Do bi-section search&#13;&#10;&#9;&#9;IF CRV_INTERP_Y(CRV_ID, X_MID)&gt;Y THEN&#13;&#10;&#9;&#9;&#9;X_MAX:=X_MID;&#13;&#10;&#9;&#9;ELSE&#13;&#10;&#9;&#9;&#9;X_MIN:=X_MID;&#13;&#10;&#9;&#9;END_IF&#13;&#10;&#9;&#9;X_MID:=(X_MIN+X_MAX)/2.0;&#13;&#10;&#9;&#9;IF CRV_INTERP_Y(CRV_ID, X_MID)&gt;Y THEN&#13;&#10;&#9;&#9;&#9;X_MAX:=X_MID;&#13;&#10;&#9;&#9;ELSE&#13;&#10;&#9;&#9;&#9;X_MIN:=X_MID;&#13;&#10;&#9;&#9;END_IF&#13;&#10;&#9;&#9;X_MID:=(X_MIN+X_MAX)/2.0;&#13;&#10;&#9;&#9;IF CRV_INTERP_Y(CRV_ID, X_MID)&gt;Y THEN&#13;&#10;&#9;&#9;&#9;X_MAX:=X_MID;&#13;&#10;&#9;&#9;ELSE&#13;&#10;&#9;&#9;&#9;X_MIN:=X_MID;&#13;&#10;&#9;&#9;END_IF&#13;&#10;&#9;ELSE&#13;&#10;&#9;&#9;IF &#32;Y &lt; CRV_INTERP_Y(CRV_ID, X_MAX) THEN&#9;&#9;&#9;&#9;// The curve is decreasing&#13;&#10;&#9;&#9;&#9;E_CODE:=-1;&#13;&#10;&#9;&#9;&#9;CRV_INTERP_X := X_MIN;&#13;&#10;&#9;&#9;END_IF&#13;&#10;&#9;&#9;IF &#32;CRV_INTERP_Y(CRV_ID, X_MIN)&lt;Y &#32;THEN&#13;&#10;&#9;&#9;&#9;E_CODE:=-2;&#13;&#10;&#9;&#9;&#9;CRV_INTERP_X := X_MAX;&#13;&#10;&#9;&#9;END_IF&#13;&#10;&#9;&#9;X_MID:=(X_MIN+X_MAX)/2.0;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;// Do bi-section search&#13;&#10;&#9;&#9;IF CRV_INTERP_Y(CRV_ID, X_MID)&lt;Y THEN&#13;&#10;&#9;&#9;&#9;X_MAX:=X_MID;&#13;&#10;&#9;&#9;ELSE&#13;&#10;&#9;&#9;&#9;X_MIN:=X_MID;&#13;&#10;&#9;&#9;END_IF&#13;&#10;&#9;&#9;X_MID:=(X_MIN+X_MAX)/2.0;&#13;&#10;&#9;&#9;IF CRV_INTERP_Y(CRV_ID, X_MID)&lt;Y THEN&#13;&#10;&#9;&#9;&#9;X_MAX:=X_MID;&#13;&#10;&#9;&#9;ELSE&#13;&#10;&#9;&#9;&#9;X_MIN:=X_MID;&#13;&#10;&#9;&#9;END_IF&#13;&#10;&#9;&#9;X_MID:=(X_MIN+X_MAX)/2.0;&#13;&#10;&#9;&#9;IF CRV_INTERP_Y(CRV_ID, X_MID)&lt;Y THEN&#13;&#10;&#9;&#9;&#9;X_MAX:=X_MID;&#13;&#10;&#9;&#9;ELSE&#13;&#10;&#9;&#9;&#9;X_MIN:=X_MID;&#13;&#10;&#9;&#9;END_IF&#13;&#10;&#9;END_IF&#13;&#10;&#13;&#10;&#9;X_MID:=(X_MIN+X_MAX)/2.0;&#13;&#10;&#13;&#10;&#9;// Do 3 Newton-Raphson iterations&#13;&#10;&#9;Y_DOT:=CRV_INTERP_V(CRV_ID, X_MID);&#9;&#9;&#9;&#9;&#9;&#9;// f&apos;(x)&#13;&#10;&#9;IF Y_DOT=0.0 THEN&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;// Avoid divide by 0 error&#13;&#10;&#9;&#9;E_CODE:=-3;&#13;&#10;&#9;&#9;CRV_INTERP_X := X_MID;&#13;&#10;&#9;END_IF&#13;&#10;&#9;X_MID := &#32;X_MID - (CRV_INTERP_Y(CRV_ID, X_MID) - Y)/Y_DOT;&#9;// x = x - f(x)/f&apos;(x)&#13;&#10;&#9;IF &#32;X_MID &lt; X_MIN THEN&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;// Make sure x has go into the weeds&#13;&#10;&#9;&#9;E_CODE:=-4;&#13;&#10;&#9;&#9;CRV_INTERP_X := X_MIN;&#13;&#10;&#9;END_IF&#13;&#10;&#9;IF &#32;X_MAX&lt;X_MID &#32;THEN&#13;&#10;&#9;&#9;E_CODE:=-5;&#13;&#10;&#9;&#9;CRV_INTERP_X := X_MAX;&#13;&#10;&#9;END_IF&#13;&#10;&#13;&#10;&#9;Y_DOT:=CRV_INTERP_V(CRV_ID, X_MID);&#9;&#9;&#9;&#9;&#9;&#9;// f&apos;(x)&#13;&#10;&#9;IF Y_DOT=0.0 THEN&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;// Avoid divide by 0 error&#13;&#10;&#9;&#9;E_CODE:=-3;&#13;&#10;&#9;&#9;CRV_INTERP_X := X_MID;&#13;&#10;&#9;END_IF&#13;&#10;&#9;X_MID := &#32;X_MID - (CRV_INTERP_Y(CRV_ID, X_MID) - Y)/Y_DOT;&#9;// x = x - f(x)/f&apos;(x)&#13;&#10;&#9;IF &#32;X_MID &lt; X_MIN THEN&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;// Make sure x has go into the weeds&#13;&#10;&#9;&#9;E_CODE:=-4;&#13;&#10;&#9;&#9;CRV_INTERP_X := X_MIN;&#13;&#10;&#9;END_IF&#13;&#10;&#9;IF &#32;X_MAX&lt;X_MID &#32;THEN&#13;&#10;&#9;&#9;E_CODE:=-5;&#13;&#10;&#9;&#9;CRV_INTERP_X := X_MAX;&#13;&#10;&#9;END_IF&#13;&#10;&#13;&#10;&#9;Y_DOT:=CRV_INTERP_V(CRV_ID, X_MID);&#9;&#9;&#9;&#9;&#9;&#9;// f&apos;(x)&#13;&#10;&#9;IF Y_DOT=0.0 THEN&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;// Avoid divide by 0 error&#13;&#10;&#9;&#9;E_CODE:=-3;&#13;&#10;&#9;&#9;CRV_INTERP_X := X_MID;&#13;&#10;&#9;END_IF&#13;&#10;&#9;X_MID := &#32;X_MID - (CRV_INTERP_Y(CRV_ID, X_MID) - Y)/Y_DOT;&#9;// x = x - f(x)/f&apos;(x)&#13;&#10;&#9;IF &#32;X_MID &lt; X_MIN THEN&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;// Make sure x has go into the weeds&#13;&#10;&#9;&#9;E_CODE:=-4;&#13;&#10;&#9;&#9;CRV_INTERP_X := X_MIN;&#13;&#10;&#9;END_IF&#13;&#10;&#9;IF &#32;X_MAX&lt;X_MID &#32;THEN&#13;&#10;&#9;&#9;E_CODE:=-5;&#13;&#10;&#9;&#9;CRV_INTERP_X := X_MAX;&#13;&#10;&#9;END_IF&#13;&#10;&#13;&#10;&#9;E_CODE:=0;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;// No Errors!&#13;&#10;&#9;CRV_INTERP_X := X_MID;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;// All right! &#32;We got this far so we must have an answer!&#10;&#10;END_FUNCTION&#13;&#10;&#13;&#10;&#13;&#10;(*&#9;ALTERNATIVE CODE THAT WORKS&#13;&#10;&#9;The curve doesn&apos;t need to be monotomically increasing.&#13;&#10;&#13;&#10;&#9;Y_MIN:REAL;&#13;&#10;&#9;Y_MID:REAL;&#13;&#10;&#9;Y_MAX:REAL;&#13;&#10;&#13;&#10;&#9;Y_MIN:=CRV_INTERP_Y(CRV_ID,X_MIN);&#13;&#10;&#9;Y_MAX:=CRV_INTERP_Y(CRV_ID,X_MAX);&#13;&#10;&#9;Y_MID:=CRV_INTERP_Y(CRV_ID,X_MID);&#13;&#10;&#13;&#10;&#9;IF &#32;Y &lt; Y_MIN AND Y &lt; Y_MAX THEN&#13;&#10;&#9;&#9;E_CODE:=-1;&#9;&#9;&#9;&#9;&#9;&#9;// Y too low&#13;&#10;&#9;&#9;CRV_INTERP_X := X_MIN;&#13;&#10;&#9;END_IF&#13;&#10;&#9;IF &#32;Y_MIN &lt; Y AND Y_MAX&lt;Y &#32;&#32;THEN&#13;&#10;&#9;&#9;E_CODE:=-2;&#9;&#9;&#9;&#9;&#9;&#9;// Y too high&#13;&#10;&#9;&#9;CRV_INTERP_X := X_MAX;&#13;&#10;&#9;END_IF&#13;&#10;&#13;&#10;&#9;IF &#9;Y_MIN &lt;= Y AND Y &lt; Y_MID OR &#13;&#10;&#9;&#9;Y_MIN &gt;= Y AND Y &gt; Y_MID THEN&#13;&#10;&#9;&#9;X_MAX:=X_MID;&#13;&#10;&#9;&#9;Y_MAX:=Y_MID;&#13;&#10;&#9;ELSE&#13;&#10;&#9;&#9;X_MIN:=X_MID;&#13;&#10;&#9;&#9;Y_MIN:=Y_MID;&#13;&#10;&#9;END_IF&#13;&#10;&#9;X_MID:=(X_MIN+X_MAX)/2.0;&#13;&#10;&#9;Y_MID:=CRV_INTERP_Y(CRV_ID,X_MID);&#13;&#10;&#13;&#10;&#9;IF &#9;Y_MIN &lt;= Y AND Y &lt; Y_MID OR &#13;&#10;&#9;&#9;Y_MIN &gt;= Y AND Y &gt; Y_MID THEN&#13;&#10;&#9;&#9;X_MAX:=X_MID;&#13;&#10;&#9;&#9;Y_MAX:=Y_MID;&#13;&#10;&#9;ELSE&#13;&#10;&#9;&#9;X_MIN:=X_MID;&#13;&#10;&#9;&#9;Y_MIN:=Y_MID;&#13;&#10;&#9;END_IF&#13;&#10;&#9;X_MID:=(X_MIN+X_MAX)/2.0;&#13;&#10;&#9;Y_MID:=CRV_INTERP_Y(CRV_ID,X_MID);&#13;&#10;&#13;&#10;&#9;IF &#9;Y_MIN &lt;= Y AND Y &lt; Y_MID OR &#13;&#10;&#9;&#9;Y_MIN &gt;= Y AND Y &gt; Y_MID THEN&#13;&#10;&#9;&#9;X_MAX:=X_MID;&#13;&#10;&#9;&#9;Y_MAX:=Y_MID;&#13;&#10;&#9;ELSE&#13;&#10;&#9;&#9;X_MIN:=X_MID;&#13;&#10;&#9;&#9;Y_MIN:=Y_MID;&#13;&#10;&#9;END_IF&#13;&#10;*)&#13;&#10;"/>
</UserFunctionMgr>
