1. This forum is obsolete and read-only. Feel free to contact us at support.keenswh.com

Solar Compass - or "What's my heading?"

Discussion in 'Programming Released Codes' started by Me 10 Jin, Apr 12, 2016.

Thread Status:
This last post in this thread was made more than 31 days old.
  1. Me 10 Jin

    Me 10 Jin Apprentice Engineer

    Messages:
    463
    https://steamcommunity.com/sharedfiles/filedetails/?id=663958680

    Version 2 includes elevation and roll in addition to heading and the compass ticks. The code itself is vastly improved as well, so I'm including it below for anyone interested.

    [​IMG]

    Code:
    /*
       Space Engineers - Programmable Block Script
       True North Compass - by Me 10 Jin
    
       Copyright © 2016 Me 10 Jin. All rights reserved.
    
       Finds the heading, elevation, and roll of a remote control block and
       displays that info on a LCD or text panel with scrolling compass ticks.
    
       Blocks Required (all with the same owner):
        1 Timer
        1 Programmable Block
        1 Remote Control
        1 LCD or Text Panel (Show public text on screen)
    
       Setup:
       Create a block group containing one or more LCD panel blocks and ONE remote
       control block oriented so it's front and top are aligned with the ship.
       Copy that group name into the "Argument" box of the programmable block.
       Load this script into that programmable block, click "Check code" then
       "Remember & exit". Then configure a timer "Run with default argument" the
       programmable block and "Start" itself, then start the timer.
    
       You may optionally add "Trigger Now" to the timer to get a much smoother
       refresh rate on the display.
    
       A known bug in the game (as of 2016-04-20) prevents LCD panels refreshing.
       Add "Increase font size" and "Decrease font size" to the timer to force it
       to update in this case.
    
    
       Sun (Azimuth,Elevation) Defaults:
       Empty World: (-0.502843, 0.789207757)
       Star System: (3.14159274, 0.0)
    
    */
    
    public static class True_North_Compass_by_Me_10_Jin
    {
       public static double sunAzimuth = 3.14159274;
       public static double sunElevation = 0;
    
       /// Finds the remote control's forward direction as an azimuth relative
       /// to local north and elevation relative to natural gravity.
       public static bool getOrientationToNaturalGravity(
         IMyRemoteControl remote,
         out double azimuth,
         out double elevation,
         out double roll )
       {
         double unused;
         MatrixD rotationMatrix;
         Vector3D solarAxis, northDir, downDir, frontDir, naturalGravity;
    
         naturalGravity = remote.GetNaturalGravity();
    
         if ( naturalGravity == Vector3D.Zero )
         {
           roll = 0.0;
           azimuth = 0.0;
           elevation = 0.0;
           return false;
         }
         else
         {
           Vector3D.CreateFromAzimuthAndElevation( sunAzimuth, sunElevation + Math.PI / 2.0, out solarAxis );
    
           northDir = naturalGravity.Cross( solarAxis.Cross( naturalGravity ) ); /// Down cross West = North
           downDir = getBlockDirectionVector( remote, Base6Directions.Direction.Down );
           frontDir = getBlockDirectionVector( remote, Base6Directions.Direction.Forward );
    
           rotationMatrix = MatrixD.Invert( MatrixD.CreateWorld( Vector3D.Zero, northDir, naturalGravity ) );
           Vector3D.GetAzimuthAndElevation( Vector3D.Transform( -frontDir, rotationMatrix ), out azimuth, out elevation );
    
           rotationMatrix = MatrixD.Invert( MatrixD.CreateWorld( Vector3D.Zero, naturalGravity, frontDir ) );
           Vector3D.GetAzimuthAndElevation( Vector3D.Transform( downDir, rotationMatrix ), out roll, out unused );
    
           return true;
         }
       }
    
       /// Returns a normalized world vector indicating the direction of the block's given face.
       public static Vector3D getBlockDirectionVector( IMyCubeBlock block, Base6Directions.Direction face )
       {
         Vector3D dir;
    
         switch( face )
         {
           case Base6Directions.Direction.Forward:
             dir = new Vector3D( -block.WorldMatrix.GetRow(2) );
           break;
    
           case Base6Directions.Direction.Backward:
             dir = new Vector3D( block.WorldMatrix.GetRow(2) );
           break;
    
           case Base6Directions.Direction.Left:
             dir = new Vector3D( -block.WorldMatrix.GetRow(0) );
           break;
    
           case Base6Directions.Direction.Right:
             dir = new Vector3D( block.WorldMatrix.GetRow(0) );
           break;
    
           case Base6Directions.Direction.Up:
             dir = new Vector3D( block.WorldMatrix.GetRow(1) );
           break;
    
           case Base6Directions.Direction.Down:
             dir = new Vector3D( -block.WorldMatrix.GetRow(1) );
           break;
    
           default:
             dir = Vector3D.Zero;
           break;
         }
    
         dir.Normalize();
         return dir;
       }
    }
    
    /// Customize this string as desired. Heck, go balls deep and rework the entire output
    /// system. If you make a full on flight control GUI, send me the workshop link plz!
    string compassString = "       sw       W       nw       N        ne        E        se        S       sw       W       nw       N        ne        E        se        S";
    
    /// How many characters from the string, on average, fit on one line of a LCD at 1.0 font size?
    double compassStringCharDensity = 33.0;
    
    StringBuilder outText = new StringBuilder(250);
    
    ///
    public void Main(string argument)
    {
       int i, halfWidth, stringHeading;
       double fontSize, heading, pitch, azimuth, elevation, roll;
    
       List<IMyTextPanel> displays = null;
       IMyRemoteControl remote = null;
    
       IMyBlockGroup group = GridTerminalSystem.GetBlockGroupWithName( argument );
    
       if ( group == null )
       {
         this.Echo( "Error: group \"" + argument + "\" not found.\nDedicated server note: use \"Run\" instead\nof \"Run with default argument\" on the\ntimer and provide the group name there." );
       }
       else
       {
         displays = new List<IMyTextPanel>(group.Blocks.Count - 1);
    
         for ( i = group.Blocks.Count - 1; 0 <= i; i-- )
         {
           if ( group.Blocks[i] is IMyRemoteControl )
             remote = group.Blocks[i] as IMyRemoteControl;
    
           if ( group.Blocks[i] is IMyTextPanel )
             displays.Add( group.Blocks[i] as IMyTextPanel );
         }
    
         if ( displays.Count > 0 && remote != null )
         {
           if ( True_North_Compass_by_Me_10_Jin.getOrientationToNaturalGravity( remote, out azimuth, out elevation, out roll ) )
           {
             // A proper heading is >= 0 and < 360
             heading = ( 360 + azimuth * 180 / Math.PI ) % 360.0;
             pitch = elevation * 180 / Math.PI;
             roll = roll * 180 / Math.PI;
    
             for ( i = displays.Count - 1; 0 <= i; i-- )
             {
               fontSize = displays[i].GetValueFloat( "FontSize" );
               if ( displays[i].DisplayNameText.StartsWith( "Wide " ) )
                 fontSize *= 0.5;
    
             // Since we want the compass marks centered in the screen, we guess
             // the screen's width in characters. Large fontsize looks ugly though.
               halfWidth = (int) Math.Round( compassStringCharDensity / fontSize );
               stringHeading = (int) Math.Round( ( 0.5 + heading / 360.0 ) * compassString.Length / 2.0 );
    
               outText.Clear();
    
               outText.Append( compassString.Substring( stringHeading - halfWidth, halfWidth * 2 ) );
               outText.Append( "\n  Heading : " );
               outText.Append( Math.Round(heading, 1).ToString() );
               outText.Append( "°\n  Pitch    : " );
               outText.Append( Math.Round(pitch, 0).ToString() );
               outText.Append( "°\n  Roll     : " );
               outText.Append( Math.Round(roll, 0).ToString() );
               outText.Append( "°" );
    
               displays[i].WritePublicText( outText.ToString() );
             }
           }
           else
           {
             outText.Clear();
             outText.Append( "Error" );
    
             for ( i = displays.Count - 1; 0 <= i; i-- )
             {
               displays[i].WritePublicText( outText.ToString() );
             }
           }
         }
       }
    }
     
    Last edited: May 2, 2016
    • Like Like x 3
Thread Status:
This last post in this thread was made more than 31 days old.