/* * DC ERROR SIGNAL - compute DARM error signal from DC readout signal * see T0900023 * * This code is intended to be used in the LIGO Bork-space environment, * as an arbitrary C code block. As such its interface to the world is * through an input array and an output array. The expected data * order in these arrays are: * * datIn[0] = Pas = DC readout signal * datIn[1] = Parm = average arm power signal (forced greater than 1.0) * datIn[2] = Pref = reference arm power * datIn[3] = Pdfct = contrast defect offset (a.k.a. kappa) * datIn[4] = x0 = fringe offset (in pm) * datIn[5] = xf = offset cofficient * datIn[6] = C2 = non-linear coefficient (for dx^2) * datIn[7] = Tramp = ramp time (in seconds) * datIn[8] = load = load bit (start ramp when this changes from 0 to 1) * datIn[9] = TP1 = test-point selector * * datOut[0] = output signal (dx + C2 * dx^2) * datOut[1] = state flag (0 = ok, 1 = need load, 2 = ramping, 3 = 1 & 2) * datOut[2] = DC to DARM slope * datOut[3] = DC to DARM offset * datOut[4] = test-point output * * Test-Point Selection Table: * 0-5: post-ramp values for Pref, Pdfct, x0, xf, C2 * 8,9: Pas, Parm * 10: Pas * Pref / Parm - Pdfct * 11: xf^2 * Pas * Pref / Parm - Pdfct * 12: xf^2 * Pas * Pref / Parm - Pdfct - x0^2 * * TP 10-12 are defined to assist in calibration. Recal that * x = x0 + dx * then * TP[10] = (x / xf)^2 * TP[11] = x^2 * TP[12] = dx * (2 * x0 + dx) * * In particular, TP[12] should be small and mean-zero when locked * on RF if the DCERR parameters match the RF offset (e.g., x0 and * xf are set properly). */ #define OMC_DCERR_FS FE_RATE /* sample rate */ //#define OMC_DCERR_FS 2048 /* sample rate */ #define OMC_DCERR_N_SIG 2 /* input signals (before ramping parameters) */ #define OMC_DCERR_N_RAMP 5 /* ramping parameters (before non-ramping) */ //#define abs fabs /* whatever the FE likes for absolute-value */ static inline double fabs(double x) { return (x>0 ? x : -x); } #define OMC_DCERR_OMC_DCERR OMC_DCERR enum OMC_DCERR_Params { OMC_DCERR_P_REF = 0, OMC_DCERR_P_DFCT = 1, OMC_DCERR_X_0 = 2, OMC_DCERR_X_F = 3, OMC_DCERR_C2 = 4, OMC_DCERR_T_RAMP = 5, OMC_DCERR_LOAD = 6, OMC_DCERR_TP1 = 7 }; /* * Structs */ typedef struct OMC_DCERR_ParamState { // running variables (updated each cycle while ramping) int isRamping; // state double val; // current value double dxPrev; // previous change // ramp parameters (updated on load) double req; // requested value double dxMax; // maximum change in x in one step double ddxMax; // maximum change in dx in one step // ramp construction parameters (user set, or fixed) double softRatio; // ratio of time at constant velocity to accelleration double softVel; // maximum velocity relative to linear ramp double minAccCycles; // accelleration cycles (when ramping is interrupted) } OMC_DCERR_ParamState; /* * Ramping Functions * (static so they are only visible in to OMC_DCERR) */ static void ParamInit(OMC_DCERR_ParamState* state, double xInit) { state->isRamping = 0; state->val = xInit; state->dxPrev = 0.0; state->req = xInit; state->dxMax = 0.0; state->ddxMax = 0.0; state->softRatio = 4.0; state->softVel = 1.36; // how to compute this from softRatio? state->minAccCycles = 0.05 * OMC_DCERR_FS; } static void ParamLoad(OMC_DCERR_ParamState* state, double req, double tRamp) { double inv_nRamp; // 1 / number of ramp cycles if( tRamp <= 0.0 ) { // no ramp state->isRamping = 0; state->val = req; state->dxPrev = 0.0; state->req = req; state->dxMax = 0.0; state->ddxMax = 0.0; } else if( state->isRamping || req != state->val ) { // setup ramp parameters inv_nRamp = 1.0 / (OMC_DCERR_FS * tRamp); state->req = req; state->dxMax = state->softVel * abs(req - state->val) * inv_nRamp; state->ddxMax = state->softRatio * state->dxMax * inv_nRamp; // if currently ramping, allow high accelleration if( state->isRamping ) state->ddxMax = abs(state->dxMax + state->dxPrev) / state->minAccCycles; // start the ramp state->isRamping = 1; } } // call only if state->isRamping // return is new parameter value static double ParamUpdate(OMC_DCERR_ParamState* state) { double dxReq; // distance to requested value double dxNow; // current step size double dxNowAbs; // abs(dxNow) double ddxNow; // current change in step size double nStop; // steps required to stop (may be non-integer) double dxStop; // shortest distance required to stop double dxLand; // current step size to achieve "soft landing" // if not ramping, just return if( !state->isRamping ) return state->val; // requested change dxReq = state->req - state->val; // apply slew limit if( dxReq > state->dxMax ) dxNow = state->dxMax; else if( dxReq < -state->dxMax ) dxNow = -state->dxMax; else dxNow = dxReq; // apply accelleration limit ddxNow = dxNow - state->dxPrev; if( ddxNow > state->ddxMax ) dxNow = state->dxPrev + state->ddxMax; else if( ddxNow < -state->ddxMax ) dxNow = state->dxPrev - state->ddxMax; // enforce "soft landing" dxNowAbs = abs(dxNow); if( dxNowAbs > state->ddxMax && dxNow * dxReq > 0.0 ) { // number of decellerations required to stop nStop = dxNowAbs / state->ddxMax; // distance traveled before stopping // assuming max decelleration in all subsequent steps dxStop = dxNow * (1.5 + 0.5 * nStop); // dxNow required to make stopping distance = dxReq // (approximation for dxReq ~ dxStop) dxLand = dxNow + (dxReq - dxStop) / (1.5 + nStop); // if dxLand is slower than dxNow, use it if( abs(dxLand) < dxNowAbs ) dxNow = dxLand; } // update state state->isRamping = !(dxNow == 0.0 && state->dxPrev == 0.0); if( dxNow == dxReq ) state->val = state->req; else state->val += dxNow; state->dxPrev = dxNow; return state->val; } /* * FE Function */ void OMC_DCERR(double* datIn, int nIn, double* datOut, int nOut) { static int isFirst = 1; static OMC_DCERR_ParamState paramState[OMC_DCERR_N_RAMP]; static double prevLoad = 0.0; double* param = datIn + OMC_DCERR_N_SIG; // short-cut int i; // counter int flag = 0; // state flag double xf2; // OMC_DCERR_X_F^2 double x0; // OMC_DCERR_X_0 double pGain; // linear gain for Pas, not including contrast defect double dx; // the DC error signal // ================ Initialize if( isFirst ) { isFirst = 0; for( i = 0; i < OMC_DCERR_N_RAMP; i++ ) ParamInit(paramState + i, param[i]); } // ================ Load New Parameters if( param[OMC_DCERR_LOAD] != 0.0 && prevLoad == 0.0 ) for( i = 0; i < OMC_DCERR_N_RAMP; i++ ) ParamLoad(paramState + i, param[i], param[OMC_DCERR_T_RAMP]); prevLoad = param[OMC_DCERR_LOAD]; // ================ Update Ramping Parameters for( i = 0; i < OMC_DCERR_N_RAMP; i++ ) { if( paramState[i].isRamping ) { // continue ramp ParamUpdate(paramState + i); flag |= 2; } // check for unloaded parameters flag |= (param[i] != paramState[i].req); } // ================ Compute Outputs // x0 and xf^2 x0 = paramState[OMC_DCERR_X_0].val; xf2 = paramState[OMC_DCERR_X_F].val; xf2 *= xf2; // normalized Pas, less contrast defect pGain = paramState[OMC_DCERR_P_REF].val; if( datIn[1] > 1.0 ) pGain /= datIn[1]; pGain -= paramState[OMC_DCERR_P_DFCT].val; // error signal if( x0 == 0.0 ) { //dx = 0.0; pGain = 0.0; } else { // dx = 0.5 * (pNorm * xf2 / x0 - x0); // Modified pGain = 0.5 * xf2 / x0 * pGain; } dx = pGain * datIn[0] - 0.5 * x0; // non-linear correction dx += paramState[OMC_DCERR_C2].val * dx * dx; // ================ Assign Outputs // DC error signal if( nOut > 0 ) datOut[0] = dx; // state flag if( nOut > 1 ) datOut[1] = flag; // pGain if( nOut > 2) datOut[2] = pGain; // offset if( nOut > 3) datOut[3] = 0.5 * x0; // test-point 1 if( nOut > 4 ) { i = (int)(param[OMC_DCERR_TP1] + 0.5); switch( i ) { // ramping parameters case OMC_DCERR_P_REF: case OMC_DCERR_P_DFCT: case OMC_DCERR_X_0: case OMC_DCERR_X_F: case OMC_DCERR_C2: datOut[4] = paramState[i].val; break; // input signals case 8: datOut[4] = datIn[0]; break; case 9: datOut[2] = datIn[1]; break; // normalized Pas case 10: datOut[4] = pGain *datIn[0]; break; // x^2 = (x0 + dx)^2 case 11: datOut[4] = pGain * datIn[0] * xf2; break; // x^2 - x0^2 = dx * (2 * x0 + dx) // when locked on RF with an offset // this should be zero (since dx ~ 0) case 12: datOut[4] = pGain * datIn[0] * xf2 - x0 * x0; break; // set output to invalid request value default: datOut[4] = i; } } } #undef abs