public void TestThermalCalibrateRadioTelescope_TriesThermalCalibrateRadioTelescopeWithAnotherCommandRunning_AlreadyMoving() { MovementResult result0 = MovementResult.None; MovementResult result1 = MovementResult.None; MovementPriority priority = MovementPriority.Manual; // This is running two commands at the same time. One of them should succeed, while // the other is rejected Thread t0 = new Thread(() => { result0 = TestRadioTelescopeController.ThermalCalibrateRadioTelescope(priority); }); Thread t1 = new Thread(() => { result1 = TestRadioTelescopeController.ThermalCalibrateRadioTelescope(priority); }); t0.Start(); t1.Start(); t0.Join(); t1.Join(); Assert.IsTrue(result0 == MovementResult.Success || result1 == MovementResult.Success); Assert.IsTrue(result0 == MovementResult.AlreadyMoving || result1 == MovementResult.AlreadyMoving); }
/// <summary> /// Method used to cancel this Radio Telescope's current attempt to change orientation. /// /// The implementation of this functionality is on a "per-RT" basis, as /// in this may or may not work, it depends on if the derived /// AbstractRadioTelescope class has implemented it. /// </summary> public bool CancelCurrentMoveCommand(MovementPriority priority) { bool result = false; if (Monitor.TryEnter(MovementLock) && priority > RadioTelescope.PLCDriver.CurrentMovementPriority) { result = RadioTelescope.PLCDriver.Cancel_move(); RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; Monitor.Exit(MovementLock); } return(result); }
/// <summary> /// Method used to request that all of the Radio Telescope's movement comes /// to an immediate stop. /// /// The implementation of this functionality is on a "per-RT" basis, as /// in this may or may not work, it depends on if the derived /// AbstractRadioTelescope class has implemented it. /// </summary> public bool ExecuteRadioTelescopeImmediateStop(MovementPriority priority) { bool success = false; if (Monitor.TryEnter(MovementLock)) { if (priority > RadioTelescope.PLCDriver.CurrentMovementPriority) { success = RadioTelescope.PLCDriver.ImmediateStop(); RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; } Monitor.Exit(MovementLock); } return(success); }
/// <summary> /// Method used to request to move the Radio Telescope to an objective /// right ascension/declination coordinate pair. /// /// The implementation of this functionality is on a "per-RT" basis, as /// in this may or may not work, it depends on if the derived /// AbstractRadioTelescope class has implemented it. /// </summary> public MovementResult MoveRadioTelescopeToCoordinate(Coordinate coordinate, MovementPriority priority) { MovementResult result = MovementResult.None; // Return if incoming priority is equal to or less than current movement if (priority <= RadioTelescope.PLCDriver.CurrentMovementPriority) { return(MovementResult.AlreadyMoving); } // We only want to do this if it is safe to do so. Return false if not if (!AllSensorsSafe) { return(MovementResult.SensorsNotSafe); } // If a lower-priority movement was running, safely interrupt it. RadioTelescope.PLCDriver.InterruptMovementAndWaitUntilStopped(); // If the thread is locked (two moves coming in at the same time), return if (Monitor.TryEnter(MovementLock)) { RadioTelescope.PLCDriver.CurrentMovementPriority = priority; result = RadioTelescope.PLCDriver.MoveToOrientation(CoordinateController.CoordinateToOrientation(coordinate, DateTime.UtcNow), GetCurrentOrientation()); if (RadioTelescope.PLCDriver.CurrentMovementPriority == priority) { RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; } Monitor.Exit(MovementLock); } else { result = MovementResult.AlreadyMoving; } return(result); }
public int ResolveConflictFreeOperations(InconsistencyCoverage ic, EntityChange.ExecutionContext ctx) { int errors = 0; Parallel.ForEach(deferredInserts.Values, bag => { Entity e; InsertPriority best = new InsertPriority(); Interlocked.Add(ref errors, bag.Count - 1); while (bag.TryTake(out e)) { var candidate = new InsertPriority(e, ctx, ic); if (candidate > best) { best = candidate; } } if (best.Destination == null || !Insert(best.Destination)) { Interlocked.Increment(ref errors); } }); deferredInserts.Clear(); Parallel.ForEach(fullMap.Values, ctr => { if (ctr.deferredUpdates.IsEmpty) { return; } Tuple <EntityID, Entity> tuple; bool inc = ic.IsInconsistentR(ctx.LocalSpace.Relativate(ctr.entity.ID.Position)); MovementPriority best = new MovementPriority(); Interlocked.Add(ref errors, ctr.deferredUpdates.Count - 1); while (ctr.deferredUpdates.TryTake(out tuple)) { bool destInc = ic.IsInconsistentR(ctx.LocalSpace.Relativate(tuple.Item2.ID.Position)); var candidate = new MovementPriority(ctr.entity, tuple.Item1, tuple.Item2, ctx, inc, destInc); if (candidate > best) { best = candidate; } } if (best.Destination != null) { ctr.entity = best.Destination; tree = null; } else { Interlocked.Increment(ref errors); } }); EntityID id; while (deferredRemovals.TryTake(out id)) { if (CheckFindAndRemove(id)) { tree = null; } else { errors++; } } return(errors); }
public void TestAllTelescopeMovements_RunABunchOfCommandsAtTheSameTime_OnlyOneCommandSucceedsWhileTheRestAreAlreadyMoving() { int threadCount = 7; MovementPriority priority = MovementPriority.Manual; Coordinate c = new Coordinate(0, 0); Orientation o = new Orientation(0, 0); // movement result array MovementResult[] results = new MovementResult[threadCount]; for (int i = 0; i < threadCount; i++) { results[i] = MovementResult.None; } Thread[] threads = new Thread[7]; threads[0] = new Thread(() => { results[0] = TestRadioTelescopeController.HomeTelescope(priority); }); threads[1] = new Thread(() => { results[1] = TestRadioTelescopeController.SnowDump(priority); }); threads[2] = new Thread(() => { results[2] = TestRadioTelescopeController.MoveRadioTelescopeToCoordinate(c, priority); }); threads[3] = new Thread(() => { results[3] = TestRadioTelescopeController.MoveRadioTelescopeToOrientation(o, priority); }); threads[4] = new Thread(() => { results[4] = TestRadioTelescopeController.ThermalCalibrateRadioTelescope(priority); }); threads[5] = new Thread(() => { results[5] = TestRadioTelescopeController.StartRadioTelescopeJog(5, RadioTelescopeDirectionEnum.ClockwiseOrNegative, RadioTelescopeAxisEnum.AZIMUTH); }); threads[6] = new Thread(() => { results[6] = TestRadioTelescopeController.FullElevationMove(priority); }); // Run all threads concurrently // These cannot be looped through because a loop adds enough delay to throw off concurrency threads[0].Start(); threads[1].Start(); threads[2].Start(); threads[3].Start(); threads[4].Start(); threads[5].Start(); threads[6].Start(); // Wait for all threads to complete foreach (Thread thread in threads) { thread.Join(); } int successes = 0; int alreadyMovings = 0; for (int i = 0; i < threadCount; i++) { if (results[i] == MovementResult.Success) { successes++; } else if (results[i] == MovementResult.AlreadyMoving) { alreadyMovings++; } } Assert.AreEqual(1, successes); Assert.AreEqual(threadCount - 1, alreadyMovings); }
/// <summary> /// fires whenever the data on the modbus server changes /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Server_Written_to_handler(object sender, DataStoreEventArgs e) { //e.Data.B //array representing data if (is_test) { logger.Info(Utilities.GetTimeStamp() + ": recived message from PLC"); } PLC_last_contact = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); switch (e.StartAddress) { case (ushort)PLC_modbus_server_register_mapping.AZ_0_LIMIT: { bool previous = limitSwitchData.Azimuth_CCW_Limit; limitSwitchData.Azimuth_CCW_Limit = !Int_to_bool(PLC_Modbusserver.DataStore.HoldingRegisters[(int)PLC_modbus_server_register_mapping.AZ_0_LIMIT]); if (previous != limitSwitchData.Azimuth_CCW_Limit) { pLCEvents.PLCLimitChanged(limitSwitchData, PLC_modbus_server_register_mapping.AZ_0_LIMIT, limitSwitchData.Azimuth_CCW_Limit); } break; } case (ushort)PLC_modbus_server_register_mapping.AZ_0_HOME: { bool previous = homeSensorData.Azimuth_Home_One; homeSensorData.Azimuth_Home_One = Int_to_bool(PLC_Modbusserver.DataStore.HoldingRegisters[(int)PLC_modbus_server_register_mapping.AZ_0_HOME]); break; } case (ushort)PLC_modbus_server_register_mapping.AZ_0_SECONDARY: { bool previous = homeSensorData.Azimuth_Home_Two; homeSensorData.Azimuth_Home_Two = Int_to_bool(PLC_Modbusserver.DataStore.HoldingRegisters[(int)PLC_modbus_server_register_mapping.AZ_0_SECONDARY]); break; } case (ushort)PLC_modbus_server_register_mapping.AZ_375_LIMIT: { bool previous = limitSwitchData.Azimuth_CW_Limit; limitSwitchData.Azimuth_CW_Limit = !Int_to_bool(PLC_Modbusserver.DataStore.HoldingRegisters[(int)PLC_modbus_server_register_mapping.AZ_375_LIMIT]); if (previous != limitSwitchData.Azimuth_CW_Limit) { pLCEvents.PLCLimitChanged(limitSwitchData, PLC_modbus_server_register_mapping.AZ_375_LIMIT, limitSwitchData.Azimuth_CW_Limit); } break; } case (ushort)PLC_modbus_server_register_mapping.EL_10_LIMIT: { bool previous = limitSwitchData.Elevation_Lower_Limit; limitSwitchData.Elevation_Lower_Limit = !Int_to_bool(PLC_Modbusserver.DataStore.HoldingRegisters[(int)PLC_modbus_server_register_mapping.EL_10_LIMIT]); if (previous != limitSwitchData.Elevation_Lower_Limit) { pLCEvents.PLCLimitChanged(limitSwitchData, PLC_modbus_server_register_mapping.EL_10_LIMIT, limitSwitchData.Elevation_Lower_Limit); if (limitSwitchData.Elevation_Lower_Limit) { logger.Info(Utilities.GetTimeStamp() + ": Elevation Lower Limit Switch Hit"); pushNotification.sendToAllAdmins("LIMIT SWITCH", "Elevation lower limit switch hit"); EmailNotifications.sendToAllAdmins("LIMIT SWITCH", "Elevation lower limit switch hit"); } } break; } case (ushort)PLC_modbus_server_register_mapping.EL_0_HOME: { bool previous = homeSensorData.Elevation_Home; homeSensorData.Elevation_Home = Int_to_bool(PLC_Modbusserver.DataStore.HoldingRegisters[(int)PLC_modbus_server_register_mapping.EL_0_HOME]); break; } case (ushort)PLC_modbus_server_register_mapping.EL_90_LIMIT: { bool previous = limitSwitchData.Elevation_Upper_Limit; limitSwitchData.Elevation_Upper_Limit = !Int_to_bool(PLC_Modbusserver.DataStore.HoldingRegisters[(int)PLC_modbus_server_register_mapping.EL_90_LIMIT]); if (previous != limitSwitchData.Elevation_Upper_Limit) { pLCEvents.PLCLimitChanged(limitSwitchData, PLC_modbus_server_register_mapping.EL_90_LIMIT, limitSwitchData.Elevation_Upper_Limit); if (limitSwitchData.Elevation_Upper_Limit) { logger.Info(Utilities.GetTimeStamp() + ": Elevation Upper Limit Switch Hit"); pushNotification.sendToAllAdmins("LIMIT SWITCH", "Elevation upper limit switch hit"); EmailNotifications.sendToAllAdmins("LIMIT SWITCH", "Elevation upper limit switch hit"); } } break; } case (ushort)PLC_modbus_server_register_mapping.Gate_Safety_INTERLOCK: { bool previous = plcInput.Gate_Sensor; plcInput.Gate_Sensor = !Int_to_bool(PLC_Modbusserver.DataStore.HoldingRegisters[(int)PLC_modbus_server_register_mapping.Gate_Safety_INTERLOCK]); if (previous != plcInput.Gate_Sensor) { if (plcInput.Gate_Sensor) { logger.Info(Utilities.GetTimeStamp() + ": gate opened"); pushNotification.sendToAllAdmins("GATE ACTIVITY", "Gate has been opened."); EmailNotifications.sendToAllAdmins("GATE ACTIVITY", "Gate has been opened."); } else { logger.Info(Utilities.GetTimeStamp() + ": gate closed"); pushNotification.sendToAllAdmins("GATE ACTIVITY", "Gate has been closed."); EmailNotifications.sendToAllAdmins("GATE ACTIVITY", "Gate has been closed."); } } break; } case (ushort)PLC_modbus_server_register_mapping.E_STOP: { bool previous = plcInput.Estop; plcInput.Estop = !Int_to_bool(PLC_Modbusserver.DataStore.HoldingRegisters[(int)PLC_modbus_server_register_mapping.E_STOP]); if (previous != plcInput.Estop) { if (plcInput.Estop) { logger.Info(Utilities.GetTimeStamp() + ": Estop Hit"); CurrentMovementPriority = MovementPriority.Critical; pushNotification.sendToAllAdmins("E-STOP ACTIVITY", "E-stop has been hit."); EmailNotifications.sendToAllAdmins("E-STOP ACTIVITY", "E-stop has been hit."); } else { logger.Info(Utilities.GetTimeStamp() + ": Estop released"); CurrentMovementPriority = MovementPriority.None; pushNotification.sendToAllAdmins("E-STOP ACTIVITY", "E-stop has been released."); EmailNotifications.sendToAllAdmins("E-STOP ACTIVITY", "E-stop has been released."); } } break; } case (ushort)PLC_modbus_server_register_mapping.EL_SLIP_CAPTURE: { bool previous = plcInput.EL_Slip_CAPTURE; plcInput.EL_Slip_CAPTURE = Int_to_bool(PLC_Modbusserver.DataStore.HoldingRegisters[(int)PLC_modbus_server_register_mapping.EL_SLIP_CAPTURE]); break; } } }
private async void DefaultLimitSwitchHandle(object sender, limitEventArgs e) { if (e.Value) { switch (e.ChangedValue) { case PLC_modbus_server_register_mapping.AZ_0_LIMIT: { if (!Overrides.overrideAzimuthProx0) { CurrentMovementPriority = MovementPriority.Critical; MCU.SendSingleAxisJog(RadioTelescopeAxisEnum.AZIMUTH, RadioTelescopeDirectionEnum.ClockwiseOrNegative, 0.25); } break; } case PLC_modbus_server_register_mapping.AZ_375_LIMIT: { if (!Overrides.overrideAzimuthProx375) { CurrentMovementPriority = MovementPriority.Critical; MCU.SendSingleAxisJog(RadioTelescopeAxisEnum.AZIMUTH, RadioTelescopeDirectionEnum.CounterclockwiseOrPositive, 0.25); } break; } case PLC_modbus_server_register_mapping.EL_10_LIMIT: { if (!Overrides.overrideElevatProx0) { CurrentMovementPriority = MovementPriority.Critical; MCU.SendSingleAxisJog(RadioTelescopeAxisEnum.ELEVATION, RadioTelescopeDirectionEnum.CounterclockwiseOrPositive, 0.25); } break; } case PLC_modbus_server_register_mapping.EL_90_LIMIT: { if (!Overrides.overrideElevatProx90) { CurrentMovementPriority = MovementPriority.Critical; MCU.SendSingleAxisJog(RadioTelescopeAxisEnum.ELEVATION, RadioTelescopeDirectionEnum.ClockwiseOrNegative, 0.25); } break; } } } if (!e.Value) { switch (e.ChangedValue) { case PLC_modbus_server_register_mapping.AZ_0_LIMIT: { if (!Overrides.overrideAzimuthProx0) { MCU.StopSingleAxisJog(RadioTelescopeAxisEnum.AZIMUTH); CurrentMovementPriority = MovementPriority.None; } break; } case PLC_modbus_server_register_mapping.AZ_375_LIMIT: { if (!Overrides.overrideAzimuthProx375) { MCU.StopSingleAxisJog(RadioTelescopeAxisEnum.AZIMUTH); CurrentMovementPriority = MovementPriority.None; } break; } case PLC_modbus_server_register_mapping.EL_10_LIMIT: { if (!Overrides.overrideElevatProx0) { MCU.StopSingleAxisJog(RadioTelescopeAxisEnum.ELEVATION); CurrentMovementPriority = MovementPriority.None; } break; } case PLC_modbus_server_register_mapping.EL_90_LIMIT: { if (!Overrides.overrideElevatProx90) { MCU.StopSingleAxisJog(RadioTelescopeAxisEnum.ELEVATION); CurrentMovementPriority = MovementPriority.None; } break; } } } }
/// <summary> /// This is a script that is called when we want to dump snow out of the dish /// </summary> public MovementResult SnowDump(MovementPriority priority) { MovementResult result = MovementResult.None; // Return if incoming priority is equal to or less than current movement if (priority <= RadioTelescope.PLCDriver.CurrentMovementPriority) { return(MovementResult.AlreadyMoving); } // We only want to do this if it is safe to do so. Return false if not if (!AllSensorsSafe) { return(MovementResult.SensorsNotSafe); } // If a lower-priority movement was running, safely interrupt it. RadioTelescope.PLCDriver.InterruptMovementAndWaitUntilStopped(); // If the thread is locked (two moves coming in at the same time), return if (Monitor.TryEnter(MovementLock)) { RadioTelescope.PLCDriver.CurrentMovementPriority = priority; // insert snow dump movements here // default is azimuth of 0 and elevation of 0 previousSnowDumpAzimuth += 45; if (previousSnowDumpAzimuth >= 360) { previousSnowDumpAzimuth -= 360; } Orientation dump = new Orientation(previousSnowDumpAzimuth, -5); Orientation current = GetCurrentOrientation(); Orientation dumpAzimuth = new Orientation(dump.Azimuth, current.Elevation); Orientation dumpElevation = new Orientation(dump.Azimuth, dump.Elevation); // move to dump snow result = RadioTelescope.PLCDriver.MoveToOrientation(dumpAzimuth, current); if (result != MovementResult.Success) { if (RadioTelescope.PLCDriver.CurrentMovementPriority == priority) { RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; } Monitor.Exit(MovementLock); return(result); } result = RadioTelescope.PLCDriver.MoveToOrientation(dumpElevation, dumpAzimuth); if (result != MovementResult.Success) { if (RadioTelescope.PLCDriver.CurrentMovementPriority == priority) { RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; } Monitor.Exit(MovementLock); return(result); } // move back to initial orientation result = RadioTelescope.PLCDriver.MoveToOrientation(current, dumpElevation); if (RadioTelescope.PLCDriver.CurrentMovementPriority == priority) { RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; } Monitor.Exit(MovementLock); } else { result = MovementResult.AlreadyMoving; } return(result); }
/// <summary> /// A demonstration script that moves the elevation motor to its maximum and minimum. /// </summary> /// <param name="priority">Movement priority.</param> /// <returns></returns> public MovementResult FullElevationMove(MovementPriority priority) { MovementResult result = MovementResult.None; // Return if incoming priority is equal to or less than current movement if (priority <= RadioTelescope.PLCDriver.CurrentMovementPriority) { return(MovementResult.AlreadyMoving); } // We only want to do this if it is safe to do so. Return false if not if (!AllSensorsSafe) { return(MovementResult.SensorsNotSafe); } // If a lower-priority movement was running, safely interrupt it. RadioTelescope.PLCDriver.InterruptMovementAndWaitUntilStopped(); // If the thread is locked (two moves coming in at the same time), return if (Monitor.TryEnter(MovementLock)) { RadioTelescope.PLCDriver.CurrentMovementPriority = priority; Orientation origOrientation = GetCurrentOrientation(); Orientation move1 = new Orientation(origOrientation.Azimuth, 0); Orientation move2 = new Orientation(origOrientation.Azimuth, 90); // Move to a low elevation point result = RadioTelescope.PLCDriver.MoveToOrientation(move1, origOrientation); if (result != MovementResult.Success) { if (RadioTelescope.PLCDriver.CurrentMovementPriority == priority) { RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; } Monitor.Exit(MovementLock); return(result); } // Move to a high elevation point result = RadioTelescope.PLCDriver.MoveToOrientation(move2, move1); if (result != MovementResult.Success) { if (RadioTelescope.PLCDriver.CurrentMovementPriority == priority) { RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; } Monitor.Exit(MovementLock); return(result); } // Move back to the original orientation result = RadioTelescope.PLCDriver.MoveToOrientation(origOrientation, move2); if (RadioTelescope.PLCDriver.CurrentMovementPriority == priority) { RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; } Monitor.Exit(MovementLock); } else { result = MovementResult.AlreadyMoving; } return(result); }
/// <summary> /// This is used to home the telescope. Immediately after homing, the telescope will move to "Stow" position. /// This will also zero out the absolute encoders and account for the true north offset. /// </summary> /// <param name="priority">The priority of this movement.</param> /// <returns>True if homing was successful; false if homing failed.</returns> public MovementResult HomeTelescope(MovementPriority priority) { MovementResult result = MovementResult.None; // Return if incoming priority is equal to or less than current movement if (priority <= RadioTelescope.PLCDriver.CurrentMovementPriority) { return(MovementResult.AlreadyMoving); } // We only want to do this if it is safe to do so. Return false if not if (!AllSensorsSafe) { return(MovementResult.SensorsNotSafe); } // If a lower-priority movement was running, safely interrupt it. RadioTelescope.PLCDriver.InterruptMovementAndWaitUntilStopped(); // If the thread is locked (two moves coming in at the same time), return if (Monitor.TryEnter(MovementLock)) { RadioTelescope.PLCDriver.CurrentMovementPriority = priority; // Remove all offsets first so we can accurately zero out the positions RadioTelescope.SensorNetworkServer.AbsoluteOrientationOffset = new Orientation(0, 0); FinalCalibrationOffset = new Orientation(0, 0); // Perform a home telescope movement result = RadioTelescope.PLCDriver.HomeTelescope(); // Zero out absolute encoders RadioTelescope.SensorNetworkServer.AbsoluteOrientationOffset = (Orientation)RadioTelescope.SensorNetworkServer.CurrentAbsoluteOrientation.Clone(); // Allow the absolute encoders' positions to even out Thread.Sleep(100); // Verify the absolute encoders have successfully zeroed out. There is a bit of fluctuation with their values, so homing could have occurred // with an outlier value. This check (with half-degree of precision) verifies that did not happen. Orientation absOrientation = RadioTelescope.SensorNetworkServer.CurrentAbsoluteOrientation; if ((Math.Abs(absOrientation.Elevation) > 0.5 && !overrides.overrideElevationAbsEncoder) || (Math.Abs(absOrientation.Azimuth) > 0.5 && !overrides.overrideAzimuthAbsEncoder)) { result = MovementResult.IncorrectPosition; } // Apply final calibration offset FinalCalibrationOffset = RadioTelescope.CalibrationOrientation; if (RadioTelescope.PLCDriver.CurrentMovementPriority == priority) { RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; } Monitor.Exit(MovementLock); } else { result = MovementResult.AlreadyMoving; } return(result); }
/// <summary> /// This is a method used to move the radio telescope by X degrees. /// Entering 0 for an axis will not move that motor. /// </summary> /// <param name="degreesToMoveBy">The number of degrees to move by.</param> /// <param name="priority">The movement's priority.</param> /// <returns></returns> public MovementResult MoveRadioTelescopeByXDegrees(Orientation degreesToMoveBy, MovementPriority priority) { // TODO: Implement (issue #379) return(MovementResult.None); }
/// <summary> /// Method used to calibrate the Radio Telescope before each observation. /// /// The implementation of this functionality is on a "per-RT" basis, as /// in this may or may not work, it depends on if the derived /// AbstractRadioTelescope class has implemented it. /// </summary> public MovementResult ThermalCalibrateRadioTelescope(MovementPriority priority) { MovementResult moveResult = MovementResult.None; // Return if incoming priority is equal to or less than current movement if (priority <= RadioTelescope.PLCDriver.CurrentMovementPriority) { return(MovementResult.AlreadyMoving); } // We only want to do this if it is safe to do so. Return false if not if (!AllSensorsSafe) { return(MovementResult.SensorsNotSafe); } // If a lower-priority movement was running, safely interrupt it. RadioTelescope.PLCDriver.InterruptMovementAndWaitUntilStopped(); // If the thread is locked (two moves coming in at the same time), return if (Monitor.TryEnter(MovementLock)) { Orientation current = GetCurrentOrientation(); moveResult = RadioTelescope.PLCDriver.MoveToOrientation(MiscellaneousConstants.THERMAL_CALIBRATION_ORIENTATION, current); if (moveResult != MovementResult.Success) { if (RadioTelescope.PLCDriver.CurrentMovementPriority != MovementPriority.Critical) { RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; } Monitor.Exit(MovementLock); return(moveResult); } // start a timer so we can have a time variable Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); // temporarily set spectracyber mode to continuum RadioTelescope.SpectraCyberController.SetSpectraCyberModeType(SpectraCyberModeTypeEnum.CONTINUUM); // read data SpectraCyberResponse response = RadioTelescope.SpectraCyberController.DoSpectraCyberScan(); // end the timer stopWatch.Stop(); double time = stopWatch.Elapsed.TotalSeconds; RFData rfResponse = RFData.GenerateFrom(response); // move back to previous location moveResult = RadioTelescope.PLCDriver.MoveToOrientation(current, MiscellaneousConstants.THERMAL_CALIBRATION_ORIENTATION); if (moveResult != MovementResult.Success) { if (RadioTelescope.PLCDriver.CurrentMovementPriority != MovementPriority.Critical) { RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; } RadioTelescope.SpectraCyberController.StopScan(); Monitor.Exit(MovementLock); return(moveResult); } // analyze data // temperature (Kelvin) = (intensity * time * wein's displacement constant) / (Planck's constant * speed of light) double weinConstant = 2.8977729; double planckConstant = 6.62607004 * Math.Pow(10, -34); double speedConstant = 299792458; double temperature = (rfResponse.Intensity * time * weinConstant) / (planckConstant * speedConstant); // convert to fahrenheit temperature = temperature * (9 / 5) - 459.67; // check against weather station reading double weatherStationTemp = RadioTelescope.WeatherStation.GetOutsideTemp(); // Set SpectraCyber mode back to UNKNOWN RadioTelescope.SpectraCyberController.SetSpectraCyberModeType(SpectraCyberModeTypeEnum.UNKNOWN); // return true if working correctly, false if not if (Math.Abs(weatherStationTemp - temperature) < MiscellaneousConstants.THERMAL_CALIBRATION_OFFSET) { moveResult = RadioTelescope.PLCDriver.MoveToOrientation(MiscellaneousConstants.Stow, current); } if (RadioTelescope.PLCDriver.CurrentMovementPriority != MovementPriority.Critical) { RadioTelescope.PLCDriver.CurrentMovementPriority = MovementPriority.None; } RadioTelescope.SpectraCyberController.StopScan(); Monitor.Exit(MovementLock); } else { moveResult = MovementResult.AlreadyMoving; } return(moveResult); }