! EncZoomMeasBL.scr ! Measure backlash ! ----------------------------------------------------------------------------------------------- loadsub "EncZoomGlob.scr" loadsub "EncZoomSdoGlob.scr" ! ----------------------------------------------------------------------------------------------- gosub GET_QUIET_FLAG ! Sets var quietFlag% ! ----------------------------------------------------------------------------------------------- gosub CLR_STATUS_MSG_ALL gosub SET_SCRIPT_STATUS_FAIL ! ----------------------------------------------------------------------------------------------- if quietFlag% = 0 then msg$ = "" msg$ = msg$ & " Measure Backlash \n\n" msg$ = msg$ & " This will measure the backlash. \n\n" msg$ = msg$ & " Requires the following: \n" msg$ = msg$ & " Plano interference cavity \n" msg$ = msg$ & " Tilt between " & val$(CalMinTiltPv) & " and " & val$(CalMaxTiltPv) msg$ = msg$ & " fringes at lowest zoom mag \n\n" msg$ = msg$ & " The Backlash Comp control will be set. \n\n" msg$ = msg$ & " This will take about 15 to 25 minutes. \n\n" msg$ = msg$ & " Do you want to continue? " msgtype% = 6 gosub SHOW_DIALOG ! Uses vars msg$ and msgtype%, sets var ok% if ok% = 0 then stop endif endif ! ----------------------------------------------------------------------------------------------- msg$ = "Measure Backlash" gosub SET_STATUS_MSG_1 gosub LOG_DATE_TIME_MSG ! ----------------------------------------------------------------------------------------------- StartTime = timedate ! ----------------------------------------------------------------------------------------------- ! NumPos% is the number of target positions at which we will take measurements. NumPos% = 7 if NumPos% < 3 then msg$ = " NumPos% must be >= 3 " gosub SHOW_ERROR_DIALOG stop endif ! ----------------------------------------------------------------------------------------------- ! NumIter% is the number of iterations. ! One iteration consists of: ! Start below the lowest target position. ! Move upward to each target position and take a measurement. ! Move above the highest target position. ! Move downward to each target position and take a measurement. NumIter% = 10 ! ----------------------------------------------------------------------------------------------- ! MaxBacklash is the maximum magnitude of backlash we will try to measure MaxBacklash = 2000 ! ----------------------------------------------------------------------------------------------- SettlingTimeWait = 1.0 ! seconds ! ----------------------------------------------------------------------------------------------- dim OutputFnRaw$[999] dim OutputFnSum$[999] OutputFnRaw$ = DataFolder$ & "\\" & "MeasBacklashRaw.csv" OutputFnSum$ = DataFolder$ & "\\" & "MeasBacklashSum.csv" ! ----------------------------------------------------------------------------------------------- if WarnBeforeOverwritingFiles% = 1 then msg$ = " OK to overwrite these files? \n" msg$ = msg$ & OutputFnRaw$ & " \n" msg$ = msg$ & OutputFnSum$ & " " on error goto DIALOG_ESCAPED ok% = dialog(msg$, 6) off error if ok% = 0 then stop endif endif ! ----------------------------------------------------------------------------------------------- assign @OutputFpRaw to OutputFnRaw$ "w" assign @OutputFpSum to OutputFnSum$ "w" ! ----------------------------------------------------------------------------------------------- ! Save the old control value gosub GET_BACKLASH_COMP ! Gets control value into var BacklashComp OldBacklashComp = BacklashComp ! Reset Backlash Comp control to zero BacklashComp = 0 gosub SET_BACKLASH_COMP ! Sets control using var BacklashComp ! ----------------------------------------------------------------------------------------------- ! Disable firmware backlash compensation gosub SDO_DISABLE_BACKLASH_COMP ! ----------------------------------------------------------------------------------------------- ! Disable internal program move limits gosub CLEAR_PROG_LIMITS_CALINFO ! ----------------------------------------------------------------------------------------------- ! Disable firmware soft limits gosub SDO_DISABLE_SOFT_LIMITS ! ----------------------------------------------------------------------------------------------- ! Delete any masks, and if this is an MST app, make a cal marker gosub DELETE_MASKS_AND_MAKE_CAL_MARKER_FOR_MST ! ----------------------------------------------------------------------------------------------- ! Find lower hard limit msg$ = "Finding lower limit" gosub SET_STATUS_MSG_2 TargPos = 0 gosub SET_ZOOM_POS_AND_IGNORE_ERRORS ! Uses var TargPos gosub GET_CUR_POS_AVG ! Sets var CurPos LowerHardLimit = CurPos ! ----------------------------------------------------------------------------------------------- VALIDATE_CAVITY_SETUP: msg$ = "Measuring to validate cavity setup" gosub SET_STATUS_MSG_2 gosub MEASURE_WITH_RETRY gosub CLR_STATUS_MSG_2 gosub GET_DATA_PV_TR ! Gets result value into var DataPv (in fringes) if (DataPv < CalMinTiltPv) or (DataPv > CalMaxTiltPv) then msg$ = " Adjust tilt to obtain PV between " ofmtr("%.0f") msg$ = msg$ & val$(CalMinTiltPv) & " and " & val$(CalMaxTiltPv) msg$ = msg$ & " fringes at lowest zoom mag. \n" msg$ = msg$ & " Click YES to retry or NO to stop. \n" msgtype% = 5 gosub SHOW_DIALOG ! Uses vars msg$ and msgtype%, sets var ok% if ok% = 1 then goto VALIDATE_CAVITY_SETUP else stop endif endif ! ----------------------------------------------------------------------------------------------- ! Set circular acquisition mask to fit data runscript(0, "EncZoomSetMask.scr") gosub VERIFY_SCRIPT_STATUS_PASS gosub SET_SCRIPT_STATUS_FAIL ! ----------------------------------------------------------------------------------------------- ! Find upper limit msg$ = "Finding upper limit" gosub SET_STATUS_MSG_2 ! Move in two steps to avoid hitting limit switch too hard TargPos = 50000 gosub SET_ZOOM_POS_AND_IGNORE_ERRORS ! Uses var TargPos TargPos = 60000 gosub SET_ZOOM_POS_AND_IGNORE_ERRORS ! Uses var TargPos gosub GET_CUR_POS_AVG ! Sets var CurPos UpperHardLimit = CurPos ! ----------------------------------------------------------------------------------------------- ! Setup target position array ! PosStep is distance between target positions dim TargPosAry(NumPos%) dim LightLevelPctAry(NumPos%) PosStep = int((UpperHardLimit - LowerHardLimit) / (NumPos% + 1)) TargPos = int(LowerHardLimit) + PosStep for PosIndex% = 1 to NumPos% TargPosAry(PosIndex%) = TargPos TargPos = TargPos + PosStep LightLevelPctAry(PosIndex%) = 0 next PosIndex% ! ----------------------------------------------------------------------------------------------- ! For each target position and for approaching from both below and above, we will obtain: ! a min, max and mean actual position and ! a min, max and mean measured PV. dim ActPosAry(NumPos%, 2 * NumIter%) dim MinPosAry(NumPos%, 2) dim MaxPosAry(NumPos%, 2) dim SumPosAry(NumPos%, 2) dim MeanPosAry(NumPos%, 2) dim SumSqPosAry(NumPos%, 2) dim SdevPosAry(NumPos%, 2) dim DataPvAry(NumPos%, 2 * NumIter%) dim MinPvAry(NumPos%, 2) dim MaxPvAry(NumPos%, 2) dim SumPvAry(NumPos%, 2) dim MeanPvAry(NumPos%, 2) dim SumSqPvAry(NumPos%, 2) dim SdevPvAry(NumPos%, 2) dim CountAry(NumPos%, 2) dim SlopeAry(NumPos%, 2) ! ----------------------------------------------------------------------------------------------- line$ = "Backlash measurement raw data" output @OutputFpRaw; line$ ! Column Headings ! Column values will come from these vars: ! LogIndex%, Date$, Time$, ElapsedTime, PosIndex%, PosDir%, TargPos, ActPos, PosErr, DataPv line$ = "" line$ = line$ & "Index" line$ = line$ & ", Date" line$ = line$ & ", Time" line$ = line$ & ", ET (sec)" line$ = line$ & ", Pos Index" line$ = line$ & ", Pos Dir" line$ = line$ & ", Target Pos" line$ = line$ & ", Actual Pos" line$ = line$ & ", Pos Error" line$ = line$ & ", PV (fr)" output @OutputFpRaw; line$ ! ----------------------------------------------------------------------------------------------- ! Set controls to support fitting a zernike plane to current data, then generating that plane GenCamRes = 0 gosub SET_ZERNIKE_PLANE_CONTROLS ! Uses var GenCamRes ! ----------------------------------------------------------------------------------------------- LogIndex% = 0 for IterIndex% = 1 to NumIter% ofmti("%d") msg$ = "Iteration " & val$(IterIndex%) & " of " & val$(NumIter%) msg$ = msg$ & ", moving below lowest target position" gosub SET_STATUS_MSG_2 TargPos = TargPosAry(1) - MaxBacklash gosub MOVE_AND_CHECK_POS_ERROR ! Uses vars TargPos and PosErrorTol, sets vars CurPos and PosError PosDir% = 1 for PosIndex% = 1 to NumPos% ofmti("%d") msg$ = "Iteration " & val$(IterIndex%) & " of " & val$(NumIter%) msg$ = msg$ & ", moving upward to position " & val$(PosIndex%) & " of " & val$(NumPos%) gosub SET_STATUS_MSG_2 gosub MOVE_TO_POS_AND_MEASURE next PosIndex% ofmti("%d") msg$ = "Iteration " & val$(IterIndex%) & " of " & val$(NumIter%) msg$ = msg$ & ", moving above highest target position" gosub SET_STATUS_MSG_2 TargPos = TargPosAry(NumPos%) + MaxBacklash gosub MOVE_AND_CHECK_POS_ERROR ! Uses vars TargPos and PosErrorTol, sets vars CurPos and PosError PosDir% = 2 for PosIndex% = NumPos% to 1 step -1 gosub MOVE_TO_POS_AND_MEASURE next PosIndex% next IterIndex% ! ----------------------------------------------------------------------------------------------- gosub CLR_STATUS_MSG_2 ! ----------------------------------------------------------------------------------------------- assign @OutputFpRaw to "" ! ----------------------------------------------------------------------------------------------- for PosDir% = 1 to 2 for PosIndex% = 1 to NumPos% MinPosAry(PosIndex%, PosDir%) = 999999 MaxPosAry(PosIndex%, PosDir%) = 0 SumPosAry(PosIndex%, PosDir%) = 0 MinPvAry(PosIndex%, PosDir%) = 999999 MaxPvAry(PosIndex%, PosDir%) = 0 SumPvAry(PosIndex%, PosDir%) = 0 for IterIndex% = 1 to NumIter% ActPos = ActPosAry(PosIndex%, PosDir% * IterIndex%) if ActPos < MinPosAry(PosIndex%, PosDir%) then MinPosAry(PosIndex%, PosDir%) = ActPos endif if ActPos > MaxPosAry(PosIndex%, PosDir%) then MaxPosAry(PosIndex%, PosDir%) = ActPos endif SumPosAry(PosIndex%, PosDir%) = SumPosAry(PosIndex%, PosDir%) + ActPos DataPv = DataPvAry(PosIndex%, PosDir% * IterIndex%) if DataPv < MinPvAry(PosIndex%, PosDir%) then MinPvAry(PosIndex%, PosDir%) = DataPv endif if DataPv > MaxPvAry(PosIndex%, PosDir%) then MaxPvAry(PosIndex%, PosDir%) = DataPv endif SumPvAry(PosIndex%, PosDir%) = SumPvAry(PosIndex%, PosDir%) + DataPv next IterIndex% next PosIndex% next PosDir% ! ----------------------------------------------------------------------------------------------- for PosDir% = 1 to 2 for PosIndex% = 1 to NumPos% MeanPosAry(PosIndex%, PosDir%) = SumPosAry(PosIndex%, PosDir%) / NumIter% MeanPvAry(PosIndex%, PosDir%) = SumPvAry(PosIndex%, PosDir%) / NumIter% next PosIndex% next PosDir% ! ----------------------------------------------------------------------------------------------- for PosDir% = 1 to 2 for PosIndex% = 1 to NumPos% SumSqPosAry(PosIndex%, PosDir%) = 0 SumSqPvAry(PosIndex%, PosDir%) = 0 MeanPos = MeanPosAry(PosIndex%, PosDir%) MeanPv = MeanPvAry(PosIndex%, PosDir%) for IterIndex% = 1 to NumIter% ActPos = ActPosAry(PosIndex%, PosDir% * IterIndex%) Diff = ActPos - MeanPos SumSqPosAry(PosIndex%, PosDir%) = SumSqPosAry(PosIndex%, PosDir%) + (Diff * Diff) DataPv = DataPvAry(PosIndex%, PosDir% * IterIndex%) Diff = DataPv - MeanPv SumSqPvAry(PosIndex%, PosDir%) = SumSqPvAry(PosIndex%, PosDir%) + (Diff * Diff) next IterIndex% next PosIndex% next PosDir% ! ----------------------------------------------------------------------------------------------- for PosDir% = 1 to 2 for PosIndex% = 1 to NumPos% SdevPosAry(PosIndex%, PosDir%) = sqrt( SumSqPosAry(PosIndex%, PosDir%) / NumIter% ) SdevPvAry(PosIndex%, PosDir%) = sqrt( SumSqPvAry(PosIndex%, PosDir%) / NumIter% ) next PosIndex% next PosDir% ! ----------------------------------------------------------------------------------------------- for PosDir% = 1 to 2 SlopeAry(1, PosDir%) = 0 for PosIndex% = 2 to (NumPos% - 1) DeltaPv = MeanPvAry(PosIndex% + 1, PosDir%) - MeanPvAry(PosIndex% - 1, PosDir%) DeltaPos = MeanPosAry(PosIndex% + 1, PosDir%) - MeanPosAry(PosIndex% - 1, PosDir%) SlopeAry(PosIndex%, PosDir%) = DeltaPv / DeltaPos next PosIndex% SlopeAry(NumPos%, PosDir%) = 0 next PosDir% ! ----------------------------------------------------------------------------------------------- line$ = "Backlash measurement summary data" output @OutputFpSum; line$ ! ----------------------------------------------------------------------------------------------- ! Column Headings ! Column values will come from these vars: ! PosIndex%, PosDir%, IterIndex%, DevPvPct line$ = "" line$ = line$ & "Pos Index" line$ = line$ & ", Pos Dir" line$ = line$ & ", Iter Index" line$ = line$ & ", Pv Dev %" output @OutputFpSum; line$ ! ----------------------------------------------------------------------------------------------- for PosIndex% = 1 to NumPos% for PosDir% = 1 to 2 for IterIndex% = 1 to NumIter% ! DevPvPct is the PV percent deviation from mean DataPv = DataPvAry(PosIndex%, PosDir% * IterIndex%) MeanPv = MeanPvAry(PosIndex%, PosDir%) DevPvPct = 100 * (DataPv - MeanPv) / MeanPv ofmti("%3d") line$ = val$(PosIndex%) line$ = line$ & ", " & val$(PosDir%) line$ = line$ & ", " & val$(IterIndex%) ofmtr("%9.3f") line$ = line$ & ", " & val$(DevPvPct) output @OutputFpSum; line$ next IterIndex% next PosDir% next PosIndex% ! ----------------------------------------------------------------------------------------------- ! Column Headings ! Column values will come from these vars: ! PosIndex%, PosDir%, MinPos, MaxPos, MeanPos, SdevPos, MinPv, MaxPv, MeanPv, SdevPv, Slope line$ = "" line$ = line$ & "Pos Index" line$ = line$ & ", Pos Dir" line$ = line$ & ", Min Pos" line$ = line$ & ", Max Pos" line$ = line$ & ", Mean Pos" line$ = line$ & ", Sdev Pos" line$ = line$ & ", Min PV (fr)" line$ = line$ & ", Max PV (fr)" line$ = line$ & ", Mean PV (fr)" line$ = line$ & ", Sdev PV (fr)" line$ = line$ & ", Slope" output @OutputFpSum; line$ ! ----------------------------------------------------------------------------------------------- for PosIndex% = 1 to NumPos% for PosDir% = 1 to 2 MinPos = MinPosAry(PosIndex%, PosDir%) MaxPos = MaxPosAry(PosIndex%, PosDir%) MeanPos = MeanPosAry(PosIndex%, PosDir%) SdevPos = SdevPosAry(PosIndex%, PosDir%) MinPv = MinPvAry(PosIndex%, PosDir%) MaxPv = MaxPvAry(PosIndex%, PosDir%) MeanPv = MeanPvAry(PosIndex%, PosDir%) SdevPv = SdevPvAry(PosIndex%, PosDir%) Slope = SlopeAry(PosIndex%, PosDir%) ofmti("%3d") line$ = val$(PosIndex%) line$ = line$ & ", " & val$(PosDir%) ofmtr("%9.2f") line$ = line$ & ", " & val$(MinPos) line$ = line$ & ", " & val$(MaxPos) line$ = line$ & ", " & val$(MeanPos) line$ = line$ & ", " & val$(SdevPos) ofmtr("%9.3f") line$ = line$ & ", " & val$(MinPv) line$ = line$ & ", " & val$(MaxPv) line$ = line$ & ", " & val$(MeanPv) line$ = line$ & ", " & val$(SdevPv) ofmtr("%10.6f") line$ = line$ & ", " & val$(Slope) output @OutputFpSum; line$ next PosDir% next PosIndex% ! ----------------------------------------------------------------------------------------------- ! Column Headings ! Column values will come from these vars: ! PosIndex%, MeanSlope, DeltaPv, Backlash line$ = "" line$ = line$ & "Pos Index" line$ = line$ & ", Mean Slope" line$ = line$ & ", Delta PV (fr)" line$ = line$ & ", Backlash" output @OutputFpSum; line$ ! ----------------------------------------------------------------------------------------------- ! Number of backlash values that will be calculated NumBacklash% = NumPos% - 2 dim BacklashAry(NumBacklash%) ! ----------------------------------------------------------------------------------------------- BacklashIndex% = 0 for PosIndex% = 2 to (NumPos% - 1) MeanSlope = (SlopeAry(PosIndex%, 1) + SlopeAry(PosIndex%, 2)) / 2 DeltaPv = MeanPvAry(PosIndex%, 2) - MeanPvAry(PosIndex%, 1) Backlash = DeltaPv / MeanSlope BacklashIndex% = BacklashIndex% + 1 BacklashAry(BacklashIndex%) = Backlash ofmti("%3d") line$ = val$(PosIndex%) ofmtr("%10.6f") line$ = line$ & ", " & val$(MeanSlope) ofmtr("%9.3f") line$ = line$ & ", " & val$(DeltaPv) ofmtr("%5.0f") line$ = line$ & ", " & val$(Backlash) output @OutputFpSum; line$ next PosIndex% ! ----------------------------------------------------------------------------------------------- MeasuredBacklash = 0 for BacklashIndex% = 1 to NumBacklash% Backlash = abs(BacklashAry(BacklashIndex%)) if Backlash > MeasuredBacklash then MeasuredBacklash = Backlash endif next BacklashIndex% ofmtr("%.0f") line$ = "Measured backlash = " & val$(MeasuredBacklash) output @OutputFpSum; line$ ! ----------------------------------------------------------------------------------------------- assign @OutputFpSum to "" ! ----------------------------------------------------------------------------------------------- ElapsedTime = timedate - StartTime ! ----------------------------------------------------------------------------------------------- msg$ = "Measure Backlash done" gosub LOG_DATE_TIME_MSG ! ----------------------------------------------------------------------------------------------- if MeasuredBacklash > BacklashMeasuredMax then ofmtr("%.0f") msg$ = "Measured backlash = " & val$(MeasuredBacklash) & " is EXCESSIVE.\n" gosub SET_STATUS_MSG_2 gosub LOG_MSG msg$ = " Measure Backlash done \n" ofmtr("%.0f") msg$ = msg$ & " Measured backlash = " & val$(MeasuredBacklash) & " is EXCESSIVE. \n" msg$ = msg$ & " This lens should be REJECTED! \n" ofmtr("%.1f") msg$ = msg$ & " Elapsed time = " & val$(ElapsedTime/60) & " min \n" gosub SHOW_ERROR_DIALOG stop endif ! ----------------------------------------------------------------------------------------------- if MeasuredBacklash < BacklashMeasuredMin then ofmtr("%.0f") msg$ = "Measured backlash = " & val$(MeasuredBacklash) & " is not significant" gosub SET_STATUS_MSG_2 gosub LOG_MSG BacklashComp = 0 gosub SET_BACKLASH_COMP ! Sets control using var BacklashComp msg$ = "Backlash Comp control set to zero" gosub LOG_MSG if quietFlag% = 0 then msg$ = " Measure Backlash done \n" ofmtr("%.0f") msg$ = msg$ & " Measured backlash = " & val$(MeasuredBacklash) & " is not significant. \n" msg$ = msg$ & " Backlash compensation is not required. \n" msg$ = msg$ & " Backlash Comp control set to zero. \n" ofmtr("%.1f") msg$ = msg$ & " Elapsed time = " & val$(ElapsedTime/60) & " min \n" gosub SHOW_MESSAGE_DIALOG endif else BacklashComp = MeasuredBacklash + BacklashCompMargin if BacklashComp < BacklashCompMin then BacklashComp = BacklashCompMin endif ! Want a negative value so that backlash compensation applies to positive move BacklashComp = -1 * BacklashComp gosub SET_BACKLASH_COMP ! Sets control using var BacklashComp ofmtr("%.0f") msg$ = "Measured backlash = " & val$(MeasuredBacklash) gosub SET_STATUS_MSG_2 gosub LOG_MSG msg$ = "Backlash Comp control set to " & val$(BacklashComp) gosub LOG_MSG if quietFlag% = 0 then msg$ = " Measure Backlash done \m" ofmtr("%.0f") msg$ = msg$ & " Measured backlash = " & val$(MeasuredBacklash) & " \n" msg$ = msg$ & " Backlash Comp control set to " & val$(BacklashComp) & ". \n" ofmtr("%.1f") msg$ = msg$ & " Elapsed time = " & val$(ElapsedTime/60) & " min \n" gosub SHOW_MESSAGE_DIALOG endif endif ! ----------------------------------------------------------------------------------------------- gosub SET_SCRIPT_STATUS_PASS ! ----------------------------------------------------------------------------------------------- end ! ----------------------------------------------------------------------------------------------- MOVE_TO_POS_AND_MEASURE: ofmti("%d") msg$ = "Iteration " & val$(IterIndex%) & " of " & val$(NumIter%) & ", moving " if PosDir% = 1 then msg$ = msg$ & "upward" else msg$ = msg$ & "downward" endif msg$ = msg$ & " and measuring position " & val$(PosIndex%) & " of " & val$(NumPos%) gosub SET_STATUS_MSG_2 TargPos = TargPosAry(PosIndex%) gosub MOVE_AND_CHECK_POS_ERROR ! Uses vars TargPos and PosErrorTol, sets vars CurPos and PosError if LightLevelPctAry(PosIndex%) <> 0 then LightLevelPct = LightLevelPctAry(PosIndex%) gosub SET_LIGHT_LEVEL_PCT ! Uses var LightLevelPct endif if SettlingTimeWait > 0 then wait SettlingTimeWait endif gosub GET_CUR_POS_AVG ! Sets var CurPos ActPos = CurPos gosub MEASURE_WITH_RETRY gosub GET_LIGHT_LEVEL_PCT ! Gets control value into var LightLevelPct LightLevelPctAry(PosIndex%) = LightLevelPct gosub GENERATE_ZERNIKE_PLANE ! Get PV result from Test+Ref Map gosub GET_DATA_PV_TR ! Gets result value into var DataPv (in fr) ActPosAry(PosIndex%, PosDir% * IterIndex%) = ActPos DataPvAry(PosIndex%, PosDir% * IterIndex%) = DataPv LogIndex% = LogIndex% + 1 t = timedate Date$ = date$(t) Time$ = time$(t) ElapsedTime = t - StartTime line$ = "" ofmti("%3d") line$ = line$ & val$(LogIndex%) line$ = line$ & ", " & Date$ line$ = line$ & ", " & Time$ ofmtr("%4.0f") line$ = line$ & ", " & val$(ElapsedTime) ofmti("%3d") line$ = line$ & ", " & val$(PosIndex%) line$ = line$ & ", " & val$(PosDir%) ofmtr("%9.2f") line$ = line$ & ", " & val$(TargPos) ofmtr("%9.2f") line$ = line$ & ", " & val$(ActPos) line$ = line$ & ", " & val$(PosError) ofmtr("%9.3f") line$ = line$ & ", " & val$(DataPv) output @OutputFpRaw; line$ return ! ----------------------------------------------------------------------------------------------- loadsub "EncZoomSubs.scr" loadsub "EncZoomSdoSubs.scr" ! -----------------------------------------------------------------------------------------------