
/* Generated by Interface Builder */

#import <math.h>
#import <appkit/nextstd.h>
#import "Car_main.h"
#import "Car.h"
#import "Wheels.h"
#import "Cycle.h"
#import "DataView.h"
#import "AppDelegate.h"
#import "Road.h"
#import "Controller.h"
#import "Transmission.h"
#import "GasTank.h"
#import "Battery.h"

@implementation Car

- init
{
    [super init];
    car = self;
    return self;
}

- read:(NXTypedStream *)stream
{
    [super read:stream];
    NXReadTypes(stream,"fffci",&dragCoefficient,&frontalArea,&mass,&plotAcceleration,&testMode);
    return self;
}

- write:(NXTypedStream *)stream
{
    [super write:stream];
    NXWriteTypes(stream,"fffci",&dragCoefficient,&frontalArea,&mass,&plotAcceleration,&testMode);
    return self;
}

- (float)dragCoefficient
{
    return dragCoefficient;
}

- setDragCoefficient:(float)aNumber
{
    dragCoefficient = aNumber;
    return self;
}

- (float)frontalArea
{
    return frontalArea;
}

- setFrontalArea:(float)aNumber
{
    frontalArea = aNumber;
    return self;
}

- (float)mass
{
    return mass;
}

- setMass:(float)aNumber
{
    mass = aNumber;
    return self;
}

- setRoadForce:(float)aNumber
{
    roadForce = aNumber;
    return self;
}

- (float)currentVelocity
{
    return currentVelocity;
}

- (BOOL)plotAcceleration
{
    return plotAcceleration;
}

- setPlotAcceleration:(BOOL)flag
{
    plotAcceleration = flag;
    return self;
}

- (int)testMode;
{
    return testMode;
}

- setTestMode:(int)mode
{
    testMode = mode;
    return self;
}

- runSimulation:sender
{
float Mass;
float totalMass;
float CdA;
float rho;
float Cr;
float aero;
float rolling;
float inertial;
float velocity;
float acceleration;
float requiredAcceleration;
double theta;
float gradient;
float power;
struct plotData point;

    [cycle reset];
    [road reset];
    [battery getSetForRun];
    [gasTank getSetForRun];

    if ( plotAcceleration )
        {
        if ( !accelerationGraph )
            {
            accelerationGraph = [appDelegate newGraph];
            [[accelerationGraph window] setTitle:"Acceleration (m/s^2)"];
            }
        [[accelerationGraph window] orderFront:nil];
        }
    [[accelerationGraph data] empty];

    if ( 1 )   // There should be another option for this graph.  Not implemented yet.
        {
        if ( !velocityGraph )
            {
            velocityGraph = [appDelegate newGraph];
            [[velocityGraph window] setTitle:"Velocity (m/s)"];
            }        
        [[velocityGraph window] orderFront:nil];
        }
    [[velocityGraph data] empty];

    if ( 1 )	// There should be another option for this graph.  Not implemented yet.
        {
        if ( !powerGraph )
            {
            powerGraph = [appDelegate newGraph];
            [[powerGraph window] setTitle:"Power (kW)"];
            }        
        [[powerGraph window] orderFront:nil];
        }
    [[powerGraph data] empty];

    Mass = mass;
    Mass += [controller mass];
    Mass += [transmission mass];
    Mass += [engine mass];
    Mass += [gasTank mass];
    Mass += [motor mass];
    Mass += [battery mass];
    Mass += [wheel mass] * 4;
    
    CdA = dragCoefficient * frontalArea;
    rho = 94 * 28.97 / 8.3143 / 293;           // air density in kg/m^3
    Cr = [wheel rollingResistance];
    energy = 0;

/******************************************************************************************************************************
 *   That was setup.  Here's where the action really starts.                                                                  *
 ******************************************************************************************************************************/
    if ( testMode == ARBITRARY )
        {
        while ( [cycle getNextVelocity:&velocity andAcceleration:&acceleration] )  
            {
            totalMass = Mass + [gasTank fuelMass];
            velocity = velocity / 3.6;			//conversion from km/h to m/s
            acceleration = acceleration / 3.6;		//likewise
            point.x = [cycle lastTime];
            point.y = velocity;
            [velocityGraph addData:&point];
            point.y = acceleration;
            [accelerationGraph addData:&point];

            aero = CdA * rho * pow(velocity+[road windSpeed],2) / 2;
            rolling = totalMass * 9.807 * Cr;
            inertial = totalMass * acceleration;
            
            theta = atan([road slope]);
            gradient = 9.807 * totalMass * sin(theta);

            power = velocity * ( aero + rolling + inertial + gradient );
            point.y = power / 1000;
            [road advance:[cycle timeStep] atSpeed:velocity];
            energy += point.y * [cycle timeStep];

            [powerGraph addData:&point];
            }
        }
    else if ( testMode == LIMITED )
        {
        [cycle getNextVelocity:&currentVelocity andAcceleration:&acceleration];
        currentVelocity /= 3.6;
        point.x = [cycle lastTime];
        point.y = currentVelocity;
        [velocityGraph addData:&point];

        while ( [cycle getNextVelocity:&velocity andAcceleration:&acceleration] ) // Note: we don't use the accel returned here
            {
            totalMass = Mass + [gasTank fuelMass];
            velocity = velocity / 3.6;			//conversion from km/h to m/s
            acceleration = ( velocity - currentVelocity ) / [cycle timeStep];
            requiredAcceleration = acceleration;
            point.x = [cycle lastTime];

            aero = CdA * rho * pow(currentVelocity+[road windSpeed],2) / 2;
            rolling = totalMass * 9.807 * Cr;
            inertial = totalMass * requiredAcceleration;
            
            theta = atan([road slope]);
            gradient = 9.807 * totalMass * sin(theta);
            [road advance:[cycle timeStep] atSpeed:currentVelocity];

            if ( currentVelocity == 0 )
                power = velocity * ( aero + rolling + inertial + gradient );
            else
                power = currentVelocity * ( aero + rolling + inertial + gradient );  // This is the required power in watts

            [controller powerRequired:power];		// We send a message to the controller, and the whole thing begins

            if ( (roadForce != 0) || (currentVelocity != 0) )
                inertial = roadForce - aero - rolling - gradient;  // After the messages have run their course, we get a force
            else 
                inertial = 0;
            acceleration = inertial / totalMass;		   // a = f/m
           
            if ( requiredAcceleration >= 0 )				// This is so we don't use up more power than we need.
                acceleration = MIN(acceleration,requiredAcceleration);  // It's just to overcome a problem with standing starts.
            else if ( requiredAcceleration < 0 )
                acceleration = MAX(acceleration,requiredAcceleration);

            point.y = currentVelocity * ( acceleration * totalMass + aero + rolling + gradient ) / 1000;
            [powerGraph addData:&point];

            currentVelocity += acceleration * [cycle timeStep]; 	// Advance the velocity by this much
            currentVelocity = MAX(currentVelocity,0);			// We never go backwards.

            point.y = currentVelocity;
            [velocityGraph addData:&point];
            point.y = acceleration;
            [accelerationGraph addData:&point];
//            fprintf(stderr,"%8f %8f %8f \n",aero,inertial,roadForce);
            }
        }
           
    [accelerationGraph drawLimits];
    [velocityGraph drawLimits];
    [powerGraph drawLimits];

    [[accelerationGraph window] display];
    [[velocityGraph window] display];
    [[powerGraph window] display];

    [appDelegate simulationEnded];

    [battery finishedRun];
    [gasTank finishedRun];

    return self;
}

- report:(NXStream *)stream
{
float Mass;

    Mass = mass;
    Mass += [controller mass];
    Mass += [transmission mass];
    Mass += [engine mass];
    Mass += [gasTank mass];
    Mass += [motor mass];
    Mass += [battery mass];
    Mass += [wheel mass] * 4;
    Mass += [gasTank fuelMass];

    NXPrintf(stream,"Car:\n");
    NXPrintf(stream,"  Total Mass:%fkg\n",Mass);
    NXPrintf(stream,"  Energy Used:%fkWh\n",energy/3600.0);
    return self;
}

@end















