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));
        }
예제 #2
0
 private void CheckInputVector(IAgCrdnVector inputVector)
 {
     if (VectorIsValid(inputVector) == false)
     {
         throw new Exception($"{((IAgCrdn)inputVector).Name} Vector is not valid.");
     }
 }
예제 #3
0
        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));
        }
예제 #4
0
        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));
        }
예제 #5
0
        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 });
        }
예제 #6
0
        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);
                }
            }
        }
예제 #7
0
        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);
        }
예제 #8
0
        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);
        }
예제 #9
0
        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);
        }
예제 #10
0
        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 });
        }
예제 #11
0
        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]);
        }
예제 #12
0
        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);
        }
예제 #13
0
        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);
        }
예제 #14
0
        private IAgCrdnVector GetVectorFromObject(IAgStkObject stkObject, string vectorName)
        {
            IAgCrdnVector vector = stkObject.Vgt.Vectors[vectorName];

            return(vector);
        }
예제 #15
0
        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);
        }
예제 #16
0
        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;
        }
예제 #17
0
        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);
        }
예제 #18
0
        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]);
        }
예제 #19
0
        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);
        }
예제 #20
0
        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);
        }