From 7ec186bee9bb9e908ce30fc888ad630928c68e1a Mon Sep 17 00:00:00 2001
From: Allanis <allanis@saracraft.net>
Date: Sat, 23 Mar 2013 21:19:11 +0000
Subject: [PATCH] [Add] Trying to work with a PID system for smart missiles.

---
 src/physics.c | 37 +++++++++++++-----------
 src/weapon.c  | 78 +++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 89 insertions(+), 26 deletions(-)

diff --git a/src/physics.c b/src/physics.c
index b3e6c80..ee6050b 100644
--- a/src/physics.c
+++ b/src/physics.c
@@ -9,13 +9,15 @@
 // MISC
 // ================
 double angle_diff(const double ref, double a) {
+  double d;
   if(a < M_PI) a += 2*M_PI;
-  double d = fmod((a-ref), 2*M_PI);
+  d = fmod((a-ref), 2*M_PI);
   return (d <= M_PI) ? d : d - 2*M_PI;
 }
 
 void limit_speed(Vec2* vel, const double speed, const double dt) {
-  double vmod = VMOD(*vel);
+  double vmod;
+  vmod = VMOD(*vel);
   if(vmod > speed) // Should not go faster.
     vect_pset(vel, (vmod-speed)*(1.-dt*3.) + speed, VANGLE(*vel));
 }
@@ -94,15 +96,15 @@ static void simple_update(Solid* obj, const double dt) {
   if(obj->dir < 0.)      obj->dir += 2*M_PI;
 
   double px, py, vx, vy;
-  px = VX(obj->pos);
-  py = VY(obj->pos);
-  vx = VX(obj->vel);
-  vy = VY(obj->vel);
+  px = obj->pos->x;
+  py = obj->pos->y;
+  vx = obj->vel->x;
+  vy = obj->vel->y;
 
   if(obj->force.mod) { // Force applied on an object.
     double ax, ay;
-    ax = VX(obj->force)/obj->mass;
-    ay = VY(obj->force)/obj->mass;
+    ax = obj->force->x/obj->mass;
+    ay = obj->force->y/obj->mass;
 
     vx += ax*dt;
     vy += ay*dt;
@@ -138,27 +140,27 @@ static void simple_update(Solid* obj, const double dt) {
 
 #define RK4_MIN_H 0.01 // Minimal pass we want.
 static void rk4_update(Solid* obj, const double dt) {
+  int i, N;                       // For iteration and pass calculation.
+  double h, px, py, vx, vy;       // Pass and position/velocity values.
+  double ix, iy, tx, ty, ax, ay;  // Initial and temp cartesian vector values.
   // Make sure angle doesn't flip.
   obj->dir += M_PI/180.*obj->dir_vel*dt;
   if(obj->dir >= 2.*M_PI) obj->dir -= 2*M_PI;
   else if(obj->dir < 0.)  obj->dir += 2*M_PI;
 
-  int N = (dt > RK4_MIN_H) ? (int)(dt/RK4_MIN_H) : 1;
-  double h = dt / (double)N; // Step.
+  N = (dt > RK4_MIN_H) ? (int)(dt/RK4_MIN_H) : 1;
+  h = dt / (double)N; // Step.
 
-  double px, py, vx, vy;
   px = obj->pos.x;
   py = obj->pos.y;
   vx = obj->vel.x;
   vy = obj->vel.y;
 
   if(obj->force.mod) {  // Force applied on object.
-    int i;
-    double ix, iy, tx, ty; // Initial and temp cartesian vector values.
-
-    double ax, ay;
-    ax = VX(obj->force)/obj->mass;
-    ay = VY(obj->force)/obj->mass;
+    // Movement quantity theorem : m*a = \sum f.
+    ax = obj->force.x / obj->mass;
+    ay = obj->force.y / obj->mass;
+    
     for(i = 0; i < N; i++) {
       // X component.
       tx = ix = vx;
@@ -182,6 +184,7 @@ static void rk4_update(Solid* obj, const double dt) {
     }
     vect_cset(&obj->vel, vx, vy);
   } else {
+    // Euler method -> p = v*t + 0.5*a*t^2 (no accel, so no error).
     px += dt*vx;
     py += dt*vy;
   }
diff --git a/src/weapon.c b/src/weapon.c
index b7df6a0..2b645c7 100644
--- a/src/weapon.c
+++ b/src/weapon.c
@@ -18,6 +18,10 @@
 #define VOICE_PRIORITY_BOLT  10  // Default.
 #define VOICE_PRIORITY_AMMO  8  // Higher.
 
+// PID values.
+#define IMIN   -50000
+#define IMAX    50000
+
 // Some stuff from pilot.
 extern Pilot** pilot_stack;
 extern int pilots;
@@ -37,7 +41,10 @@ typedef struct Weapon_ {
 
   // Update position and render.
   void(*update)(struct Weapon_*, const double, WeaponLayer); // Position update and render.
-  void(*think)(struct Weapon_*, const double); // Some missiles need to be inteligent.
+  void(*think)(struct Weapon_*, const double); // Some missiles need to be inteligent.a
+
+  double pid_last;
+  double pid_int;
 } Weapon;
 
 // Behind Pilot layer.
@@ -143,10 +150,43 @@ static void think_seeker(Weapon* w, const double dt) {
   limit_speed(&w->solid->vel, w->outfit->u.amm.speed, dt);
 }
 
+// ========================================================
 // Smart seeker brain. Much better at homing.
+//
+// SYSTEM DYNAMICS
+//  INPUT:
+//    MV = missile velocity
+//    RP = target relative position.
+//
+//  Constants:
+//    KE = error constant.
+//    KF = face constant.
+//
+//  INTERNAL VARIABLES:
+//    FD = face dir.
+//    E = error.
+//    PID = PID
+//
+//  OUTPUT:
+//    T = turn modifier.
+//
+//  -- Fancy diagram.
+//
+//  MV +  FD +                    T
+//  ---->0----->[PID]----[KF]---+---->
+//      -^     -^               |
+//  RP   |      | E             |
+//  -----+      |------[KE]------
+//
+// ========================================================
 static void think_smart(Weapon* w, const double dt) {
-  double diff;
-  Vec2 tv, sv;
+  Vec2 tv;
+  
+  // Controler stuff.
+  double ke, kf;          // Constants
+  double fd, e, ed, ei;   // Internal variables.
+  double t;               // Internal variables.
+  double kp, ki, kd;      // PID.
 
   if(w->target == w->parent) return; // No self shooting here.
 
@@ -158,12 +198,32 @@ static void think_smart(Weapon* w, const double dt) {
   }
 
   if(SDL_GetTicks() > (w->timer + w->outfit->u.amm.lockon)) {
-    vect_cset(&tv, VX(p->solid->pos) + VX(p->solid->vel),
-              VY(p->solid->pos) + VY(p->solid->vel));
-    vect_cset(&sv, VX(w->solid->pos) + VX(w->solid->vel),
-              VY(w->solid->pos) + VY(w->solid->vel));
-    diff = angle_diff(w->solid->dir, vect_angle(&tv, &sv));
-    w->solid->dir_vel = 10*diff*w->outfit->u.amm.turn; // Face the target.
+    // Begin controler.
+    //  Constants.
+    kp = 1.;
+    kd = 1.;
+    ki = 1.;
+    
+    // Calculate fd.
+    vect_cset(&tv, p->solid->pos.x - w->solid->pos.x,
+              p->solid->pos.y - w->solid->pos.y);
+    e = angle_diff(VANGLE(w->solid->vel), VANGLE(tv));
+
+    e = fd;
+    ed = fd - ke*w->pid_last;
+    w->pid_int += e;
+    if(w->pid_int > IMAX) w->pid_int = IMAX;
+    else if(w->pid_int < IMIN) w->pid_int = IMIN;
+    ei = w->pid_int;
+    t = e*kp + ed*kd + ei*ki;
+
+    // Final output.
+    t *= kf;
+    w->pid_last = t;
+
+    // End controller.
+
+    w->solid->dir_vel = t * w->outfit->u.amm.turn; // Face the target.
     if(w->solid->dir_vel > w->outfit->u.amm.turn)
       w->solid->dir_vel = w->outfit->u.amm.turn;
     else if(w->solid->dir_vel < -w->outfit->u.amm.turn)