<?xml version="1.0" encoding="utf-8"?>

<UserFunctionMgr>
   <UserFunction
       Expression="(*Returns the X value for for a given Y value in a curve.&#13;&#10;The curve values should be always increasing or always decreasing, with no &apos;flat spots&apos; or 0 slopes.&#13;&#10;The returned error code has the following values:&#13;&#10;0&#9;No error&#13;&#10;-1&#9;Y is too low for the curve&#13;&#10;-2&#9;Y is too high for the curve&#13;&#10;-3&#9;Divide by 0 error. &#32;The curve as a zero slope.&#13;&#10;-4&#9;X is less than the first x&#13;&#10;-5&#9;X is greater than the last x*)&#13;&#10;&#13;&#10;(*&#13;&#10;The function does 3 bi-section iterations to get into the ball park and then the&#13;&#10;search is &apos;polished&apos; using 3 iterations of Newton-Raphson&#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;ERR_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;ERR_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;ERR_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;ERR_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;ERR_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;ERR_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;ERR_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;ERR_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;ERR_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;ERR_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;ERR_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;ERR_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;ERR_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;ERR_CODE:=-5;&#13;&#10;&#9;&#9;CRV_INTERP_X := X_MAX;&#13;&#10;&#9;END_IF&#13;&#10;&#13;&#10;&#9;ERR_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>
