public static GraphPane GenericSatComparisonGraph(Graphics g, string sat1Path, string sat2Path, string centerEpochISOYMD) { IAgCrdnVector diff = StateCompare.GetVectorBetweenObjects(sat1Path, sat1Path, sat2Path); object[] diffMin = StateCompare.GetTimeOfMinAndValue(diff); string timeOfMinRange = (string)diffMin.GetValue(0); double minRange = ((double)diffMin.GetValue(1)); double[] ric = StateCompare.GetRICDifferenceAtTCA(sat1Path, sat2Path, centerEpochISOYMD); double rangeAtTCA = Math.Sqrt(ric[0] * ric[0] + ric[1] * ric[1] + ric[2] * ric[2]); TextObj stktcaComment = ZedGraphAssistant.CreateGraphLabel("Time of Min Range: " + timeOfMinRange, .01f, .01f, Color.Black); TextObj stkmissComment = ZedGraphAssistant.CreateGraphLabel("Minimum Range: " + minRange.ToString(), .01f, .05f, Color.Black); TextObj tcaComment = ZedGraphAssistant.CreateGraphLabel("Comparison Time : " + centerEpochISOYMD, 1f, .01f, AlignH.Right, AlignV.Top, Color.Black); TextObj tcaRangeComment = ZedGraphAssistant.CreateGraphLabel("Range : " + rangeAtTCA.ToString(), 1f, .05f, AlignH.Right, AlignV.Top, Color.Black); TextObj[] comments = new TextObj[] { stktcaComment, stkmissComment, tcaComment, tcaRangeComment }; StateCompare.RICResults ricOverTime = StateCompare.GetRICDifferenceOverTime(sat1Path, sat2Path, centerEpochISOYMD); PointPairList pplR = ZedGraphAssistant.ArrayToPlottableList(ricOverTime.Times, ricOverTime.R); PointPairList pplI = ZedGraphAssistant.ArrayToPlottableList(ricOverTime.Times, ricOverTime.I); PointPairList pplC = ZedGraphAssistant.ArrayToPlottableList(ricOverTime.Times, ricOverTime.C); PointPairList pplRange = ZedGraphAssistant.ArrayToPlottableList(ricOverTime.Times, ricOverTime.Range); PointPairList[] ppl = new PointPairList[] { pplR, pplI, pplC, pplRange }; string[] pplNames = new string[] { "Radial", "In-Track", "Cross-Track", "Range", "" }; string sat1Name = sat1Path.Substring(sat1Path.LastIndexOf("/") + 1); string sat2Name = sat2Path.Substring(sat2Path.LastIndexOf("/") + 1); return(ZedGraphAssistant.CreateGraph("Comparison: " + sat1Name + " to " + sat2Name, centerEpochISOYMD, g, ppl, pplNames, comments)); }
private void CheckInputVector(IAgCrdnVector inputVector) { if (VectorIsValid(inputVector) == false) { throw new Exception($"{((IAgCrdn)inputVector).Name} Vector is not valid."); } }
private void GetStkTCA() { IAgCrdnVector diff = StateCompare.GetVectorBetweenObjects("*/Satellite/" + Primary.StkCdmSatelliteName, "*/Satellite/" + Primary.StkCdmSatelliteName, "*/Satellite/" + Secondary.StkCdmSatelliteName); object[] diffMin = StateCompare.GetTimeOfMinAndValue(diff); _stkTimeOfMinRange = (string)diffMin.GetValue(0); _stkMinimumRange = ((double)diffMin.GetValue(1)); }
private void GetStkCdmTleTCA() { IAgCrdnVector diff = StateCompare.GetVectorBetweenObjects(StkCdmSatellitePath, StkCdmSatellitePath, StkTleSatellitePath); object[] diffMin = StateCompare.GetTimeOfMinAndValue(diff); _stkTimeOfMinRange = (string)diffMin.GetValue(0); _stkMinimumRange = ((double)diffMin.GetValue(1)); }
public static object[] GetTcaRangeInfo(string sat1Path, string sat2Path) { IAgCrdnVector diff = StateCompare.GetVectorBetweenObjects(sat1Path, sat1Path, sat2Path); object[] diffMin = StateCompare.GetTimeOfMinAndValue(diff); string stkTimeOfMinRange = (string)diffMin.GetValue(0); double stkMinimumRange = ((double)diffMin.GetValue(1)); return(new object[] { stkTimeOfMinRange, stkMinimumRange }); }
public static void CreateSensorFromVector(IAgCrdnVector vector, double fovAngle, double displayRange = double.NaN, string[] displayTimes = null) { string[] pathParts = (vector as IAgCrdn).QualifiedPath.Split(' '); string parentObjectPath = "*/" + pathParts[0]; IAgStkObject parentObject = Root.GetObjectFromPath(parentObjectPath); IAgCrdnVector zenith = parentObject.Vgt.Vectors["Zenith(Detic)"]; string sensorName = (vector as IAgCrdn).Name; int count = 1; while (Root.ObjectExists(parentObject.Path + "/Sensor/" + sensorName)) { sensorName = (vector as IAgCrdn).Name + "_" + count.ToString(); ++count; } IAgStkObject sensor = parentObject.Children.New(AgESTKObjectType.eSensor, sensorName); (sensor as IAgSensor).SetPatternType(AgESnPattern.eSnSimpleConic); ((sensor as IAgSensor).Pattern as IAgSnSimpleConicPattern).ConeAngle = fovAngle; string command = "Point " + sensor.Path + " AlongVector \"" + (vector as IAgCrdn).Path + "\" \"" + (zenith as IAgCrdn).Path + "\""; string result; if (!TryConnect(command, out result)) { //MessageBox.Show(result, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } if (displayRange != double.NaN) { command = "VO " + sensor.Path + " Projection SpaceProjection " + displayRange.ToString(); if (!TryConnect(command, out result)) { //MessageBox.Show(result, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } if (displayTimes != null) { string displayTimesCommand = ""; foreach (string time in displayTimes) { displayTimesCommand += "\"" + time + "\" "; } command = "DisplayTimes " + sensor.Path + " Intervals Add " + (displayTimes.Length / 2).ToString() + " " + displayTimesCommand; if (!TryConnect(command, out result)) { //MessageBox.Show(result, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } }
private void ShowAttitudeGraphics(IAgCrdnVector alignedVector, IAgCrdnVector constrainedVector, IAgCrdnAxes bodyAxes, IAgCrdnAngle offAxisAngle) { // Show aligned vector and constrained vector DisplayElement(AgEGeometricElemType.eVectorElem, (IAgCrdn)alignedVector, Color.Yellow); DisplayElement(AgEGeometricElemType.eVectorElem, (IAgCrdn)constrainedVector, Color.LawnGreen); // Show body axes DisplayElement(AgEGeometricElemType.eAxesElem, (IAgCrdn)bodyAxes, Color.RoyalBlue); // Display angle from constraint vector to Body axis on original satellite to show limited atttiude. DisplayElement(AgEGeometricElemType.eAngleElem, (IAgCrdn)offAxisAngle, Color.White); }
private bool VectorIsValid(IAgCrdnVector vector) { // If vector cannot be evaluated at scenario start (0 EpSec), it's invalid IAgCrdnAxes axes = m_referenceSatellite.Vgt.Axes["Body"]; string dateFormat = CommonData.StkRoot.UnitPreferences.GetCurrentUnitAbbrv("DateFormat"); string epoch = CommonData.StkRoot.ConversionUtility.ConvertDate("EpSec", dateFormat, "0"); IAgCrdnVectorFindInAxesResult result = vector.FindInAxes(epoch, axes); return(result.IsValid); }
private IAgCrdnAngle CreateAngle(IAgStkObject stkObject, string name, IAgCrdnVector fromVector, IAgCrdnVector toVector) { IAgCrdnAngleFactory factory = stkObject.Vgt.Angles.Factory; // Check if component exists. CheckExistingVgtComponent(stkObject.Vgt.Angles, name); IAgCrdnAngleBetweenVectors angle = factory.Create(name, "", AgECrdnAngleType.eCrdnAngleTypeBetweenVectors) as IAgCrdnAngleBetweenVectors; angle.FromVector.SetVector(fromVector); angle.ToVector.SetVector(toVector); return(angle as IAgCrdnAngle); }
public static object[] GetTimeOfMinAndValue(IAgCrdnVector vector) { IAgStkObject parentObject = StkAssistant.Root.GetObjectFromPath("*/" + (vector as IAgCrdn).QualifiedPath.Split(' ')[0]); //Create new scalar to monitor Vector Magnitude value string newVgtBaseName = (vector as IAgCrdn).Name; string scalarVectorMagName = StkAssistant.GetUniqueAWBName(newVgtBaseName, parentObject, AgECrdnKind.eCrdnKindCalcScalar); IAgCrdnCalcScalar vectorMagnitudeScalar = parentObject.Vgt.CalcScalars.Factory.Create( scalarVectorMagName, "temp", AgECrdnCalcScalarType.eCrdnCalcScalarTypeVectorMagnitude); (vectorMagnitudeScalar as IAgCrdnCalcScalarVectorMagnitude).InputVector = vector; IAgCrdnCalcScalar minRange = (vectorMagnitudeScalar as IAgCrdn).EmbeddedComponents[(vectorMagnitudeScalar as IAgCrdn).Name + ".Min"] as IAgCrdnCalcScalar; IAgCrdnEvent timeOfMinRange = (vectorMagnitudeScalar as IAgCrdn).EmbeddedComponents[(vectorMagnitudeScalar as IAgCrdn).Name + ".TimeOfMin"] as IAgCrdnEvent; string tca = timeOfMinRange.FindOccurrence().Epoch.ToString(); double minRangeValue = minRange.Evaluate(tca).Value; return(new object[] { tca, minRangeValue }); }
private void SetAlignedConstrainedAttitude(AgSatellite satellite, IAgCrdnVector alignedVector, string alignedAxis, IAgCrdnVector constrainedVector, string constrainedAxis) { // Set attitude to Aligned & Constrained IAgVeOrbitAttitudeStandard attitude = satellite.Attitude as IAgVeOrbitAttitudeStandard; attitude.Basic.SetProfileType(AgEVeProfile.eProfileAlignedAndConstrained); double[] alignedBody = GetCartesianArray(alignedAxis); double[] constrainedBody = GetCartesianArray(constrainedAxis); // Set vectors and body axis IAgVeProfileAlignedAndConstrained profile = attitude.Basic.Profile as IAgVeProfileAlignedAndConstrained; profile.AlignedVector.ReferenceVector = ((IAgCrdn)alignedVector).Path; profile.AlignedVector.Body.AssignXYZ(alignedBody[0], alignedBody[1], alignedBody[2]); profile.ConstrainedVector.ReferenceVector = ((IAgCrdn)constrainedVector).Path; profile.ConstrainedVector.Body.AssignXYZ(constrainedBody[0], constrainedBody[1], constrainedBody[2]); }
private IAgCrdnAxes CreateAxes(IAgStkObject stkObject, string name, IAgCrdnVector alignedVector, string alignedAxis, IAgCrdnVector constrainedVector, string constrainedAxis) { IAgCrdnAxesFactory factory = stkObject.Vgt.Axes.Factory; double[] alignedArray = GetCartesianArray(alignedAxis); double[] constrainedArray = GetCartesianArray(constrainedAxis); // Check if component exists. CheckExistingVgtComponent(stkObject.Vgt.Axes, name); IAgCrdnAxesAlignedAndConstrained axes = factory.Create(name, "", AgECrdnAxesType.eCrdnAxesTypeAlignedAndConstrained) as IAgCrdnAxesAlignedAndConstrained; axes.AlignmentReferenceVector.SetVector(alignedVector); axes.AlignmentDirection.AssignXYZ(alignedArray[0], alignedArray[1], alignedArray[2]); axes.ConstraintReferenceVector.SetVector(constrainedVector); axes.ConstraintDirection.AssignXYZ(constrainedArray[0], constrainedArray[1], constrainedArray[2]); return(axes as IAgCrdnAxes); }
public static IAgCrdnAngle CreateAngleBetweenVectors(IAgCrdnVector fromVector, IAgCrdnVector toVector, IAgStkObject parentObject, string name = "", bool addDisplay = false) { IAgCrdnAngleFactory angleFactory = parentObject.Vgt.Angles.Factory; string newAngleName; if (!String.IsNullOrEmpty(name)) { newAngleName = name; } else { newAngleName = "From_" + (fromVector as IAgCrdn).Name + "_To_" + (toVector as IAgCrdn).Name; } IAgCrdnAngleBetweenVectors angle; if (parentObject.Vgt.Angles.Contains(newAngleName)) { angle = parentObject.Vgt.Angles[newAngleName] as IAgCrdnAngleBetweenVectors; } else { angle = angleFactory.Create(newAngleName, newAngleName, AgECrdnAngleType.eCrdnAngleTypeBetweenVectors) as IAgCrdnAngleBetweenVectors; angle.FromVector.SetVector(fromVector); angle.ToVector.SetVector(toVector); } if (addDisplay) { StkAssistant.DisplayAngle(newAngleName, parentObject, System.Drawing.Color.Yellow); } return(angle as IAgCrdnAngle); }
private IAgCrdnVector GetVectorFromObject(IAgStkObject stkObject, string vectorName) { IAgCrdnVector vector = stkObject.Vgt.Vectors[vectorName]; return(vector); }
public static IAgCrdnAngleToPlane GetCreateAngleToPlane(IAgCrdnProvider vgtPrv, IAgCrdnPlane plane, IAgCrdnVector vector, string angleName, string description) { IAgCrdnAngleToPlane planeAngle; if (vgtPrv.Angles.Contains(angleName)) { planeAngle = (IAgCrdnAngleToPlane)vgtPrv.Angles[angleName]; } else { planeAngle = (IAgCrdnAngleToPlane)vgtPrv.Angles.Factory.Create(angleName, description, AgECrdnAngleType.eCrdnAngleTypeToPlane); } planeAngle.ReferencePlane.SetPlane(plane); planeAngle.ReferenceVector.SetVector(vector); planeAngle.Signed = AgECrdnSignedAngleType.eCrdnSignedAngleNone; return(planeAngle); }
private void CreateAttitudeButton_Click(object sender, EventArgs e) { try { progressBar.Visible = true; UpdateProgress(0); // Check inputs CheckUserInputs(); // Get inputs string alignedVectorName = alignedVectorComboBox.SelectedItem.ToString(); string constrainedVectorName = constrainedVectorComboBox.SelectedItem.ToString(); double angleLimit = Convert.ToDouble(angleLimitTextBox.Text); string alignedBodyAxis = GetBodyAxis(VectorType.eAligned); string constrainedBodyAxis = GetBodyAxis(VectorType.eConstrained); UserInputs inputs = new UserInputs { AlignedVectorName = alignedVectorName, AlignedBodyAxis = alignedBodyAxis, ConstrainedVectorName = constrainedVectorName, ConstrainedBodyAxis = constrainedBodyAxis, AngleLimit = angleLimit }; UpdateProgress(25); // Duplicate the satellite to create a reference sat. All AWB components are created on this reference sat to avoid circular logic. m_referenceSatellite = DuplicateObject(m_selectedObject); UpdateProgress(50); // Create AWB components from user inputs IAgCrdnVector scheduledVector = CreateAlignedScheduledVector(inputs); UpdateProgress(75); // Set final attitude profile IAgCrdnVector selectedConstrainedVector = GetVectorFromObject((IAgStkObject)m_selectedObject, constrainedVectorName); SetAlignedConstrainedAttitude(m_selectedObject, scheduledVector, alignedBodyAxis, selectedConstrainedVector, constrainedBodyAxis); if (showGraphicsCheckBox.Checked == true) { // Get AWB components to display IAgCrdnAxes selectedBodyAxes = m_selectedObject.Vgt.Axes["Body"]; IAgCrdnVector selectedConstrainedBodyVector = GetVectorFromAxes((IAgStkObject)m_selectedObject, selectedBodyAxes, constrainedBodyAxis); IAgCrdnVector selectedAlignedVector = GetVectorFromObject((IAgStkObject)m_selectedObject, alignedVectorName); IAgCrdnAngle displayAngle = CreateAngle((IAgStkObject)m_selectedObject, $"Off_{constrainedVectorName}", selectedConstrainedVector, selectedConstrainedBodyVector); // Display in 3D Graphics window. ShowAttitudeGraphics(selectedAlignedVector, selectedConstrainedVector, selectedBodyAxes, displayAngle); // Add negative body axis if necessary if (constrainedBodyAxis.Contains("-")) { IAgCrdnVector bodyVector = m_selectedObject.Vgt.Vectors[$"Body.{constrainedBodyAxis}"]; DisplayElement(AgEGeometricElemType.eVectorElem, (IAgCrdn)bodyVector, Color.RoyalBlue); } } UpdateProgress(100); } catch (Exception exception) { MessageBox.Show(exception.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } progressBar.Visible = false; }
public static IAgCrdnPlaneNormal GetCreatePlaneNormal(IAgCrdnProvider vgtPrv, IAgCrdnPoint Orgin, IAgCrdnVector RefVector, IAgCrdnVector NormalVector, string PlaneName, string description) { IAgCrdnPlaneNormal plane; if (vgtPrv.Planes.Contains(PlaneName)) { plane = (IAgCrdnPlaneNormal)vgtPrv.Planes[PlaneName]; } else { plane = (IAgCrdnPlaneNormal)vgtPrv.Planes.Factory.Create(PlaneName, description, AgECrdnPlaneType.eCrdnPlaneTypeNormal); } plane.NormalVector.SetVector(NormalVector); plane.ReferencePoint.SetPoint(Orgin); plane.ReferenceVector.SetVector(RefVector); return(plane); }
private IAgCrdnVector CreateVector(IAgStkObject stkObject, string name, IAgCrdnCondition satisfactionCondition, IAgCrdnVector onScheduleVector, IAgCrdnVector offScheduleVector) { // Check if component exists. CheckExistingVgtComponent(stkObject.Vgt.Vectors, name); // No Object Model for Scheduled Vector, so Connect command must be used. string commandString = $"VectorTool * {stkObject.ClassName}/{stkObject.InstanceName} Create Vector {name} \"Scheduled\" Schedule " + $"\"{((IAgCrdn)satisfactionCondition).Path}.SatisfactionIntervals\" OnSchedule \"{((IAgCrdn)onScheduleVector).Path}\" UseOffSchedule On" + $" OffSchedule \"{((IAgCrdn)offScheduleVector).Path}\" UseAdditionalCondition Off UseSlew Off"; try { CommonData.StkRoot.ExecuteCommand(commandString); } catch { throw new Exception($"Command failed while creating {name} Vector."); } return(stkObject.Vgt.Vectors[name]); }
public static IAgCrdnAngleBetweenVectors GetCreateAngleBetweenVectors(IAgCrdnProvider vgtPrv, IAgCrdnVector FromVector, IAgCrdnVector ToVector, string AngleName, string description) { IAgCrdnAngleBetweenVectors angle; if (vgtPrv.Angles.Contains(AngleName)) { angle = vgtPrv.Angles[AngleName] as IAgCrdnAngleBetweenVectors; } else { angle = (IAgCrdnAngleBetweenVectors)vgtPrv.Angles.Factory.Create(AngleName, description, AgECrdnAngleType.eCrdnAngleTypeBetweenVectors); } angle.FromVector.SetVector(FromVector); angle.ToVector.SetVector(ToVector); return(angle); }
private IAgCrdnVector CreateAlignedScheduledVector(UserInputs inputs) { // Unpack inputs string alignedVectorName = inputs.AlignedVectorName; string alignedBodyAxis = inputs.AlignedBodyAxis; string constrainedVectorName = inputs.ConstrainedVectorName; string constrainedBodyAxis = inputs.ConstrainedBodyAxis; double angleLimit = inputs.AngleLimit; // Get vectors from user input IAgCrdnVector duplicateAlignedVector = GetVectorFromObject((IAgStkObject)m_referenceSatellite, alignedVectorName); IAgCrdnVector duplicateConstrainedVector = GetVectorFromObject((IAgStkObject)m_referenceSatellite, constrainedVectorName); // Make sure vectors are valid CheckInputVector(duplicateAlignedVector); CheckInputVector(duplicateConstrainedVector); // Create Body vector string for naming string constrainedBodyVectorName = $"Body.{constrainedBodyAxis}"; // Get body vector IAgCrdnAxes duplicateBodyAxes = m_referenceSatellite.Vgt.Axes["Body"]; IAgCrdnVector duplicateConstrainedBodyVector = GetVectorFromAxes((IAgStkObject)m_referenceSatellite, duplicateBodyAxes, constrainedBodyAxis); // Set duplicate satellite's attitude to Aligned & Constrained with user input vectors. This is the unconstrained attitude that our // final satellite's attitude is based on. SetAlignedConstrainedAttitude(m_referenceSatellite, duplicateAlignedVector, alignedBodyAxis, duplicateConstrainedVector, constrainedBodyAxis); // Create angle from constraint vector to specified body axis. Set condition to find when this angle exceeds user limit IAgCrdnAngle constraintToBodyAngle = CreateAngle((IAgStkObject)m_referenceSatellite, $"{constrainedVectorName}To{constrainedBodyVectorName}", duplicateConstrainedVector, duplicateConstrainedBodyVector); IAgCrdnCalcScalar constraintToBodyScalar = CreateScalar((IAgStkObject)m_referenceSatellite, $"{constrainedVectorName}To{constrainedBodyVectorName}Value", constraintToBodyAngle); IAgCrdnCondition constraintToBodyCondition = CreateCondition((IAgStkObject)m_referenceSatellite, $"{constrainedVectorName}To{constrainedBodyVectorName}Condition", constraintToBodyScalar, AgECrdnConditionThresholdOption.eCrdnConditionThresholdOptionBelowMax, angleLimit); // Create angle from constraint vector to aligned vector and set condition from 0 to 90 deg. This is because the attitude profile is flipped // if the angle is between 90 and 180 deg. IAgCrdnAngle constraintToAlignedAngle = CreateAngle((IAgStkObject)m_referenceSatellite, $"{constrainedVectorName}To{alignedVectorName}", duplicateConstrainedVector, duplicateAlignedVector); IAgCrdnCalcScalar constraintToAlignedScalar = CreateScalar((IAgStkObject)m_referenceSatellite, $"{constrainedVectorName}To{alignedVectorName}Value", constraintToAlignedAngle); IAgCrdnCondition constraintToAlignedCondition = CreateCondition((IAgStkObject)m_referenceSatellite, $"{constrainedVectorName}To{alignedVectorName}Condition", constraintToAlignedScalar, AgECrdnConditionThresholdOption.eCrdnConditionThresholdOptionInsideMinMax, 0, 90); // Create axes aligned with the constraint vector and constrained with the aligned vector. IAgCrdnAxes alignedConstrainedAxes = CreateAxes((IAgStkObject)m_referenceSatellite, $"{constrainedVectorName}Axes", duplicateConstrainedVector, constrainedBodyAxis, duplicateAlignedVector, alignedBodyAxis); // Rotate the new axes by both the positive and negative Angle Offset value. IAgCrdnAxes positiveRotatedAxes = CreateAxes((IAgStkObject)m_referenceSatellite, $"{constrainedVectorName}Axes_RotatedPositive", alignedConstrainedAxes, alignedBodyAxis, constrainedBodyAxis, angleLimit); IAgCrdnAxes negativeRotatedAxes = CreateAxes((IAgStkObject)m_referenceSatellite, $"{constrainedVectorName}Axes_RotatedNegative", alignedConstrainedAxes, alignedBodyAxis, constrainedBodyAxis, -angleLimit); // Create scheduled axes IAgCrdnAxes scheduledAxes; double[] alignedAxisVector = GetCartesianArray(alignedBodyAxis); double[] constrainedAxisVector = GetCartesianArray(constrainedBodyAxis); // If the cross product vector of the aligned body axis and constrained body axis is positive, the axes need to be // positively rotated when the angle between the aligned vector and constrained vector is between 0 deg and 90 deg. // If the angle is between 90 and 180, the axes need to be negatively rotated. This is flipped if the cross product // is negative. if (NormalVectorIsPositive(alignedAxisVector, constrainedAxisVector)) { scheduledAxes = CreateAxes((IAgStkObject)m_referenceSatellite, $"{constrainedVectorName}Axes_Scheduled", constraintToAlignedCondition, positiveRotatedAxes, negativeRotatedAxes); } else { scheduledAxes = CreateAxes((IAgStkObject)m_referenceSatellite, $"{constrainedVectorName}Axes_Scheduled", constraintToAlignedCondition, negativeRotatedAxes, positiveRotatedAxes); } // Create scheduled vector to point along alignment vector when constraint is not broken, and hold at constraint when violated. IAgCrdnVector alignedScheduledAxis = GetVectorFromAxes((IAgStkObject)m_referenceSatellite, scheduledAxes, alignedBodyAxis); IAgCrdnVector scheduledVector = CreateVector((IAgStkObject)m_referenceSatellite, $"{alignedVectorName}Scheduled", constraintToBodyCondition, duplicateAlignedVector, alignedScheduledAxis); return(scheduledVector); }