Overview Package Class Source Class tree Glossary
previous class      next class frames      no frames

USARBot.UnderwaterRobot


00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
class UnderwaterRobot extends NauticVehicle config(USARBot);

// Structure to hold both the part index and the part's force
struct PartData
{
    var int PartNumber;
    var float Force;
};

// Configuration variable to be set in the defaultproperties section of the vehicle's class
var float waterDensity;

// Programming variables (these variables are transparent to the users)
var float currentVel, totalVel, currentTime, oldTime;
var bool atSurface, initialized;
var array<PartData> Propellers, Rudders, SternPlanes;

function ProcessCarInput()
{
    // Programming variables
    local int i;
    local float maxSpeed, maxAngle, ControllerSpeed, ControllerRudder, ControllerSternPlane, InputSpeed, InputRudder, InputSternPlane;
    local bool isCommandNormalized;
    
    Super.ProcessCarInput();

    // Initializes various variables and fills up arrays. Note: this section is only executed once.
    if(!initialized)
    {
        // Here, we get the propeller(s), rudder(s), and stern plane(s) into separate dynamic arrays
        for(i=0; i<JointParts.length; i++)
        {
            // Get propeller(s)
            if(InStr(Caps(string(JointParts[i].PartName)), "PROPELLER") != -1)
            {
                Propellers.Insert(Propellers.length, 1);        // Make space in the dynamic array to add a propeller
                Propellers[Propellers.length-1].PartNumber = i; // Store the index number of the part in the dynamic array
            }

            // Get rudder(s)
            if(InStr(Caps(string(JointParts[i].PartName)), "RUDDER") != -1)
            {
                Rudders.Insert(Rudders.length, 1);        // Make space in the dynamic array to add a rudder
                Rudders[Rudders.length-1].PartNumber = i; // Store the index number of the part in the dynamic array
            }

            // Get stern plane(s)
            if(InStr(Caps(string(JointParts[i].PartName)), "STERNPLANE") != -1)
            {
                SternPlanes.Insert(SternPlanes.length, 1);        // Make space in the dynamic array to add a stern plane
                SternPlanes[SternPlanes.length-1].PartNumber = i; // Store the index number of the part in the dynamic array
            }
        }

        // Initialize the controller's properties
        USARRemoteBot(Controller).Normalized = false;
        USARRemoteBot(Controller).Propeller = 0.0;
        USARRemoteBot(Controller).Rudder = 0.0;
        USARRemoteBot(Controller).SternPLane = 0.0;
        initialized = true;
        
        // Section used for debugging purposes to see if the correct joints have been saved in the dynamic arrays
        if(bDebug)
        {
            for(i=0; i<Propellers.Length; i++) Log("Propeller #" $ i+1 $ ": " $ Propellers[i].PartNumber);
            for(i=0; i<Rudders.Length; i++) Log("Rudder #" $ i+1 $ ": " $ Rudders[i].PartNumber);
            for(i=0; i<SternPlanes.Length; i++) Log("Stern Plane #" $ i+1 $ ": " $ SternPlanes[i].PartNumber);
        }
    }

    currentTime = Level.TimeSeconds;

    // If a DRIVE command was issued
    if(USARRemoteBot(Controller).bNewThrottle)
    {
        // If the underwater robot is underwater (not at the surface)
        if(!atSurface)
        {
            isCommandNormalized = USARRemoteBot(Controller).Normalized;  // Get the normalized bool from the controller
            ControllerSpeed = USARRemoteBot(Controller).Propeller;       // Get the propeller's spin speed value from the controller
            ControllerRudder = USARRemoteBot(Controller).Rudder;         // Get the rudder angle from the controller
            ControllerSternPlane = USARRemoteBot(Controller).SternPlane; // Get the stern plane angle from the controller

            // Here, we deal with the propeller(s)
            for(i=0; i<Propellers.Length; i++)
            {
                maxSpeed = Propeller(Parts[Propellers[i].PartNumber]).maxSpinSpeed; // Get the maximum spin speed for this propeller

                // If a normalized drive command was received (e.g. the propeller spin speed is between -100 and 100)
                if (isCommandNormalized)
                {
                    if(ControllerSpeed < -100)     InputSpeed = -maxSpeed;                         // If the normalized value is less than -100, we use the negative of the propeller's maximum spin speed
                else if(ControllerSpeed > 100) InputSpeed = maxSpeed;                          // If the normalized value is more than 100, we use the propeller's maximum spin speed
                else                           InputSpeed = (ControllerSpeed/100) * maxSpeed;  // If the normalized value is between -100 and 100, we use a percentage of the maximum spin speed
                }
                // If a non-normalized drive command was received (e.g. the propeller spin speed is an absolute value)
                else
                {
                    if(ControllerSpeed < -maxSpeed)     InputSpeed = -maxSpeed;       // If the absolute value is less than -(maximum speed), we use the negative of the propeller's maximum spin speed
                else if(ControllerSpeed > maxSpeed) InputSpeed = maxSpeed;        // If the absolute value is more than the maximum speed, we use the propeller's maximum spin speed
                else                                InputSpeed = ControllerSpeed; // Otherwise, we use the controller's value for the propeller's spin speed
                }

                // Physically spin the propeller (spin the part)
                setSpinSpeed(Propellers[i].PartNumber, Converter.SpinSpeedToUU(InputSpeed));

                // Find the velocity generated by this propeller
                currentVel = (InputSpeed/6.283185307179586476925286766559) * (Propeller(Parts[Propellers[i].PartNumber]).Pitch);

                // Add the velocity generated by this propeller to the velocity generated by the other propellers
                if(i==0) totalVel = currentVel;
                else     totalVel += currentVel;

                // Find the force generated by this propeller
                Propellers[i].Force = getPropellerForce();
            }

            // Here, we deal with the rudder(s)
            for(i=0; i<Rudders.Length; i++)
            {
                maxAngle = ControlSurface(Parts[Rudders[i].PartNumber]).maxAngle; // Get the maximum rudder angle for this rudder

                // If a normalized drive command was received (e.g. the rudder angle is between -100 and 100)
                if (isCommandNormalized)
                {
                    if(ControllerRudder < -100)     InputRudder = -maxAngle;                         // If the normalized value is less than -100, we use the negative of the maximum rudder angle
                else if(ControllerRudder > 100) InputRudder = maxAngle;                          // If the normalized value is more than 100, we use the maximum rudder angle
                else                            InputRudder = (ControllerRudder/100) * maxAngle; // If the normalized value is between -100 and 100, we use a percentage of the maximum rudder angle
                }
                // If a non-normalized drive command was received (e.g. the rudder angle is an absolute value)
                else
                {
                    if(ControllerRudder < -maxAngle)     InputRudder = -maxAngle;        // If the absolute value is less than -(maximum angle), we use the negative of the rudder's maximum angle
                else if(ControllerRudder > maxAngle) InputRudder = maxAngle;         // If the absolute value is more than the maximum angle, we use the rudder's maximum angle
                else                                 InputRudder = ControllerRudder; // Otherwise, we use the controller's value for the rudder angle
                }

                // Physically rotate the rudder (rotate the part)
                setAngle(Rudders[i].PartNumber, Converter.AngleToUU(InputRudder));
                
                // Find the side force generated by the rudder
                Rudders[i].Force = getSideForce(ControlSurface(Parts[Rudders[i].PartNumber]).Area, InputRudder, InputSpeed);
            }

            // Here, we deal with the stern plane(s)
            for(i=0; i<SternPlanes.Length; i++)
            {
                maxAngle = ControlSurface(Parts[SternPlanes[i].PartNumber]).maxAngle; // Get the maximum stern plane angle for this stern plane

                // If a normalized drive command was received (e.g. the stern plane angle is between -100 and 100)
                if(isCommandNormalized)
                {
                    if(ControllerSternPlane < -100)     InputSternPlane = -maxAngle;                             // If the normalized value is less than -100, we use the negative of the maximum stern plane angle
                else if(ControllerSternPlane > 100) InputSternPlane = maxAngle;                              // If the normalized value is more than 100, we use the maximum stern plane angle
                else                                InputSternPlane = (ControllerSternPlane/100) * maxAngle; // If the normalized value is between -100 and 100, we use a percentage of the maximum stern plane angle
                }
                // If a non-normalized drive command was received (e.g. the stern plane angle is an absolute value)
                else
                {
                    if(ControllerSternPlane < -maxAngle)     InputSternPlane = -maxAngle;            // If the absolute value is less than -(maximum angle), we use the negative of the stern plane's maximum angle
                else if(ControllerSternPlane > maxAngle) InputSternPlane = maxAngle;             // If the absolute value is more than the maximum angle, we use the stern plane's maximum angle
                else                                     InputSternPlane = ControllerSternPlane; // Otherwise, we use the controller's value for the stern plane angle
                }

            // Physically rotate the stern plane (rotate the part)
            setAngle(SternPlanes[i].PartNumber, Converter.AngleToUU(InputSternPlane));

            // Find the side force generated by the stern plane
                SternPlanes[i].Force = getSideForce(ControlSurface(Parts[SternPlanes[i].PartNumber]).Area, InputSternPlane, InputSpeed);
            }
        }

        oldTime = currentTime;

        KWake(); // Make sure that the Karma Engine is "awake"
    }
}


//*********************************************************************************************************************
// getPropellerForce Function
// --------------------------
//     Function that calculates and returns the force generated by the underwater robot's propeller.
//*********************************************************************************************************************
function float getPropellerForce()
{
    return (1.1 * currentVel * ChassisMass) / (currentTime - oldTime);
}


//*********************************************************************************************************************
// getSideForce Function
// --------------------------
//     Function that calculates and returns the side force generated by the underwater robot's control surfaces.
//   The control surfaces can either be rudders or stern planes. The equation used is the drag equation.
//*********************************************************************************************************************
function float getSideForce(float SurfaceArea, float Angle, float Speed)
{
    local float Force;

    // Calculate the side force generated by the part
    if(Sign(Angle) == Sign(Speed))
    {
        Force = 0.5 * waterDensity * Square(totalVel) * getDragConstant(Angle) * SurfaceArea;
    }
    else
    {
        Force = -0.5 * waterDensity * Square(totalVel) * getDragConstant(Angle) * SurfaceArea;
    }

    return Force;
}


//*********************************************************************************************************************
// Sign Function
// ------------------------
//     Helper function (for the getSideForce function) that returns the sign of the number passed as a parameter.
//     If the variable passed as a parameter is 0, the function returns 0.
//     If the variable passed as a parameter is positive, the function returns 1.
//     If the variable passed as a parameter is negative, the function returns -1.
//*********************************************************************************************************************
function int Sign(float theValue)
{
    if(theValue == 0) return 0;
    return (theValue / Abs(theValue));
}


//*********************************************************************************************************************
// getDragConstant Function
// ------------------------
//     Helper function (for the getSideForce function) that calculates and returns the drag coefficient given
//   the angle of a part (e.g given the angle of a rudder or of a stern plane).
//*********************************************************************************************************************
function float getDragConstant(float angle)
{
    local float X_2,X_3;

    angle = Converter.AngleToDeg(Abs(angle));

    X_2 = angle * angle;
    X_3 = X_2 * angle;

    return ((0.00003 * X_3) - (0.0002 * X_2) + (0.0028 * angle));
}


//*********************************************************************************************************************
// KApplyForce Event
// -----------------
//     Event called at the same frequency of the Tick function, as long as the karma engine is "awake".
//     The event calculates the appropriate forces and torques, based on the underwater robot's rotation, which
//   are then automatically applied by the Karma Engine.
//*********************************************************************************************************************
event KApplyForce(out vector Force, out vector Torque)
{
    local int i;
    local float PropellerForce, RudderTorque, SternPlaneTorque;

    // Calculate the total force generated by all the propellers
    PropellerForce = 0;
    for(i=0; i<Propellers.Length; i++)
    {
        PropellerForce += Propellers[i].Force;
    }

    // Calculate the total torque generated by all the rudders (Torque = Force * Length_from_center_of_mass)
    RudderTorque = 0;
    for(i=0; i<Rudders.Length; i++)
    {
        RudderTorque += (Rudders[i].Force * Converter.LengthFromUU(Sqrt(Square(Location.X - Parts[Rudders[i].PartNumber].Location.X) +
                                                                        Square(Location.Y - Parts[Rudders[i].PartNumber].Location.Y) +
                                                                        Square(Location.Z - Parts[Rudders[i].PartNumber].Location.Z))));
    }

    // Calculate the total torque generated by all the stern planes (Torque = Force * Length_from_center_of_mass)
    SternPlaneTorque = 0;
    for(i=0; i<SternPlanes.Length; i++)
    {
        SternPlaneTorque += (SternPlanes[i].Force * Converter.LengthFromUU(Sqrt(Square(Location.X - Parts[SternPlanes[i].PartNumber].Location.X) +
                                                                                Square(Location.Y - Parts[SternPlanes[i].PartNumber].Location.Y) +
                                                                                Square(Location.Z - Parts[SternPlanes[i].PartNumber].Location.Z))));
    }

    // Set the Force and Torque to be applied by the Karma Engine, based on the vehicle's rotation
    Force = (PropellerForce * vect(1,0,0)) >> Rotation;
    Torque = ((Vect(0,1,0) * SternPlaneTorque) + (Vect(0,0,1) * RudderTorque)) >> Rotation;
}


//*********************************************************************************************************************
// PhysicsVolumeChange Event
// -------------------------
//     Event called whenever the submarine enters a new physics volume.
//     The event sets the atSurface boolean variable if the underwater robot is not in a water volume.
//*********************************************************************************************************************
simulated event PhysicsVolumeChange( PhysicsVolume NewVolume )
{
    Super.PhysicsVolumeChange(NewVolume);

    atSurface = !NewVolume.bWaterVolume;
}


//*********************************************************************************************************************
// DEFAULT PROPERTIES
// DO NOT change these properties since they are used to initialize programming variables
//*********************************************************************************************************************
defaultproperties
{
    atSurface = false
    initialized = false
}

Overview Package Class Source Class tree Glossary
previous class      next class frames      no frames
Class file time: Fr 19.1.2007 22:33:56.000 - Creation time: Mo 16.4.2007 11:20:55.218 - Created with UnCodeX