AntarcticaGeometry.cxx
1 #include "AntarcticaGeometry.h"
2 #include "AnitaGeomTool.h"
3 #include "RampdemReader.h"
4 #include "Adu5Pat.h"
5 #include "FFTtools.h"
6 #include "UsefulAdu5Pat.h"
7 #include "TRandom.h"
8 #include "TGraph2D.h"
9 #include "TString.h"
10 #include "TFile.h"
11 
12 #ifdef USE_GEOGRAPHIC_LIB
13 #include "GeographicLib/GeodesicLine.hpp"
14 #include "GeographicLib/Geodesic.hpp"
15 #endif
16 
17 ClassImp(AntarcticCoord);
19 ClassImp(PayloadParameters);
20 ClassImp(StereographicGrid);
21 
22 template <int coarseness>
24 {
25 
26  public:
27  double compute(double E, double N)
28  {
29 // printf("%g,%g\n", E,N);
30  return map->Interpolate(E,N);
31  }
32 
33  TProfile2D *map;
34  SurfaceWrapper(RampdemReader::dataSet set)
35  {
36  map = RampdemReader::getMap(set, coarseness);
37  }
38 
40  {
41 // delete map;
42 
43  }
44 };
45 
46 template <int coarseness>
47 static SurfaceWrapper<coarseness> & getSurface(RampdemReader::dataSet set)
48 {
49  if (set == RampdemReader::surface)
50  {
51  static SurfaceWrapper<coarseness> w(set);
52  return w;
53  }
54 
55  //the only other thing that makes sense is rampdem
56  static SurfaceWrapper<coarseness> w(RampdemReader::rampdem);
57  return w;
58 }
59 
60 
61 
62 
63 
64 void AntarcticCoord::asString(TString * s) const
65 {
66 
67  if (type == WGS84)
68  {
69  s->Form("%g N %g E %g m",x,y,z);
70  }
71 
72  if (type == STEREOGRAPHIC)
73  {
74  s->Form("%g m(E), %g m(N), %g m",x,y,z);
75  }
76 
77  if (type == CARTESIAN)
78  {
79  s->Form("(%g,%g,%g) m",x,y,z);
80  }
81 
82 }
83 
84 PayloadParameters::PayloadParameters(const Adu5Pat * gps, const AntarcticCoord & source_pos, const Refraction::Model * refract)
85  : payload(AntarcticCoord::WGS84, gps->latitude, gps->longitude, gps->altitude),
86  source(source_pos)
87 {
88 
89  payload.to(AntarcticCoord::CARTESIAN);
90  //vector from source to payload
91 
92  TVector3 p = payload.v();
93  TVector3 s = source.v();
94 
95 #ifndef USE_GEOGRAPHIC_LIB
96  TVector3 sprime = s;
97  // this is stolen from AnitaGeomTool. Seems like it should be equivalent to a some form of rotateUz
98  // wihch would probably be a more efficient way to do this without trig functions
99  sprime.RotateZ(-1 * p.Phi());
100  sprime.RotateY(-1 * p.Theta());
101  sprime[2]=p.Mag()-fabs(sprime.z());
102 
103  sprime.RotateZ(gps->heading*TMath::DegToRad());
104 
105  //TODO: check if these axes need modification. Right now pitch and roll are 0 :)
106  sprime.RotateY(-AnitaStaticAdu5Offsets::pitch *TMath::DegToRad());
107  sprime.RotateX(-AnitaStaticAdu5Offsets::roll *TMath::DegToRad());
108 
109  source_phi = FFTtools::wrap(sprime.Phi() * TMath::RadToDeg(),360);
110  source_theta = 90 - sprime.Theta() * TMath::RadToDeg();
111 
112  //Now do the opposite
113  TVector3 pprime = p;
114  pprime.RotateZ(-1*s.Phi());
115  pprime.RotateY(-1*s.Theta());
116  pprime[2] = s.Mag() - fabs(pprime.z());
117  payload_el = -FFTtools::wrap( 90 - pprime.Theta() * TMath::RadToDeg(), 180, 0);
118  payload_az = pprime.Phi() * TMath::RadToDeg();
119 
120 #else
121 
122  //vector from source to paylaod
123  TVector3 v = (s-p);
124  //angle between v and p gives zenith angle. elevation is - 90.
125  source_theta = p.Angle(v) * TMath::RadToDeg() - 90;
126  payload_el = s.Angle(v) * TMath::RadToDeg() - 90;
127  //To get phi, we have to solve the inverse geodesic problem
128  AntarcticCoord swgs84 = source.as(AntarcticCoord::WGS84);
129 
130  GeographicLib::Geodesic::WGS84().Inverse(gps->latitude, gps->longitude, swgs84.x, swgs84.y, source_phi, payload_az);
131  //rotate by 180 to get direction towards payload, and wrap
132  payload_az = FFTtools::wrap(payload_az-180, 360);
133 
134 
135  source_phi = FFTtools::wrap(gps->heading - source_phi,360);
136 #endif
137  apparent_source_theta = source_theta;
138  apparent_payload_el = payload_el;
139 
140  if (refract)
141  {
142  //We actually want the apparent anagle
143  double payload_el_correction = 0;
144  apparent_source_theta -= refract->getElevationCorrection(gps, &source, &payload_el_correction);
145  apparent_payload_el+= payload_el_correction;
146  }
147 
148 
149 
150  distance = (source.v() - payload.v()).Mag();
151 }
152 
153 static void cart2stereo(double*,double*,double*) __attribute__((optimize("fast-math"),optimize("O3")));
154 static void stereo2cart(double*,double*,double*) __attribute__((optimize("fast-math"),optimize("O3")));
155 
156 // for ECE -> lat/lon
157 static const double a = 6378137;
158 static const double b = 6356752.31424518;
159 static const double binv = 1./6356752.31424518;
160 static const double e = sqrt((a*a-b*b)/(a*a));
161 static const double ep = e * a/b;
162 
163 //these are copied and pasted from RampdemReader, there may be some redundancy with other constants but oh well
164 static const double scale_factor=0.97276901289;
165 static const double ellipsoid_inv_f = 298.257223563;
166 static const double eccentricity = sqrt((1/ellipsoid_inv_f)*(2-(1/ellipsoid_inv_f)));
167 static const double c_0 = (2*R_EARTH / sqrt(1-pow(eccentricity,2))) * pow(( (1-eccentricity) / (1+eccentricity) ),eccentricity/2);
168 static const double a_bar = pow(eccentricity,2)/2 + 5*pow(eccentricity,4)/24 + pow(eccentricity,6)/12 + 13*pow(eccentricity,8)/360;
169 static const double b_bar = 7*pow(eccentricity,4)/48 + 29*pow(eccentricity,6)/240 + 811*pow(eccentricity,8)/11520;
170 static const double c_bar = 7*pow(eccentricity,6)/120 + 81*pow(eccentricity,8)/1120;
171 static const double d_bar = 4279*pow(eccentricity,8)/161280;
172 
173 
174 static void cart2stereo(double *x, double * y, double *z)
175 {
176  //Turns out this is an important conversion for performance. Will hackily put it in right here.
177  //I'm using a slightly different conversion from cartesian to WGS84, and also avoiding all trig functions, which just cause havoc.
178  // This is based on http://www.microem.ru/pages/u_blox/tech/dataconvert/GPS.G1-X-00006.pdf
179 
180  double X = *y; //silly
181  double Y = *x; //silly
182  double Z = *z;
183 
184  double H = sqrt(X*X+Y*Y);
185  double Hinv = 1./H;
186 
187  double sin_lon = Y*Hinv;
188  double cos_lon = Y!=0 ? (X/Y * sin_lon) : ( (X > 0) - (X < 0));
189 
190  double tan_theta = Z*Hinv*(a*binv);
191  double cos_theta = pow(1+ tan_theta* tan_theta,-0.5);
192  double sin_theta = tan_theta * cos_theta;
193 
194  //this might be able to be simplified
195  double num = Z + (ep *ep*b) * (sin_theta * sin_theta * sin_theta);
196  double denom = H - (e *e*a) * (cos_theta * cos_theta * cos_theta);
197 
198  double H2inv = pow(num*num + denom*denom,-0.5);
199  double sin_lat = num * H2inv * ( (denom > 0) - ( denom < 0) ); //do I need this sign change here? I suspect denom is always well greater than zero...
200  double cos_lat = denom / num * sin_lat;
201 
202 
203  double N = a * pow(1. - (e*e) * sin_lat * sin_lat,-0.5);
204 
205  *z = H / cos_lat - N;
206 
208 
209  double R = scale_factor * c_0 * pow( (1 + eccentricity * sin_lat ) / (1 - eccentricity * sin_lat), eccentricity/2) ;
210 
211  //use some trig identities here...
212  double tan_lat_over_2 = sin_lat / (1 + cos_lat);
213  R *= (1 - tan_lat_over_2) / (1 + tan_lat_over_2);
214 
215  *x = R * sin_lon;
216  *y = R * cos_lon;
217 
218 }
219 
220 
221 
222 static void stereo2cart(double *x, double *y, double *z)
223 {
224  double E = *x;
225  double N = *y;
226 
227  if (!E && !N) //special case the south pole
228  {
229  *z +=b;
230  return;
231  }
232 
233  double alt = *z;
234 
235  double H2 = E*E + N *N;
236  double H = sqrt(H2);
237  double Hinv = 1./H;
238  double sin_lon = E*Hinv;
239  double cos_lon = N*Hinv;
240 
241 
242  // Here I'm just taking the equations from EastingNorthingToLonLat and avoiding trig using
243  // identities:
244  // sin(pi/2-x) = cos(x) ,
245  // cos(pi/2-x) = sin(x) ,
246  // cos(2x) = (1 - tan^2 x) / (1 + tan^2 x) ,
247  // sin(2x) = 2 tan x / ( 1 + tan^2 x)
248 
249  double tan2_factor = H2 /(scale_factor * scale_factor * c_0 * c_0);
250  double sin_iso_lat = (1 - tan2_factor) / (1 + tan2_factor);
251  double cos_iso_lat = 2 * H/(scale_factor * c_0) / (1 + tan2_factor);
252 
253  // and now the fun begins
254  double s2 = sin_iso_lat * sin_iso_lat;
255  double sc = sin_iso_lat * cos_iso_lat;
256  double s3c = s2 * sc;
257  double s5c = s2 * s3c;
258  double s7c = s2 * s5c;
259 
260  double sin2_iso_lat = 2 * sc;
261  double sin4_iso_lat = 4 * sc - 8 * s3c;
262  double sin6_iso_lat = 6 * sc - 32 * s3c + 32 *s5c;
263  double sin8_iso_lat = 8 * sc - 80 * s3c + 192* s5c - 128* s7c;
264 
265  //Well, I guess I will use trig functions here... since otherwise I'll have a very very nasty sum
266  //There is probably a better way to do this calculation, but it's quite tricky
267 
268  double correction = a_bar * sin2_iso_lat + b_bar * sin4_iso_lat + c_bar * sin6_iso_lat + d_bar * sin8_iso_lat;
269 
270  // sadly, one trig call
271  double sin_corr = sin(correction);
272  double cos_corr = cos(correction);
273  double sin_lat = fabs(sin_iso_lat * cos_corr + cos_iso_lat * sin_corr); //force positivity
274  double cos_lat = cos_iso_lat * cos_corr - sin_iso_lat * sin_corr;
275 
276  //printf(" lat: %g lon: %g\n", atan2(sin_lat, cos_lat) * TMath::RadToDeg(), atan2(sin_lon,cos_lon) * TMath::RadToDeg());
277 
278  //copied and pasted from getCartesianCoords, with a few intermediate values stored
279 
280  const double C1 = (1-FLATTENING_FACTOR) * (1-FLATTENING_FACTOR);
281 
282  double C2 = pow(cos_lat * cos_lat + C1 * sin_lat*sin_lat,-0.5);
283  double Q2 = C1* C2;
284 
285  double C3 = R_EARTH * C2 + alt;
286 
287  //same silly convention as before here
288  *x = C3 * cos_lat * sin_lon;
289  *y = C3 * cos_lat * cos_lon;
290  *z = (R_EARTH * Q2 + alt) * sin_lat;
291 }
292 
293 
294 
295 void AntarcticCoord::convert(CoordType t)
296 {
297 
298  if (type == WGS84)
299  {
300  if (t == CARTESIAN)
301  {
302  double cartesian[3];
303  AnitaGeomTool::Instance()->getCartesianCoords(x,y,z, cartesian);
304  x = cartesian[0];
305  y = cartesian[1];
306  z = cartesian[2];
307  }
308 
309  else if ( t==STEREOGRAPHIC)
310  {
312  }
313 
314  }
315 
316  else if (type == STEREOGRAPHIC)
317  {
318 
319 
320  if (t == WGS84)
321  {
323  }
324 
325  else if (t == CARTESIAN)
326  {
327 // convert(WGS84);
328 // convert(CARTESIAN);
329  stereo2cart(&x,&y,&z);
330 
331  }
332  }
333 
334  else if (type == CARTESIAN)
335  {
336 
337  if (t == WGS84)
338  {
339  double cartesian[3];
340  cartesian[0] = x;
341  cartesian[1] = y;
342  cartesian[2] = z;
343  AnitaGeomTool::Instance()->getLatLonAltFromCartesian(cartesian, x,y,z);
344  }
345  else if ( t== STEREOGRAPHIC)
346  {
347 
348 // convert(WGS84);
349 // convert(STEREOGRAPHIC);
350 
351  cart2stereo(&x,&y,&z);
352 
353  }
354  }
355 
356  else
357  {
358  fprintf(stderr,"Invalid CoordType: %d!", type);
359  return;
360  }
361 
362 
363  type = t;
364 
365 }
366 
367 
368 
370 {
371 
372  if (strstr(key,"stereographic_grid"))
373  {
374  int nx,ny,maxE,maxN;
375  ny = -1;
376  nx = 0;
377  maxE = 3330000;
378  maxN = 3330000;
379  sscanf(key,"stereographic_grid_%d_%d_%d_%d", &nx, &ny, &maxN, &maxE);
380  if (!nx) return 0;
381  if (ny < 0) ny = nx;
382  return new StereographicGrid(nx,ny,maxE,maxN);
383  }
384 
385  return 0;
386 }
387 
388 const int nsamples = 64;
389 
390 void AntarcticSegmentationScheme::DrawI(const char * opt, const int * idata, const double * range) const
391 {
392  int N = NSegments();
393  double * data = 0;
394  if (idata)
395  {
396  data = new double[N];
397  for (int i = 0; i < N; i++) { data[i] = idata[i]; }
398  Draw(opt,data,range);
399  delete [] data;
400  }
401 }
402 
403 void AntarcticSegmentationScheme::Draw(const char * opt, const double * data, const double * range) const
404 {
405  int N = NSegments();
406 
407  TGraph2D * g = new TGraph2D(N*nsamples);
408  g->SetBit(kCanDelete);
409 
410  AntarcticCoord samples[nsamples];
411 
412  for (int i =0; i < N; i++)
413  {
414  /* do not need altitude for this! */
415  sampleSegment(i, nsamples, samples, true,false);
416 
417  for (int j =0; j< nsamples; j++)
418  {
419  samples[j].to(AntarcticCoord::STEREOGRAPHIC);
420  g->SetPoint(nsamples*i+j,samples[j].x,samples[j].y,data ? data[i] : i);
421  }
422  }
423 
424  if (range)
425  {
426  g->GetXaxis()->SetRangeUser(range[0],range[1]);
427  g->GetYaxis()->SetRangeUser(range[2],range[3]);
428  }
429 
430  g->Draw(opt);
431 }
432 
433 
434 
435 StereographicGrid::StereographicGrid(int NX, int NY, double MAX_E, double MAX_N)
436  : AntarcticSegmentationScheme(), nx(NX), ny(NY), max_E(MAX_E), max_N(MAX_N)
437 {
438 
439  dx = (2*max_E)/nx;
440  dy = (2*max_N)/ny;
441 }
442 
444 {
445  AntarcticCoord stereo = coord.as(AntarcticCoord::STEREOGRAPHIC);
446 
447  if (stereo.x > max_E || stereo.x < -max_E || stereo.y > max_N || stereo.y < -max_N) return -1;
448 
449 
450  int xbin = nx/2 + stereo.x / dx;
451  int ybin = ny/2 - stereo.y / dy;
452 
453  return xbin + nx * ybin;
454 
455 }
456 
457 void StereographicGrid::getSegmentCenter(int idx, AntarcticCoord * fillme, bool fillalt) const
458 {
459 
460  int xbin = idx % nx;
461  int ybin = idx / nx;
462  double x = (xbin +0.5) *dx - max_E;
463  double y = max_N - (ybin +0.5) *dy ;
464  double z = fillalt ? getSurface<4> (dataset).compute(x,y): 0;
465  fillme->set(AntarcticCoord::STEREOGRAPHIC,x,y,z);
466 }
467 
468 
469 AntarcticCoord * StereographicGrid::sampleSegment(int idx, int N, AntarcticCoord * fill, bool random, bool fillalt) const
470 {
471 
472  if (!fill) fill = new AntarcticCoord[N];
473 
474  int xbin = idx % nx;
475  int ybin = idx / nx;
476  double lx = (xbin) *dx - max_E;
477  double ly = max_N - (ybin) *dy ;
478 
479 
480  if (random)
481  {
482  for (int i = 0; i < N; i++)
483  {
484  double x = gRandom->Uniform(lx, lx+dx);
485  double y = gRandom->Uniform(ly, ly-dy);
486  double z = fillalt ? getSurface<4> (dataset).compute(x,y): 0;
487  fill[i].set(AntarcticCoord::STEREOGRAPHIC,x,y,z);
488  }
489  }
490  else
491  {
492  int grid = sqrt(N) + 0.5 ;
493 
494  for (int i = 0; i < N; i++)
495  {
496  //TODO check if this is what i want!
497  double x = lx + (dx / (grid-1)) * ((i % grid));
498  double y = ly - (dy / (grid-1)) * ((i / grid));
499  double z = fillalt ? getSurface<4> (dataset).compute(x,y): 0;
500  fill[i].set(AntarcticCoord::STEREOGRAPHIC,x,y,z);
501  }
502  }
503 
504  return fill;
505 }
506 
507 
508 
509 void StereographicGrid::Draw(const char * opt, const double * data, const double * range) const
510 {
511  if(gDirectory->Get("tmp")){
512  delete gDirectory->Get("tmp");
513  }
514  TH2 * h = 0;
515 
516  TString stropt(opt);
517  stropt.ToUpper();
518 
519  bool do_map = false;
520  if (stropt.Contains("MAP"))
521  {
522  stropt.ReplaceAll("MAP","");
523  h = new TH2DAntarctica("tmp","Stereographic Grid", nx, ny);
524  do_map = true;
525  AntarcticCoord c;
526  for (int i = 0; i < NSegments(); i++)
527  {
528  getSegmentCenter(i,&c,false);
529  c.to(AntarcticCoord::WGS84);
530  h->Fill(c.y, c.x, data ? data[i]: i);
531  // std::cout << c.x<<" "<<c.y<<" "<<data[i]<<std::endl;
532  }
533  }
534  else
535  {
536  h = new TH2D("tmp","Stereographic Grid", nx, -max_E, max_E, ny, -max_N, max_N);
537  AntarcticCoord c;
538  for (int i = 0; i < NSegments(); i++)
539  {
540  getSegmentCenter(i,&c,false);
541  h->Fill(c.x, c.y, data ? data[i]: i);
542  // std::cout << c.x<<" "<<c.y<<" "<<data[i]<<std::endl;
543  }
544  }
545 
546 
547 
548  h->SetStats(0);
549 
550  if (range)
551  {
552 
553  h->GetXaxis()->SetRangeUser(range[0], range[1]);
554  h->GetYaxis()->SetRangeUser(range[2], range[3]);
555 
556  }
557  // h->DrawCopy(opt);
558  // std::cout<<opt<< " "<< stropt<<std::endl;
559  h->Draw(stropt);
560  // delete h;
561 }
562 
563 void StereographicGrid::asString(TString * str) const
564 {
565  str->Form("stereographic_grid_%d_%d_%d_%d", nx,ny,(int) max_E,(int) max_N);
566 }
567 
568 
569 
570 int StereographicGrid::getNeighbors(int segment, std::vector<int> * neighbors) const
571 {
572 
573  int N = 0;
574 
575  bool left_edge = (segment % nx ) == 0;
576  bool right_edge = (segment % nx ) == nx-1 ;
577  bool top_edge = (segment / ny ) == 0;
578  bool bottom_edge = (segment / ny ) == ny-1 ;
579 
580  int start_x = left_edge ? 0 : -1;
581  int end_x = right_edge ? 0 : 1;
582  int start_y = top_edge ? 0 : -1;
583  int end_y = bottom_edge ? 0 : 1;
584 
585  for (int i = start_x; i <= end_x; i++)
586  {
587  for (int j = start_y; j <= end_y; j++)
588  {
589  if (i == 0 && j == 0) continue;
590  if (i * j != 0) continue; // added by peng, only looking for 4 neighbours instead of 8 neighbours.
591 
592  N++;
593  if (neighbors) neighbors->push_back(segment + i + j *nx);
594  }
595  }
596 
597  return N;
598 }
599 
600 
601 
602 
603 static const CartesianSurfaceMap & cartmap ( RampdemReader::dataSet d )
604 {
605  if (d == RampdemReader::surface)
606  {
607  static CartesianSurfaceMap sm(1000, d);
608  return sm;
609  }
610 
611  static CartesianSurfaceMap sm;
612  return sm;
613 }
614 
615 
616 
617 
618 bool PayloadParameters::checkForCollision(double dx, AntarcticCoord * w, AntarcticCoord * w_exit, RampdemReader::dataSet d, double grace, bool reverse) const
619 {
620 
621  AntarcticCoord x = (reverse ? payload: source).as(AntarcticCoord::CARTESIAN);
622  TVector3 v = (reverse ? -1 : 1) * (payload.v() - source.v()).Unit() * dx;
623 
624 
625  while(true)
626  {
627  x.to(AntarcticCoord::CARTESIAN);
628  x.x+= v.x();
629  x.y+= v.y();
630  x.z+= v.z();
631 
632  double height_above_surface = cartmap(d).metersAboveIce(x.x, x.y, x.z);
633 
634  if ( (!reverse && height_above_surface> 5000) || (reverse && (x.v() - source.v()).Mag2() < dx*dx))
635  break;
636 
637  if (height_above_surface + grace < 0 )
638  {
639 // printf("BOOM! alt(%g,%g,%g)= %g\n", x.x, x.y, x.z, cartmap().surface(x.x, x.y));
640  if (w)
641  {
642  *w = x;
643  }
644 
645  //figure out where no longer a collision
646  if ( w_exit)
647  {
648  while (true)
649  {
650  x.x+= v.x();
651  x.y+= v.y();
652  x.z+= v.z();
653  height_above_surface = cartmap(d).metersAboveIce(x.x, x.y, x.z);
654 
655  if (height_above_surface + grace >=0 )
656  {
657  *w_exit = x;
658  break;
659  }
660  else if ( reverse && (x.v() - source.v()).Mag2() < dx*dx)
661  {
662  *w_exit = source;
663  break;
664  }
665 
666  }
667  }
668 
669  return true;
670  }
671  }
672 
673 
674  return false;
675 
676 
677 }
678 
679 
680 PayloadParameters::PayloadParameters(const PayloadParameters & other)
681 {
682  source_phi = other.source_phi;
683  source_theta = other.source_theta;
684  payload_el = other.payload_el;
685  payload_az = other.payload_az;
686  distance = other.distance;
687  payload = other.payload;
688  source = other.source;
689 }
690 //binary search to get the horizon.
691 double PayloadParameters::getHorizon(double phi, const Adu5Pat * gps, const Refraction::Model * refractionModel, double tol, RampdemReader::dataSet rampdemData) {
692 #ifdef USE_GEOGRAPHIC_LIB
693  GeographicLib::GeodesicLine gl(GeographicLib::Geodesic::WGS84(), gps->latitude, gps->longitude, gps->heading - phi);
694  double low = 0; //low distance is 0 km
695  double high = 1000 * 1000 ;// high distance is 1000km, it means start from the (long, lat) of payload and go 1000km towards the phi direction. as used in the later function gs.Posiiton()
696  int count = 0;
697  while (1){
698  count ++;
699  // force get out of loop if count reach 100, something wrong happen, horizon did not find.
700  double mid = (low + high)/2.0;
701  double lat, lon;
702  gl.Position(mid, lat, lon);
703  AntarcticCoord coordinate(AntarcticCoord::WGS84, lat, lon, RampdemReader::SurfaceAboveGeoid(lon,lat,rampdemData));
704  PayloadParameters payloadParameter = PayloadParameters(gps, coordinate, refractionModel);
705  //if the elevation of payload is nearly 0, return the horizon theta in payload frame
706  if (fabs(payloadParameter.payload_el) < tol or count > 100){
707  if (count > 100){
708  std::cout<<"The loop never stop. the last payload elevation(0 is better) is: "<<payloadParameter.payload_el<<std::endl;
709  }
710  // std::ofstream myfile;
711  // myfile.open ("horizon.txt", std::ios::app);
712  // myfile << -1*payloadParameter.source_theta <<"\n";
713  // myfile.close();
714  return payloadParameter.source_theta;
715  }
716  if (payloadParameter.payload_el > 0 ){
717  low = mid;
718  }else{
719  high = mid;
720  }
721  }
722 #endif
723 
724 }
725 
726 int PayloadParameters::findSourceOnContinent(double theta, double phi, const Adu5Pat * gps, PayloadParameters * p,
727  const Refraction::Model * m,
728  double collision_check_dx,
729  double min_dx, double tol, double min_el,
730  RampdemReader::dataSet d)
731 {
732  //no chance.
733  if (theta < 0 )
734  {
735  return 0;
736  }
737 
738  AntarcticCoord payload(AntarcticCoord::WGS84, gps->latitude, gps->longitude, gps->altitude);
739  AntarcticCoord x = payload.as(AntarcticCoord::CARTESIAN);
740 
741 
742 #ifdef USE_GEOGRAPHIC_LIB
743  GeographicLib::GeodesicLine gl(GeographicLib::Geodesic::WGS84(), gps->latitude, gps->longitude, gps->heading - phi);
744 
745  size_t i = 1;
746  double step = 1000*300; //start with 300 km step
748  int loopCount = 0;
749  while (loopCount<100) //simply hard coded the upper limit of the loop, otherwise it may stuck in the loop forever.
750  {
751  loopCount++;
752  double lat, lon;
753  gl.Position(i * step, lat, lon);
754  AntarcticCoord c(AntarcticCoord::WGS84, lat, lon, RampdemReader::SurfaceAboveGeoid(lon,lat,d));
755  *p = PayloadParameters(gps, c, m);
756 
757  //printf("%f::%f %f::%f:: %g %f\n", phi, p->source_phi, theta, p->source_theta, p->payload_el, i * step);
758 
759 
760  //we found something that works
761  if (fabs(p->source_phi -phi) < tol && fabs(p->source_theta - theta) < tol && p->apparent_payload_el>= min_el)
762  {
763  //check for a collision,
764  //then go to exit point?
765  if (collision_check_dx && p->checkForCollision( TMath::Min(collision_check_dx,step), 0, &c, d))
766  {
767  step= TMath::Min(collision_check_dx, step) ;
768  double dist = AntarcticCoord::surfaceDistance(payload, c);
769  i = dist/step;
770  step = dist/i;
771  continue;
772  }
773 
774  return 1;
775  }
776 
777  //overshot the source, or are over the horizon with too large a step size , we should lower the step size and go back
778  if (p->source_theta < theta || (p->payload_el < min_el && step > min_dx))
779  {
780  step = step / 2;
781  i = i*2-1;
782  continue;
783  }
784  else if (p->payload_el < min_el)
785  {
786  *p = ok;
787  return 0;
788  }
789  else if ( step < min_dx)
790  {
791  return -1;
792  }
793 
794  //store last ok thing)
795  if (p->payload_el >= min_el)
796  {
797  ok = *p;
798  }
799 
800  i++;
801  }
802 
803 
804 
805  #else
806 
807  /* This doesn't work yet
808  //use great ellipse
809 
810  //Find vector normal to our plane, use 0,0,0 as our point
811  TVector3 d = (x.x,x.y,0); //north pointing vector
812  d.RotateZ(gps->heading);
813 
814  TVector3 n = x.v().Cross(d);
815 
816  // The intersection of the plane and geoid will be an ellipse. We must follow it.
817  // This sucks.
818 
819  while(true)
820  {
821  //somehow propagate along ellipse
822 
823  //form the coordinates on the ice surface
824  AntarcticCoord s(AntarcticCoord::CARTESIAN, x2.X(), x2.Y(), cartmap().z(x2.X(), x2.Y()));
825  TString str;
826  s.asString(&str);
827  printf("%s\n",str.Data());
828 
829  PayloadParameters pp(x,gps->heading, s);
830 
831  printf("%f::%f %f::%f\n", phi, pp.source_phi, theta, pp.source_theta);
832  if (isnan(s.z)) return 0;
833 
834  if (fabs(pp.source_phi -phi) < tol && fabs(pp.source_theta - theta) < tol)
835  {
836  return new PayloadParameters(pp);
837  }
838  }
839  */
840 #endif
841 
842  return 0;
843 }
844 
845 
846 CartesianSurfaceMap::CartesianSurfaceMap(double resolution , RampdemReader::dataSet d)
847 {
848  // figure out the corners of the stereographic projection in cartesian.
849 
850  AntarcticCoord sa(AntarcticCoord::STEREOGRAPHIC);
851  AntarcticCoord sb(AntarcticCoord::STEREOGRAPHIC);
852  AntarcticCoord sc(AntarcticCoord::STEREOGRAPHIC);
853  AntarcticCoord sd(AntarcticCoord::STEREOGRAPHIC);
854 
855  double xu,xl, yl,yu;
856  RampdemReader::getMapCoordinates(xl,yl,xu,yu, d);
857 
858  sa.x = xl;
859  sa.y = yl;
860  sb.x = xu;
861  sb.y = yl;
862  sc.x = xu;
863  sc.y = yu;
864  sd.x = xl;
865  sd.y = yu;
866 
867 
868  sa.to(AntarcticCoord::CARTESIAN);
869  sb.to(AntarcticCoord::CARTESIAN);
870  sc.to(AntarcticCoord::CARTESIAN);
871  sd.to(AntarcticCoord::CARTESIAN);
872 
873  double xmin = TMath::Min ( TMath::Min( sa.x,sb.x), TMath::Min(sc.x,sd.x));
874  double xmax = TMath::Max ( TMath::Max( sa.x,sb.x), TMath::Max(sc.x,sd.x));
875  double ymin = TMath::Min ( TMath::Min( sa.y,sb.y), TMath::Min(sc.y,sd.y));
876  double ymax = TMath::Max ( TMath::Max( sa.y,sb.y), TMath::Max(sc.y,sd.y));
877 
878 
879  int nbinsx = (xmax - xmin)/ resolution;
880  int nbinsy = (ymax - ymin)/ resolution;
881  map = new TH2D("cartmap","Cartesian Map", nbinsx, xmin, xmax, nbinsy, ymin,ymax);
882  map->SetDirectory(0);
883 
884  for (int i = 1; i <= nbinsx; i++)
885  {
886  for (int j = 1; j <= nbinsy; j++)
887  {
888  // get cartesian point on geoid...
889  double x = map->GetXaxis()->GetBinCenter(i);
890  double y = map->GetYaxis()->GetBinCenter(j);
891 
892  static const double a = 6378137;
893  static const double b = 6356752.31424518;
894  double z = b * sqrt( 1 - (1./(a*a))*(x*x+y*y));
895  AntarcticCoord c(AntarcticCoord::CARTESIAN, x,y,z);
896 
897  double R = c.v().Mag();
898 
899  c.to(AntarcticCoord::STEREOGRAPHIC);
900  double alt = 0;
901  if (c.x > xl && c.x < xu && c.y > yl && c.y < yu)
902  {
903  alt = getSurface<4>(d).map->Interpolate(c.x, c.y);
904  }
905 
906  map->SetBinContent(i,j, R + alt);
907  }
908  }
909 }
910 
911 
912 double CartesianSurfaceMap::CartesianSurfaceMap::surface(double x, double y) const
913 {
914  return map->Interpolate(x,y);
915 }
916 
917 double CartesianSurfaceMap::CartesianSurfaceMap::z(double x, double y) const
918 {
919  double r = surface(x,y);
920  return sqrt( r*r - x*x - y*y);
921 }
922 
923 
924 
925 double CartesianSurfaceMap::metersAboveIce(double x, double y, double z) const
926 {
927  return sqrt(x*x + y*y+z*z) - surface(x,y);
928 }
929 
930 
931 
932 #ifndef USE_GEOGRAPHIC_LIB
933 static int nagged_about_surface = 0;
934 static double hav(double phi) { return ((1.-cos(phi))/2.); }
935 #endif
936 
938 {
939  AntarcticCoord A = a.as(WGS84);
940  AntarcticCoord B = b.as(WGS84);
941  return surfaceDistance(A.x, B.x, A.y, B.y);
942 
943 
944 }
945 double AntarcticCoord::surfaceDistance(double lat0 , double lat1, double lon0, double lon1)
946 {
947  if (lat0 < -90 || lat0 < -90 || lon0 < -180 || lon1 < -180) return -999;
948 #ifndef USE_GEOGRAPHIC_LIB
949 
950  if (!nagged_about_surface)
951  {
952  nagged_about_surface++;
953  fprintf(stderr,"WARNING: compiled without geographic lib support. Using haversine distance between points\n");
954  }
955  lat0 *= TMath::DegToRad();
956  lat1 *= TMath::DegToRad();
957  lon0 *= TMath::DegToRad();
958  lon1 *= TMath::DegToRad();
959  double a = hav(lat1-lat0) + cos(lat0) * cos(lat1) * hav(lon1-lon0);
960  double c = 2 * atan2(sqrt(a), sqrt(1-a));
961  double d = R_EARTH * c;
962  return d;
963 #else
964 
965  double distance;
966  GeographicLib::Geodesic::WGS84().Inverse( lat0,lon0,lat1,lon1, distance);
967  return distance;
968 #endif
969 
970 }
971 
972 
struct __attribute__((packed))
Debugging use only TURF scaler data.
static TProfile2D * getMap(RampdemReader::dataSet dataSet, int coarseness_factor)
virtual void Draw(const char *opt="colz", const double *data=0, const double *range=0) const
static Double_t SurfaceAboveGeoid(Double_t longitude, Double_t latitude, RampdemReader::dataSet=rampdem)
virtual AntarcticCoord * sampleSegment(int idx, int N, AntarcticCoord *fillus=0, bool random=true, bool fillalt=true) const
Adu5Pat – The ADU5 Position and Attitude Data.
Definition: Adu5Pat.h:26
Float_t latitude
In degrees.
Definition: Adu5Pat.h:42
virtual AntarcticCoord * sampleSegment(int idx, int N, AntarcticCoord *fillus=0, bool random=true, bool fillalt=true) const =0
Float_t longitude
In degrees.
Definition: Adu5Pat.h:43
static double getHorizon(double phi, const Adu5Pat *gps, const Refraction::Model *refractionModel=0, double tol=5e-6, RampdemReader::dataSet rampdemData=RampdemReader::rampdem)
static void getMapCoordinates(double &xMin, double &yMin, double &xMax, double &yMax, RampdemReader::dataSet=rampdem)
virtual void getSegmentCenter(int idx, AntarcticCoord *fill, bool fillalt=true) const
Float_t altitude
In metres.
Definition: Adu5Pat.h:44
static AntarcticSegmentationScheme * factory(const char *key)
Inelasticity distributions: stores parametrizations and picks inelasticities.
Definition: Primaries.h:38
void wrap(size_t N, double *vals, double period=360)
Definition: FFTtools.cxx:2851
static void LonLatToEastingNorthing(Double_t lon, Double_t lat, Double_t &easting, Double_t &northing)
static double surfaceDistance(const AntarcticCoord &a, const AntarcticCoord &b)
virtual void Draw(const char *opt="colz", const double *data=0, const double *range=0) const
virtual int getNeighbors(int segment, std::vector< int > *neighbors=NULL) const
virtual int getSegmentIndex(const AntarcticCoord &coord) const
Float_t heading
0 is facing north, 180 is facing south
Definition: Adu5Pat.h:45
static AnitaGeomTool * Instance(int anita_version=0)
Instance generator. If version_number == 0, uses AnitaVersion::get();.
static void EastingNorthingToLonLat(Double_t easting, Double_t northing, Double_t &lon, Double_t &lat)