// Copyright Bruce Allen 2017
// Compile with gcc -o antenna antenna.c -lm

// REMAINING THINGS TO CHECK:
// (a) sign conventions for h, which arm is positive
// (b) direction and origin conventions for the polarization axis
// (c) double check the hand calculations

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "antenna_lib.h"

// Global variables for passing information. Ugly style but doesn't
// matter for this!
struct InputStruct inputdata;

// For converting degrees to radians
const double deg_to_rad = M_PI/180.0;

// For computing arrival time delays, need mean radius of the earth in
// milliseconds. Get this by dividing radius in km by speed of light in
// km/s.
const double radius_earth = 6371.0*1000.0/299792.458;

// Event time
// GPS 1187008882
// UTC:	Thu 2017 Aug 17	12:41:04
// Julian Day 2457983.02852
// Modified Julian Day 57982.52852

// The convention in this code is that Lattitude is measured north of
// the equator.  So Lattitude -10 means 10 degrees SOUTH of the
// equator.  Longitude is measured East of Greenwich.  So longitude
// -90 means 90 degrees West of Greenwich (somewhere in the USA).

// Galaxy NGC 4993 (from Wikipedia):
//    Right ascension: 13h 09m 47.2s
//    Declination: −23° 23′ 4″
// We will need to convert this to location over the Earth at the
// event time.  It will turn out to be:
//    Lattitude −23.3844 degrees (south of equator)
//    Longitude 41.092981 degrees (east of Greenwich)


// From Chapter 11 of "Astronomical Algorithms" by Jean Meeus, published 1991
void print_galaxy_coordinates() {
   
   // Here is the Julian day of the event, including a fractional part
   double JD = 2457983.02852;
   
   // This is the Julian day (note previous integer part!)
   double JD0 = 2457982.5;
   
   // Compute Julian century t
   double day = JD0-2451545.0;
   double t = day/36525.0;
   
   // The following is equation 11.4 of the previous reference
   double degrees = 280.46061837 + 360.98564736629*(JD-2451545.0)+t*t*0.000387933 - t*t*t/38710000.0;
   degrees = fmod(degrees,360.0);

   printf("Greenwich Mean Sidereal time is %f degrees\n", degrees);
   
   // Now the longitude of our source (with sign conventions above) is given by L = RA - MST, where
   // MST (in degrees) is given above and RA is the RA of our source
   // Here a positive number means "East of Greenwich"
   double longitude = (13*3600.0+9*60+47.2)/240 - degrees;
   printf("Longitude of source is %f degrees\n", longitude);
}

//  Note these are in the order lattitude,longitude.  See routine
// "print_galaxy_coordinates" to convert location on sky to Earth
// location at event time.  These are set/used in the function
// populate_source() below.

struct Source {
   // lattitude north of equator, radians
   // longitude east from Greenwich, radians
   double location[2];
   
   // Unit vector from Earth to source
   double vec[3];

   // Unit vectors defining plane perpendicular to the source
   // direction.  U points east, V points north
   double u[3];
   double v[3];
};

struct Source source;

struct Detector {
   // null terminated char string
   char name[8];
   
   // lattitude north of equator, radians
   // longitude east from Greenwich, radians
   double location[2];
   
   // orientation of Y arm CCW from North, radians
   double orientation;

   // Unit vector from center of Earth to detector
   double vec[3];

   // Unit vectors pointing along the north and east directions at the
   // detector site
   double north[3];
   double east[3];

   // Unit vectors along the two arms
   double lx[3];
   double ly[3]; 
};

// LLO, LHO, Virgo, in that order
struct Detector detectors[3];

// input is a lattitude/longitude set in radians
// output is unit vectors
void make_unit_vectors(double *out, double *in) {
   double lat = in[0];
   double lon = in[1];
   out[0] = cos(lon)*cos(lat);
   out[1] = sin(lon)*cos(lat);
   out[2] = sin(lat);
}

void make_u_v_vectors(struct Source *src) {
   // lattitude and longitude
   double lat = src->location[0];
   double lon = src->location[1];

   // construct unit vectors perpendicular to line of sight
   // u points east
   src->u[0] = -sin(lon);
   src->u[1] = cos(lon);
   src->u[2] = 0.0;

   // v points north, so u,v are a right-handed pair like x,y
   src->v[0] = -sin(lat)*cos(lon);
   src->v[1] = -sin(lat)*sin(lon);
   src->v[2] = cos(lat);
}

// input is a lattitude/longitude set in radians
// output is unit vectors in the north and east directions
void make_north_east_vectors(struct Detector *det) {

   double lat = det->location[0];
   double lon = det->location[1];
   
   det->north[0] = -sin(lat)*cos(lon);
   det->north[1] = -sin(lat)*sin(lon);
   det->north[2] = cos(lat);
   det->east[0] = -sin(lon);
   det->east[1] = cos(lon);
   det->east[2] = 0.0;

   double cpsi = cos(det->orientation);
   double spsi = sin(det->orientation);
   int i;
   for (i=0; i<3; i++) {
      det->lx[i] =  cpsi*det->east[i] + spsi*det->north[i]; 
      det->ly[i] = -spsi*det->east[i] + cpsi*det->north[i];
   }
}

// sets up the different vectors needed to define the source
void populate_source() {
   
   source.location[0] = -1.0*deg_to_rad*(23.0 + 23.0/60.0 + 4.0/3600.0);
   source.location[1] = deg_to_rad*41.092981;
   
#if 0
   // for testing purposes, put source directly over a detector
   source.location[0] = 46.45*deg_to_rad;
   source.location[1] = -119.41*deg_to_rad;
#endif

   
   make_unit_vectors(source.vec,source.location);
   make_u_v_vectors(&source);
}

// initially take detector locations and orientations from
// https://arxiv.org/pdf/gr-qc/9607075.pdf
// Be sure to check this later!!

void populate_detectors(){
   
   // LLO
   // Sanity checked using Google Earth!
   strcpy(detectors[0].name, " LLO ");
   detectors[0].location[0] = 30.56*deg_to_rad;
   detectors[0].location[1] = -90.77*deg_to_rad;
   detectors[0].orientation = (198.0+inputdata.orientation[0])*deg_to_rad;

   // LHO
   // Sanity checked using Google Earth!
   strcpy(detectors[1].name, " LHO ");
   detectors[1].location[0] = 46.45*deg_to_rad;
   detectors[1].location[1] = -119.41*deg_to_rad;
   detectors[1].orientation = (126.8+inputdata.orientation[1])*deg_to_rad;
   
   // VIRGO
   // Sanity checked using Google Earth!
   strcpy(detectors[2].name, "VIRGO");
   detectors[2].location[0] = 43.63*deg_to_rad;
   detectors[2].location[1] = 10.5*deg_to_rad;
   detectors[2].orientation = (71.5+inputdata.orientation[2])*deg_to_rad;

   int i;

   // Coordinate system has x/y plane through the equator, north pole
   // along positive z axis, and Greenwich passing through x axis (ie
   // y=0).
   for (i=0;i<3;i++) {
      make_unit_vectors(detectors[i].vec, detectors[i].location);
      make_north_east_vectors(detectors+i);
   }
}

void print_detector(int det) {
   printf("name: %s\n"
	  "lattitude (north): %f\n"
	  "longitude (east): %f\n"
	  "orientation: (CCW from North): %f\n"
	  "earth center to detector %f %f %f\n"
	  "vector along X arm %f %f %f\n"
	  "vector along Y arm %f %f %f\n\n",
	  detectors[det].name,
	  detectors[det].location[0],
	  detectors[det].location[1],
	  detectors[det].orientation,
	  detectors[det].vec[0], detectors[det].vec[1], detectors[det].vec[2],
	  detectors[det].lx[0], detectors[det].lx[1], detectors[det].lx[2],
	  detectors[det].ly[0], detectors[det].ly[1], detectors[det].ly[2]
	  );
}

void print_source() {
   printf("source at:\n"
	  "lattitude: %f\n"
	  "longitude: %f\n"
	  "earth center to source: %f %f %f\n"
	  "U vector: %f %f %f\n"
	  "V vector: %f %f %f\n\n",
	  source.location[0],
	  source.location[1],
	  source.vec[0], source.vec[1],source.vec[2],
	  source.u[0],source.u[1],source.u[2],
	  source.v[0],source.v[1],source.v[2]
	  );
}

// This dots the mass quadrupole with the antenna functions
void get_UV_combinations(int det, double *alpha, double *beta, double *rdotn) {
  
   double a=0,b=0,dot=0;
   int i, j;
   
   double *su=source.u;
   double *sv=source.v;
   double *n=source.vec;
   
   double *dx=detectors[det].lx;
   double *dy=detectors[det].ly;
   double *r=detectors[det].vec;
   
   for (i=0; i<3; i++)
      for (j=0; j<3; j++)
	 a += (su[i]*su[j] - sv[i]*sv[j]) * (dx[i]*dx[j] - dy[i]*dy[j]);
   
   for (i=0; i<3; i++)
      for (j=0; j<3; j++)
	 b += (su[i]*sv[j] + sv[i]*su[j]) * (dx[i]*dx[j] - dy[i]*dy[j]);

   // The quantity dot is positive if the vector from the earth center
   // to a detector has the SAME direction as the vector from the
   // earth center to the source.  This means that the signal arrives
   // EARLIER at that detector.  Hence the signal waveform seen at the
   // detector is of the form h(t) = wave(t + R*dot) where R is the
   // (positive) earth radius in units of time and wave(t) is the
   // emitted source waveform.
   
   for (i=0; i<3; i++)
      dot += n[i]*r[i];
   
   *alpha = a;
   *beta = b;
   *rdotn = dot;
}


// library function that can be called either from the GUI code or
// from a stand-alone terminal program.  This function does not modify
// the input struct, but does populate/modify the output strut!
void get_antenna(struct OutputStruct *out, struct InputStruct *in) {
   int i;
   // set up the source and detectors
   inputdata=*in;
   populate_source();
   populate_detectors();

   double iota = inputdata.iota;
   double psi = inputdata.psi;
#ifdef DEBUG
   fprintf(stderror, "Iota = %f degrees\nPsi = %f degrees\n", iota, psi);
#endif
   iota *= deg_to_rad;
   psi *= deg_to_rad;

   // get angles needed to calculate antenna response. The minus sign
   // in the definition of cos(iota) is because my calculational notes
   // assume that iota=0 corresponds to orbital motion CCW in the
   // (right-handed) UV coordinate system.  That corresponds to having
   // the orbital angular momentum pointing radially AWAY from earth.
   // But the standard convention is that iota=0 is face-on, meaning
   // orbital angular momentum pointing TO the earth.  So I change the
   // sign of cos(iota) below to correct for this.
   
   double ci = -cos(iota);
   double c2p = cos(2*psi);
   double s2p = sin(2*psi);

   // Now find waveforms.  At each site, waveform is w^2 [X sin(2wt) + Y cos(2wt) ]
   // loop over detectors
   for (i=0; i<3; i++) {
      double alpha, beta, X, Y, dt;

      // Ugly, should pass as argument to populate_detectors()
      strcpy(out->name[i], detectors[i].name);
      
      // get antenna pattern overlap with source plane
      get_UV_combinations(i, &alpha, &beta, &dt);

      // form combinations as functions of inclination and polarization angles
      X =         2.0*ci*(alpha*s2p - beta*c2p);
      Y = -(ci*ci + 1.0)*(alpha*c2p + beta*s2p);
      // compute time delay in milliseconds
      dt *= radius_earth;
      
      // printf("For detector %s the waveform is w^2 [ %.3f sin(2w(t%+.1f ms))%+.3f cos(2w(t%+.1f ms)) ]\n", detectors[i].name, X, dt, Y, dt);

      // compute alternative form of output
      // X sin(phi) + Y cos(phi) = sqrt(X^2+Y^2) sin(phi + ang) where ang=atan2(Y,X)
      
      double amp = sqrt(X*X + Y*Y);
      double ang = 180.0*atan2(Y, X)/M_PI;
      
      // pass outputs In this code the relevant phases are the
      // DIFFERENCES between those at the different detectors.  So to
      // make for a less confusing visual display in the GUI, I have
      // offset the output phase as a function of psi, in a way that
      // tries to reduce confusion about the meaining of the orbital
      // coalescence phase.
      out->amp[i]= amp;
      out->phase[i] = ang-2*psi*ci/deg_to_rad;
      out->dt[i] = dt;
      
#ifdef DEBUG
      // degree character in UTF-8 character set (most likely terminal type!)
      int deg1=0xC2, deg2=0xB0;
      fprintf(stderr,
	      "For detector %s"
	      "the waveform is %.3f"
	      " w^2 sin(2w[t%+.1f ms]%+.1f%c%c)\n",
	      detectors[i].name,
	      amp, dt, ang, deg1, deg2);
#endif
   }
   return;
}