예제 #1
0
        private void ProxGeometry_CheckedChanged(object sender, EventArgs e)
        {
            IAgSatellite sat    = CommonData.StkRoot.GetObjectFromPath("Satellite/" + CommonData.TargetName) as IAgSatellite;
            IAgStkObject satObj = sat as IAgStkObject;

            if (ProxGeometry.Checked)
            {
                if (CommonData.RunList[0].IsSpherical)
                {
                    sat.VO.Proximity.Ellipsoid.IsVisible = true;
                    IAgCrdnAxes axes = satObj.Vgt.Axes["RIC"];
                    sat.VO.Proximity.Ellipsoid.ReferenceFrame  = axes;
                    sat.VO.Proximity.Ellipsoid.XSemiAxisLength = CommonData.RunList[0].UserMinRange;
                    sat.VO.Proximity.Ellipsoid.YSemiAxisLength = CommonData.RunList[0].UserMinRange;
                    sat.VO.Proximity.Ellipsoid.ZSemiAxisLength = CommonData.RunList[0].UserMinRange;
                }
                else
                {
                    sat.VO.Proximity.ControlBox.IsVisible = true;
                    IAgCrdnAxes axes = satObj.Vgt.Axes["RIC"];
                    sat.VO.Proximity.ControlBox.ReferenceFrame = axes;
                    sat.VO.Proximity.ControlBox.XAxisLength    = CommonData.RunList[0].UserMinR;
                    sat.VO.Proximity.ControlBox.YAxisLength    = CommonData.RunList[0].UserMinI;
                    sat.VO.Proximity.ControlBox.ZAxisLength    = CommonData.RunList[0].UserMinC;
                }
            }
            else
            {
                sat.VO.Proximity.Ellipsoid.IsVisible  = false;
                sat.VO.Proximity.ControlBox.IsVisible = false;
            }
        }
예제 #2
0
        public void MoonAction()
        {
            IAgStkGraphicsSceneManager manager = ((IAgScenario)m_Root.CurrentScenario).SceneManager;
            IAgCrdnAxes moonAxes = ((IAgCrdnSystemAssembled)m_Root.CentralBodies["Moon"].Vgt.Systems["ICRF"]).ReferenceAxes.GetAxes();

            if (manager.Scenes.Count > 0)
            {
                manager.Scenes[0].Camera.ViewCentralBody("Moon", moonAxes);
            }
            m_Panning = false;
        }
예제 #3
0
 private IAgCrdnVector GetVectorFromAxes(IAgStkObject stkObject, IAgCrdnAxes axes, string axis)
 {
     // Get axis by name
     try
     {
         return(stkObject.Vgt.Vectors[$"{((IAgCrdn)axes).Name}.{axis}"]);
     }
     catch
     {
         throw new Exception("Unable to get axis vector.");
     }
 }
예제 #4
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);
        }
예제 #5
0
        private IAgCrdnAxes CreateAxes(IAgStkObject stkObject, string name, IAgCrdnAxes referenceAxes, string alignedAxis, string constrainedAxis, double angle)
        {
            IAgCrdnAxesFactory factory = stkObject.Vgt.Axes.Factory;

            double[] eulerAngles = GetEulerArray(alignedAxis, constrainedAxis, angle);

            // Check if component exists.
            CheckExistingVgtComponent(stkObject.Vgt.Axes, name);

            IAgCrdnAxesFixed axes = factory.Create(name, "", AgECrdnAxesType.eCrdnAxesTypeFixed) as IAgCrdnAxesFixed;

            axes.ReferenceAxes.SetAxes(referenceAxes);
            axes.FixedOrientation.AssignEulerAngles(AgEEulerOrientationSequence.e123, eulerAngles[0], eulerAngles[1], eulerAngles[2]);

            return(axes as IAgCrdnAxes);
        }
예제 #6
0
        public void Orbit_generation()
        {
            //load_orbit_file();
            scenarioCheck();
            var format = new NumberFormatInfo();

            format.NegativeSign           = "-";
            format.NumberDecimalSeparator = ".";
            // planetodetic.Lat = Double.Parse(lat2[i], format);

            for (int i = 0; i < orbitmissioncount; i++)
            {
                //debug infomation///////
                if (orbitdata[i].misnum == orbitdata[i].cod_id && orbitdata[i].used == 1 && orbitdata[i].efileused == false)
                {
                    //generate the timeline file
                    TL_file_generator(orbitdata[i].name, orbitdata[i].name);

                    Console.Write("Misnum == cod_id\n");

                    Console.Write("Mission= " + orbitdata[i].name + "\n" + orbitdata[i].epoch + " " + orbitdata[i].epoch_time + " ");
                    Console.Write(orbitdata[i].sma + " " + orbitdata[i].ecc + " " + orbitdata[i].inc + " " + orbitdata[i].raan + " " + orbitdata[i].aop + " " + orbitdata[i].ma + "\n");

                    string centerbodyname;
                    if (orbitdata[i].centerbody == 1)
                    {
                        centerbodyname = "Earth";
                    }
                    else if (orbitdata[i].centerbody == 2)
                    {
                        centerbodyname = "Moon";
                    }
                    else
                    {
                        centerbodyname = "Earth";
                    }


                    // AGI.STKObjects.IAgSatellite sat = (IAgSatellite)m_oApplication.CurrentScenario.Children.New(AGI.STKObjects.AgESTKObjectType.eSatellite, mission2[i]);
                    //sat.SetPropagatorType(AGI.STKObjects.AgEVePropagatorType.ePropagatorTwoBody);

                    try
                    {
                        //create a new sat with the cod orbit details;
                        orbitdata[i].MisSat = (IAgSatellite)m_oApplication.CurrentScenario.Children.NewOnCentralBody(AGI.STKObjects.AgESTKObjectType.eSatellite, orbitdata[i].name, centerbodyname);
                        //AGI.STKObjects.IAgSatellite sat = (IAgSatellite)m_oApplication.CurrentScenario.Children.NewOnCentralBody(AGI.STKObjects.AgESTKObjectType.eSatellite, orbitdata[i].name, centerbodyname);
                    }catch
                    {
                        //sat already exists reset the scenario;
                        this.m_oApplication.CloseScenario();
                        scenarioCheck();
                        Orbit_generation();
                        return;
                    }
                    //disable the leading ground track
                    groundtrack_set(orbitdata[i].MisSat, groundtrack_displayed);

                    //set the propagator type to HPOP
                    orbitdata[i].MisSat.SetPropagatorType(AGI.STKObjects.AgEVePropagatorType.ePropagatorHPOP);

                    //TODO update below code to use sat not hpop;
                    AGI.STKObjects.IAgVePropagatorHPOP hpop = (AGI.STKObjects.IAgVePropagatorHPOP)orbitdata[i].MisSat.Propagator;

                    IAgOrbitState orbit = hpop.InitialState.Representation;
                    //create the string to hold the missions epoch date & time
                    string cmb_epoch = orbitdata[i].epoch;
                    cmb_epoch += " ";
                    cmb_epoch += orbitdata[i].epoch_time;
                    DateTime epochDT = Convert.ToDateTime(orbitdata[i].epoch);
                    DateTime startDT = Convert.ToDateTime(orbitdata[i].start_date);
                    DateTime endDT;

                    if (orbitdata[i].endopt == 0)
                    {
                        endDT = startDT;
                        endDT = endDT.AddDays((double)orbitdata[i].duration);
                        orbitdata[i].end_time = orbitdata[i].start_time;
                    }
                    else
                    {
                        endDT = Convert.ToDateTime(orbitdata[i].end_date);
                    }

                    //hpop.ForceModel.EclipsingBodies.AssignEclipsingBody( centerbodyname);
                    //check the centerbody of the provided orbit data;
                    if (orbitdata[i].centerbody == 1)
                    {// centerbody == EARTH
                        Console.Write("centerbody2[i]) == 1 \n");
                        Console.Write("\n Centralbodyfile=" + hpop.ForceModel.CentralBodyGravity.File + "\n");

                        hpop.ForceModel.Drag.Use = false;
                        hpop.ForceModel.SolarRadiationPressure.Use = false;
                    }
                    else if (orbitdata[i].centerbody == 2)
                    {//CenterBody == Moon
                        Console.Write("centerbody2[i]) == 2 \n");
                        hpop.ForceModel.Drag.Use = false;
                        hpop.ForceModel.SolarRadiationPressure.Use = false;

                        Console.Write("\n Centralbodyfile=" + hpop.ForceModel.CentralBodyGravity.File + "\n");
                        //change gravity file for HPOP use with the moon
                        hpop.ForceModel.CentralBodyGravity.File = "STKData\\CentralBodies\\Moon\\LP100K.grv";
                    }
                    //hpop.InitialState.Representation.Assign(orbit);
                    Console.Write("set epochtime = " + epochDT.ToString("dd MMM yyyy ") + orbitdata[i].epoch_time + "\n");
                    //set the epoch date/time from the orbit bin file
                    hpop.InitialState.Representation.Epoch = (epochDT.ToString("dd MMM yyyy ") + orbitdata[i].epoch_time);

                    Console.Write("set start/stop times = " + (startDT.ToString("dd MMM yyyy ") + orbitdata[i].start_time) + " / " + startDT.AddDays(1).ToString("dd MMM yyyy ") + "\n");
                    //only proagate the orbit 1 day from the start date/time to get the required data to convert to a Fixed coordnate system
                    hpop.EphemerisInterval.SetStartAndStopTimes((startDT.ToString("dd MMM yyyy ") + orbitdata[i].start_time), startDT.AddDays(1).ToString("dd MMM yyyy "));
                    //hpop.EphemerisInterval.SetStartAndStopTimes((startDT.ToString("dd MMM yyyy ") + orbitdata[i].start_time), (endDT.ToString("dd MMM yyyy ") + orbitdata[i].end_time));
                    hpop.InitialState.Representation.AssignClassical(AgECoordinateSystem.eCoordinateSystemJ2000, orbitdata[i].sma, orbitdata[i].ecc, orbitdata[i].inc, orbitdata[i].aop, orbitdata[i].raan, orbitdata[i].ma);

                    hpop.Propagate();
                    //check if the orbit is earth centered and that the
                    //     orbit start date is different from the Mission model date
                    if (orbitdata[i].centerbody == 1 && orbitdata[i].start_date != startdate)
                    {
                        Console.Write("Startdate != orbit_start date converting to Cartesian elements for fixed system" + "\n");

                        IAgStkObject sat = m_oApplication.CurrentScenario.Children[orbitdata[i].name];

                        // Get the satellite's ICRF cartesian position at 180 EpSec using the data provider interface
                        IAgDataProviderGroup dpGroup = sat.DataProviders["Cartesian Position"] as IAgDataProviderGroup;
                        Array elements = new object[] { "x", "y", "z" };
                        //***TODO*** find J2000 group instead of ICRF
                        IAgDataPrvTimeVar dp = dpGroup.Group["ICRF"] as IAgDataPrvTimeVar;

                        //get the elements at the start date/time of the orbit using in NPAS
                        IAgDrResult dpResult = dp.ExecSingleElements(hpop.StartTime, ref elements);

                        Console.Write("hpop.startTime=" + hpop.StartTime + "\n");


                        double xICRF = (double)dpResult.DataSets[0].GetValues().GetValue(0);
                        double yICRF = (double)dpResult.DataSets[1].GetValues().GetValue(0);
                        double zICRF = (double)dpResult.DataSets[2].GetValues().GetValue(0);

                        // Get the satellite's ICRF cartesian velocity at 180 EpSec using the data provider interface
                        dpGroup = sat.DataProviders["Cartesian Velocity"] as IAgDataProviderGroup;

                        //***TODO*** find J2000 group instead of ICRF
                        dp = dpGroup.Group["ICRF"] as IAgDataPrvTimeVar;

                        //get the elements at the start date/time of the orbit using in NPAS
                        dpResult = dp.ExecSingleElements(hpop.StartTime, ref elements);

                        double xvelICRF = (double)dpResult.DataSets[0].GetValues().GetValue(0);
                        double yvelICRF = (double)dpResult.DataSets[1].GetValues().GetValue(0);
                        double zvelICRF = (double)dpResult.DataSets[2].GetValues().GetValue(0);

                        Console.Write("J2000 cartesian vectors\n");
                        Console.Write("X=" + xICRF + " Y=" + yICRF + " Z=" + zICRF + "\n");
                        Console.Write("Xd=" + xvelICRF + " Yd=" + yvelICRF + " Zd=" + zvelICRF + "\n");

                        // Create a position vector using the ICRF coordinates
                        IAgCrdnAxes         axesICRF   = sat.Vgt.WellKnownAxes.Earth.ICRF;
                        IAgCartesian3Vector vectorICRF = m_oApplication.ConversionUtility.NewCartesian3Vector();

                        vectorICRF.Set(xICRF, yICRF, zICRF);

                        // Create a velocity vector using the ICRF coordinates
                        IAgCartesian3Vector vectorvelICRF = m_oApplication.ConversionUtility.NewCartesian3Vector();
                        vectorvelICRF.Set(xvelICRF, yvelICRF, zvelICRF);


                        // Use the TransformWithRate method to transform ICRF to Fixed
                        IAgCrdnAxes axesFixed = sat.Vgt.WellKnownAxes.Earth.Fixed;
                        IAgCrdnAxesTransformWithRateResult result = axesICRF.TransformWithRate(hpop.StartTime, axesFixed, vectorICRF, vectorvelICRF);

                        // Get the Fixed position and velocity coordinates
                        double xFixed    = result.Vector.X;
                        double yFixed    = result.Vector.Y;
                        double zFixed    = result.Vector.Z;
                        double xvelFixed = result.Velocity.X;
                        double yvelFixed = result.Velocity.Y;
                        double zvelFixed = result.Velocity.Z;

                        Console.Write("converted cartesian vectors" + "\n");
                        Console.Write("X=" + xFixed + " Y=" + yFixed + " Z=" + zFixed + "\n");
                        Console.Write("Xd=" + xvelFixed + " Yd=" + yvelFixed + " Zd=" + zvelFixed + "\n");

                        DateTime start, stop;
                        start = Convert.ToDateTime(startdate);
                        stop  = Convert.ToDateTime(enddate);

                        //set the epoch and start date/time to the selected scenario date/time
                        hpop.InitialState.Representation.Epoch = (start.ToString("dd MMM yyyy "));
                        hpop.EphemerisInterval.SetStartAndStopTimes((start.ToString("dd MMM yyyy ")), (stop.ToString("dd MMM yyyy ")));

                        hpop.InitialState.Representation.AssignCartesian(AgECoordinateSystem.eCoordinateSystemFixed, xFixed, yFixed, zFixed, xvelFixed, yvelFixed, zvelFixed);

                        hpop.InitialState.Representation.Epoch = (start.ToString("dd MMM yyyy "));
                        hpop.EphemerisInterval.SetStartAndStopTimes((start.ToString("dd MMM yyyy ")), (stop.ToString("dd MMM yyyy ")));

                        //propagate new orbit
                        hpop.Propagate();
                    }

                    orbitdata[i].Missensor = generate_sensor(orbitdata[i].name, "Stations");
                    orbitdata[i].MisChain  = generate_chain(orbitdata[i].name, "Stations", orbitdata[i].name + "_sensor");
                }
                else if (orbitdata[i].used == 1 && orbitdata[i].efileused == false)
                {
                    //generate the missions timeline file inside the orignial cod mis name
                    //don't generate multiple orbits on top of each other;
                    TL_file_generator(orbitdata[i].name, missionindex[orbitdata[i].cod_id]);
                    orbitdata[i].MisChain = null;
                    Console.Write("Misnum != cod_id && orbitdata[i].used == 1 for " + orbitdata[i].name + "\n");
                }
                else if (orbitdata[i].used == 1 && orbitdata[i].efileused == true)
                {
                    TL_file_generator(orbitdata[i].name, missionindex[orbitdata[i].cod_id]);

                    orbitdata[i].MisSat = (IAgSatellite)m_oApplication.CurrentScenario.Children.New(AGI.STKObjects.AgESTKObjectType.eSatellite, orbitdata[i].name);
                    //disable the leading ground track
                    groundtrack_set(orbitdata[i].MisSat, groundtrack_displayed);

                    //set the propagator type to STK E file
                    orbitdata[i].MisSat.SetPropagatorType(AGI.STKObjects.AgEVePropagatorType.ePropagatorStkExternal);

                    AGI.STKObjects.IAgVePropagatorStkExternal EFileProp = (AGI.STKObjects.IAgVePropagatorStkExternal)orbitdata[i].MisSat.Propagator;

                    //strip the file name from the entire director name;
                    EFileProp.Filename = (Efile_directory + orbitdata[i].efilename.Split('/').Last().ToString());
                    //propagate the efile orbit
                    EFileProp.Propagate();

                    //add new sensor to the current sat and change the sensor type to target
                    orbitdata[i].Missensor = generate_sensor(orbitdata[i].name, "Stations");

                    //add a new chain for the current sat
                    orbitdata[i].MisChain = generate_chain(orbitdata[i].name, "Stations", orbitdata[i].name + "_sensor");
                }
                else
                {
                    orbitdata[i].MisChain = null;
                    Console.Write("Misnum != cod_id for " + orbitdata[i].name + "\n");
                }
            }
        }
예제 #7
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;
        }
예제 #8
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);
        }
예제 #9
0
        private IAgCrdnAxes CreateAxes(IAgStkObject stkObject, string name, IAgCrdnCondition satisfactionCondition, IAgCrdnAxes onScheduleAxes, IAgCrdnAxes offScheduleAxes)
        {
            // Check if component exists.
            CheckExistingVgtComponent(stkObject.Vgt.Axes, name);

            // No Object Model for Scheduled Axes, so Connect command must be used.
            string commandString = $"VectorTool * {stkObject.ClassName}/{stkObject.InstanceName} Create Axes {name} \"Scheduled\" Schedule" +
                                   $" \"{((IAgCrdn)satisfactionCondition).Path}.SatisfactionIntervals\" OnSchedule \"{((IAgCrdn)onScheduleAxes).Path}\"" +
                                   $" UseOffSchedule On OffSchedule \"{((IAgCrdn)offScheduleAxes).Path}\" UseAdditionalCondition Off UseSlew Off";

            try
            {
                CommonData.StkRoot.ExecuteCommand(commandString);
            }
            catch
            {
                throw new Exception($"Command failed while creating {name} Axes.");
            }

            return(stkObject.Vgt.Axes[name]);
        }
예제 #10
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);
        }