package Orbit; # Copyright (C) 2008 Bernhard W. Adams # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . use strict; use Math::Trig; use POSIX; # floor() use aux; # aux. routines: # epoch_to_date, date_to_epoch, dumpvars, msg, ems my ($pi,$deg,$AU,$day,$np,$INF,$INVALID,$E_ZZ,$ORBIT_VAL,$ORBIT_INV); $pi=2*asin(1.0); $deg=$pi/180.0; $AU=1.5e8; $day=3600*24; $np=1.0e-12; # num. prec. $INF=1.0e30; $INVALID=1.0e30; $E_ZZ=-2; # flag for e="0/0" in determination of ellipse, below $ORBIT_INV=-1; $ORBIT_VAL=1; # orbit is valid # scratchpad for given set of end points (space and time) my (@tr_t,@orb_per,@ecc,@Tfa); # [0 ... (ntheta+2)] ############################## # # construct an orbit # with the orbital parameters # $a km, s.m. axis # $e rad, eccentricity # $i rad, inclination # $an rad, long. of ascending node # $om rad, arg. of periapsis # $M0 rad, mean anomaly at epoch # $tp s, time of periapsis closest to epoch # $ep s, standard epoch # !!!! only one of M0, tp needed, the other will be calculated # $mu km^2/s^2, grav. constant # $I in rad, not used here # $L in rad, mean long at time t_L # $om # $no # $t_L # $mu in km^3/s^2 # $ps, $pe, $tra, $orb: filenames for positions, trajectory, orbit #### $tp in s # ############################## sub new { my ($class) = shift; my ($a,$dadt,$e,$dedt,$i,$didt,$an,$dandt,$ep,$om,$omb,$domdt,$L0,$M0,$tp,$mu, $dir,$name,$ps,$pe,$tra,$orb,$rps,$rpe,$rtra,$rorb,$deltav,$ttlog,$from, $to,$nlp,$diag)=@_; my ($M,$op,$t); # aux::dumpvars("L0 M0",($L0,$M0)); my $this = { _name => undef, _group => undef, _a => undef, _dadt => undef, _e => undef, _dedt => undef, _i => undef, _didt => undef, _an => undef, _dandt => undef, _ep => undef, _om => undef, _domdt => undef, _M0 => undef, _mu => undef, _dir => undef, _op => undef, _PS => "undef", _PE => "undef", _TR => "undef", _OR => "undef", _RPS => "undef", _RPE => "undef", _RTR => "undef", _ROR => "undef", _deltav => "undef", _from => undef, _to => undef, _nlp => undef, _complete => undef, _ntheta => undef, _rfx => undef, _rfy => undef, _rfz => undef, _rfa => undef, _rtx => undef, _rty => undef, _rtz => undef, _rta => undef, _DT => undef, _tf => undef, _tt => undef, _ntheta => undef, _diag => undef }; # _M0 is the mean anomaly at ref. epoch _ep # M is the mean anomaly last calculated for given time _t bless $this, $class; $this->{_ntheta}=0; $this->{_complete} = $ORBIT_VAL; # set to 0 if not all orbital parameters are init. # set to -1 if invalid orbit if(($this->{_e}=$e) eq "undef") {$this->{_complete}=0;} if(($this->{_dedt}=$dedt) eq "undef") {$this->{_complete}=0;} if(($this->{_a}=$a) eq "undef") {$this->{_complete}=0;} if(($this->{_dadt}=$dadt) eq "undef") {$this->{_complete}=0;} if(($this->{_i}=$i) eq "undef") {$this->{_complete}=0;} if(($this->{_didt}=$didt) eq "undef") {$this->{_complete}=0;} if(($this->{_an}=$an) eq "undef") {$this->{_complete}=0;} if(($this->{_dandt}=$dandt) eq "undef") {$this->{_complete}=0;} if($om ne "undef") { $this->{_om} = $om; $this->{_domdt} = $domdt; } else { if($omb eq "undef") {$this->{_complete}=0;} $this->{_om} = $omb-$an; $this->{_domdt} = $domdt-$dandt; } if(($this->{_mu}=$mu) eq "undef") {$this->{_complete}=0;} $op=2*$pi*sqrt($a*$a*$a)/sqrt($mu); # orb. period in seconds $this->{_dir}=$dir; $this->{_op}=$op; # http://perldoc.perl.org/perlsyn.html#Loop-Control for switch statement if( (($L0 == "undef") || ($ep == "undef")) && (($M0 == "undef") || ($ep == "undef")) && ($tp == "undef")) {$this->{_complete}=0;} if(($L0 != "undef") && ($ep != "undef")) { $M0=$L0-$this->{_om}-$this->{_an}; $tp=$ep-$op*$M0/(2*$pi); } else { if(($M0 != "undef") && ($ep != "undef")) { $tp=$ep-$op*$M0/(2*$pi); } else { # $tp print $name." tp\n"; $M0=0; $ep=$tp; } } $this->{_M0} = $M0; $this->{_ep} = $ep; $this->{_PS} = $ps; $this->{_PE} = $pe; $this->{_TR} = $tra; $this->{_OR} = $orb; $this->{_RPS} = $rps; $this->{_RPE} = $rpe; $this->{_RTR} = $rtra; $this->{_ROR} = $rorb; $this->{_deltav} = $deltav; $this->{_name} = $name; $this->{_from} = $from; $this->{_ttlog} = $ttlog; $this->{_to} = $to; $this->{_nlp} = $nlp; if($diag eq "yes") {$this->{_diag}=1;} else {$this->{_diag}=0;} return $this; } ############################## # number of loops ############################## sub nlp {my ($ob) = shift; $ob->{_nlp};} ############################## # time of periapsis ############################## sub tp { my ($ob) = shift; my $tp; $tp=$ob->{_ep}-$ob->{_op}*$ob->{_M0}/(2*$pi); return $tp; } ############################## # orbital period, using the values a and mu from orbit initialization ############################## sub P {my ($ob) = shift; $ob->{_op};} ############################## # returns the object's name ############################## sub name {my ($ob) = shift; $ob->{_name};} ############################## # returns the object's "from" name ############################## sub from {my ($ob) = shift; $ob->{_from};} ############################## # returns the object's "to" name ############################## sub to {my ($ob) = shift; $ob->{_to};} ############################## # returns the object's "to" name ############################## sub valid { my ($ob) = shift; if($ob->{_complete} == $ORBIT_VAL) {return 1;} else {return 0;} } ################################ # position of orbiting body at time t # in polar coordinates relative to this orbit # t: time # returns array (r,M,E,T) ################################ sub posn_pol_rel { my $ME="posn_pol_rel"; # for error tracking my ($ob) = shift; # this my ($t,$CF)=@_; # time, called from (error tracking) my ($tr,$no,$r,$M,$E,$T,@res); my ($e,$a,$tp,$om,$mu,$op,$Dt); $Dt=$t-$ob->{_ep}; $a=$ob->{_a}+$ob->{_dadt}*$Dt; $e=$ob->{_e}+$ob->{_dedt}*$Dt; # $tp=$ob->{_tp}; $tp=$ob->{_ep}-$ob->{_op}*$ob->{_M0}/(2*$pi); $om=$ob->{_om}+$ob->{_domdt}*$Dt; $mu=$ob->{_mu}; $op=$ob->{_op}; $tr=$t-$tp; # time since ref periapsis $no=floor($tr/$op); # no. of full orbits $tr-=$no*$op; # time since last periapsis if($ob->{_dir}==1) {$M=2*$pi*$tr/$op;} # mean anomaly else {$M=2*$pi*(1-$tr/$op);} if(($M<0) || ($M>2*$pi)) {aux::ems("bug in ".$ME." ".$CF.", M =".$M);} # $E=$ob->Kepler($M,$ME." ".$CF); # $T=2*atan(tan($E/2)*sqrt(1+$e)/sqrt(fabs(1-$e))); # check this: fabs() ($E,$T)=$ob->Kepler($M,$ME." ".$CF); if($e<1) {$r=$a*(1-$e*$e)/(1+$e*cos($T));} else {$r=$a*($e*$e-1)/(1+$e*cos($T));} @res=($r,$M,$E,$T); } ################ # # solves the Kepler equation M=E-e*sin E # for E with given M, e # using Danby's method # http://www.cdeagle.com/ccnum/pdf/demokep1.pdf # http://www.projectpluto.com/kepler.htm ################ sub Kepler { my $ME="Kepler"; my ($ob)=shift; my ($M,$CF)=@_; # my ($i,$E,$e,$m,$dm,$dE,$n,@Ea); my ($e,$E,$T,$f,$fp,$fpp,$fppp,$D,$Da,$nit,@res); my $maxit=300; $e=$ob->{_e}; if($e<0) {die "negative eccentricity not allowed here";} $nit=0; if($e<1) { # ellipse if(sin($M)>0) {$E=$M+0.85*$e;} else {$E=$M-0.85*$e;} do { $f=$E-$e*sin($E)-$M; $fp=1-$e*cos($E); $fpp=$e*sin($E); $fppp=$e*cos($E); $D=-$f/$fp; $Da=-$f/($fp+0.5*$D*$fpp); $E-=$f/($fp+0.5*$D*$fpp+$Da*$Da*$fppp/6); $nit+=1; if($nit>$maxit) {die;} } while(fabs($f)>$np); # $T=atan(sqrt(1-$e*$e)*sin($E))/(cos($E)-$e); $T=2*atan(sqrt((1+$e)/(1-$e))*tan(0.5*$E)); } else { # hyperbola $E=log(2*$M/$e+1.8); # log is the natural logarithm, not base-10 do{ $f=$e*sinh($E)-$E-$M; $fp=$e*cosh($E)-1; $fpp=$e*sinh($E); $fppp=$e*cosh($E); $D=-$f/$fp; $Da=-$f/($fp+0.5*$D*$fpp); $E-=$f/($fp+0.5*$D*$fpp+$Da*$Da*$fppp/6); $nit+=1; if($nit>$maxit) {die;} } while(fabs($f)>$np); $T=atan(sqrt($e*$e-1)*sinh($E))/($e-cosh($E)); } return @res=($E,$T); } ################ # # solves the Kepler equation M=E-e*sin E # for E with given M, e # http://www.cdeagle.com/ccnum/pdf/demokep1.pdf # http://www.projectpluto.com/kepler.htm ################ sub _Kepler { my $ME="Kepler"; my ($ob)=shift; my ($M,$CF)=@_; my ($i,$E,$e,$m,$dm,$dE,$n,@Ea,$T,@res); my $maxit=300; $e=$ob->{_e}; $E=$M; $n=0; do { $m=$E-$e*sin($E); $dm=1-$e*cos($E); # if(1/$dm>$pi) {$dm=1/$pi;} $dE=($m-$M)/$dm; if(fabs($dE)>1) {$dE=$dE/fabs($dE);} $E=$E-$dE; if($E<0) {$E=0;} if($E>2*$pi) {$E=2*$pi;} $Ea[$n]=$E; if($n>$maxit) { print "M = ".$M."\n"; for($i=0;$i$M*$np); # $T=atan(sqrt(1-$e*$e)*sin($E)/(cos($E)-$e)); $T=2*atan(sqrt((1+$e)/(1-$e))*tan(0.5*$E)); @res=($E,$T); } ################################ # # position of orbiting body at time t # in cartesian coordinates relative to this orbit # t: time (seconds, not epoch) # returns array (x,y) ################################ sub posn_cart_rel { my $ME="posn_cart_rel"; # for error tracking my ($ob) = shift; # this my ($t,$CF)=@_; # time, called from (error tracking) my (@pol,@xy); @pol=$ob->posn_pol_rel($t,$ME." ".$CF); $xy[0]=$pol[0]*cos($pol[3]); $xy[1]=$pol[0]*sin($pol[3]); @xy; } ################################ # # position of orbiting body at time t # in polar coordinates in absolute coordinate system # t: time (seconds, not epoch) # returns array (r,phi,Theta) ################################ sub posn_pol_abs { my $ME="pos_pol_abs"; my ($ob)=shift; my ($t,$CF)=@_; my (@rel,$T,$r,$theta,$phi,@res); my ($om,$Om,$i,$Dt); $Dt=$t-$ob->{_ep}; $om=$ob->{_om}+$ob->{_domdt}*$Dt; # long. of periapsis $Om=$ob->{_an}+$ob->{_dandt}*$Dt; # long of ascending node $i=$ob->{_i}+$ob->{_didt}*$Dt; # inclination @rel=$ob->posn_pol_rel($t,$ME." ".$CF); $r=$rel[0]; # radius $T=$rel[3]; # true anomaly Theta $theta=arcsin(sin($T+$om)*sin($i)); $phi=$Om+atan(sin($Om+$om)*sin($i)/cos($Om+$om)); $res[0]=$r; $res[1]=$theta; $res[2]=$phi; @res; } ################################ # # returns absolute coordinates x,y of orbiting body at time t # t: time (seconds, not epoch) # returns array (x,y,z) in km ################################ sub posn_cart_abs { my $ME="posn_cart_abs"; my ($ob) = shift; my ($t,$CF)=@_; my ($i,$om,$Om); my ($Dt,@R,@Z,@OM); # my $test=0; $Dt=$t-$ob->{_ep}; $i=$ob->{_i}+$ob->{_didt}*$Dt; $om=$ob->{_om}+$ob->{_domdt}*$Dt; $Om=$ob->{_an}+$ob->{_dandt}*$Dt; @R=$ob->posn_cart_rel($t,$ME." ".$CF); # if(($R[0] > 0) && (fabs($R[0]/$R[1]) > 100)) {$test=1;} push @R,0; # z component # if($test==1) {print "Om, om, i = ".($Om/$deg).", ".($om/$deg).", ".($i/$deg)."\n";} # if($test==1) {print "--R = (".$R[0].", ".$R[1].", ".$R[2].")\n";} @Z=(0,0,1); @OM=(cos($Om),sin($Om),0); @R=aux::rotate(@R,@Z,$om+$Om); # if($test==1) {print "--R = (".$R[0].", ".$R[1].", ".$R[2].")\n";} @R=aux::rotate(@R,@OM,$i); # if($test==1) {print "--R = (".$R[0].", ".$R[1].", ".$R[2].")\n"; # my @tst=(-0.8,-0.6,0); # @tst=aux::rotate(@tst,-0.8,-0.6,0,2); # print "tst = (".$tst[0].", ".$tst[1].", ".$tst[2].")\n"; # } @R; } ################################ # velocity of orbiting body at time t # in rotated rectilinear coordinates relative to this orbit # t: time # returns array (r,v_r,v_t,T,v,vp) # r: radius # v_r: radial vel. # v_t: tang. vel. # v: abs. velocity # T: true anomaly ################################ sub vel_rrc_rel { my $ME="vel_rrc_rel"; # for error tracking my ($ob) = shift; # this my ($t,$CF)=@_; # time, called from (error tracking) my ($tr,$no,$r,$M,$E,$T,@res); my ($Dt,$mu,$a,$e,$rp,$vps,$vs,$v,$vr,$vt); if($ob->{_complete}!=$ORBIT_VAL) {return ($ob->{_a},$INF,$INF,$INF,0);} $Dt=$t-$ob->{_ep}; $mu=$ob->{_mu}; $a=$ob->{_a}+$ob->{_dadt}*$Dt; $e=$ob->{_e}+$ob->{_dedt}*$Dt; if($e==1.0) {$e=1.0-1.0e-12;} if($e<1.0) {$rp=(1-$e)*$a;} else {$rp=($e-1)*$a;} $vps=(1+$e)*$mu/$rp; # square of periapsis vel. ($r,$M,$E,$T)=$ob->posn_pol_rel($t,$ME." ".$CF); $vs=$vps-2*$mu/$rp+2*$mu/$r; # velocity squared $v=sqrt($vs); # velocity $vt=$rp*sqrt($vps)/$r; # constant angular momentum $vr=sqrt(fabs($vs-$vt*$vt)); # fabs to be tolerant to small neg. values due if(($T>$pi) || ($T<0)) {$vr*=-1;} # to roundoff errors if($ob->{_dir}==-1) {$vt*=-1; $vr*=-1;} @res=($r,$vr,$vt,$v,$T); } ################################ # velocity of orbiting body at time t # in polar coordinates relative to this orbit # t: time # returns array (v,vp) # v: abs. vel. # vp: phi of vel. ################################ sub vel_pol_rel { my $ME="vel_pol_rel"; # for error tracking my ($ob) = shift; # this my ($t,$CF)=@_; # time, called from (error tracking) my ($r,$vr,$vt,$v,$T,@res); ($r,$vr,$vt,$v,$T)=$ob->vel_rrc_rel($t,$ME." ".$CF); @res=($v,$T+0.5*$pi-atan($vr/$vt)); } ################################ # velocity of orbiting body at time t # in absolute cartesian coordinates # t: time # returns array (r,v_r,v_t) ################################ sub vel_cart_abs { my $ME="vel_cart_abs"; # for error tracking my ($ob) = shift; # this my ($t,$CF)=@_; # time, called from (error tracking) my ($Dt,$i,$om,$Om,$r,$vr,$vt,$v,$T,@V,@OM); $Dt=$t-$ob->{_ep}; $i=$ob->{_i}+$ob->{_didt}*$Dt; $om=$ob->{_om}+$ob->{_domdt}*$Dt; $Om=$ob->{_an}+$ob->{_dandt}*$Dt; ($r,$vr,$vt,$v,$T)=$ob->vel_rrc_rel($t,$ME." ".$CF); @V=($vr,$vt,0); @V=aux::rotate(@V,0,0,1,$T); @V=aux::rotate(@V,0,0,1,$om+$Om); @OM=(cos($Om),sin($Om),0); @V=aux::rotate(@V,@OM,$i); } ########################## # scratchpad for given set of end points (space and time) # my (@from,@to,@obpl); # [0..2] # my ($DT,$tf,$tt,$rfa,$rta); # my (@tr_t,@orb_per,@Tfa); # [0 ... (ntheta+2)] # ########################## sub set_endpoints { my $ME="set_endpoints"; my ($ob) = shift; my ($tfr,$tto,$rfx,$rfy,$rfz,$rtx,$rty,$rtz,$CF)=@_; my ($m,$Dt,$rfa,$rta,$DT,$Tf,$dTf,$ay); my ($e,$a,$tt,$op); # my ($A0,$A1,$x0,$x1,$y0,$y1,$T0,$T1,$r0,$r1,$cT0,$cT1,$x10,$x11,$x20,$x21); $ob->{_rfx}=$rfx; $ob->{_rfy}=$rfy; $ob->{_rfz}=$rfz; $ob->{_rfa}=$rfa=aux::vabs($rfx,$rfy,$rfz); $ob->{_rtx}=$rtx; $ob->{_rty}=$rty; $ob->{_rtz}=$rtz; $ob->{_rta}=$rta=aux::vabs($rtx,$rty,$rtz); $ob->{_tf}=$tfr; $ob->{_tt}=$tto; $Dt=$tto-$tfr; $ob->{_DT}=$DT=acos(aux::sc_prod($rfx,$rfy,$rfz,$rtx,$rty,$rtz)/($rfa*$rta)); if($ob->{_diag}==1) { open F,">".$ob->{_ttlog}; # for given endpoints in space & time print F "#Dt = ".($tto-$tfr)."\n"; print F "#DT = ".$DT."\n"; print F "# rfa,rta = ".$rfa.", ".$rta."\n"; print F "#1:m, 2:Tf, 3:Tf+DT, 4:e, 5:a, 6:tt, 7:op-tt, 8:tt+op, 9:op\n"; } $dTf=2*$pi/$ob->{_ntheta}; for($m=0;$m<$ob->{_ntheta};++$m) { $Tf=$m*$dTf; ($a,$e,$tt,$op)=$ob->ell_pars($Tf); $Tfa[$m+1]=$Tf; $tr_t[$m+1]=$tt; # +1 to leave space at index 0 $orb_per[$m+1]=$op; # for closure of cycle below $ecc[$m+1]=$e; # 1-7: m, Tf, Tt, e, a, tt, tt-Dt # 8-10: op-tt-Dt, tt+op-Dt, op # 10-17: diag A0, A1, x0, x1, y0, y1, T0, T1, # 18-25 r0, r1, cosT0, cosT1, x10, x11, x20, x21 if($ob->{_diag}==1) { print F $m."\t".$Tf."\t".($Tf+$DT)."\t".$e."\t".$a."\t".$tt."\t".($tt-$Dt). "\t".($op-$tt-$Dt)."\t".($tt+$op-$Dt)."\t".$op."\n"; # "\t".$A0."\t".$A1."\t".$x0."\t".$x1."\t".$y0."\t".$y1. # "\t".$T0."\t".$T1."\t".$r0."\t".$r1."\t".$cT0."\t".$cT1. # "\t".$x10."\t".$x11."\t".$x20."\t".$x21."\n"; } } $Tfa[0]=$Tfa[$ob->{_ntheta}]; $Tfa[$ob->{_ntheta}+1]=$Tfa[1]; $tr_t[0]=$tr_t[$ob->{_ntheta}]; $tr_t[$ob->{_ntheta}+1]=$tr_t[1]; # close cycle on both ends $orb_per[0]=$orb_per[$ob->{_ntheta}]; $orb_per[$ob->{_ntheta}]=$orb_per[1]; # close cycle on both ends if($ob->{_diag}==1) {close F;} } ################## # # times from - to in days (epoch) # by definition of DT (acos above) the ellipses are all # calculated so that the arc does not sweep across the sun # ############################## sub transfer_trajectory { my $ME="transfer_trajectory"; my ($ob)=shift; my ($nl)=@_; my (@from,@to,@obpl,@pa,@an,@po); # all [0... 2] my ($Dt); my ($m,$a,$e,$tt,$op,$ta,$ea,$ay,$mm); my ($time,$Tl,$Tr,$Tm,$Tmn,$oTm,$ttl,$ttr,$ttm,$ottm,$expand); my ($dT,$trt,$hyp,$nit,$min_dt); my (@mt); # [1...ntheta] # ntheta is def'd in new() my (@res); # [0..3] my $maxit=100; @from=($ob->{_rfx},$ob->{_rfy},$ob->{_rfz}); @to=($ob->{_rtx},$ob->{_rty},$ob->{_rtz}); @obpl=aux::cross(@from,@to); @obpl=aux::v_prod_s(@obpl,1/aux::vabs(@obpl)); if($obpl[2]<0) {$nl=-$nl-1;} if($nl>-1) {$ob->{_dir}=1;} else {$ob->{_dir}=-1;} @an=aux::cross(0,0,1,@obpl); # vector towards ascending node if(aux::vabs(@an)==0) {@an=(1,0,0);} # if origin, dest. trajectories have i=0 @an=aux::v_prod_s(@an,1/aux::vabs(@an)); # unit vector ... $ay=aux::sc_prod(0,1,0,@an); if($ay>0) {$ob->{_an}=acos(aux::sc_prod(@an,1,0,0));} else {$ob->{_an}=2*$pi-acos(aux::sc_prod(@an,1,0,0));} $ob->{_dandt}=0; if($obpl[2]>0) {$ob->{_i}=asin(aux::vabs(aux::cross(0,0,1,@obpl)));} else {$ob->{_i}=$pi-asin(aux::vabs(aux::cross(0,0,1,@obpl)));} $ob->{_didt}=0; $Dt=$ob->{_tt}-$ob->{_tf}; $min_dt=$INF; # $nl=0... : add $nl full orb. periods # $nl=0 : less than 1 orb. period forward # $nl=-1 : less than 1 orb. period backward # $nl<-1 : less than 1 orb. backw. PLUS -$nl-1 full orb backw. # find closest match over $m for($m=1;$m<=$ob->{_ntheta};++$m) { ####### if($ecc[$m]<1) { # ellipse if($nl>=0) {$time= $tr_t[$m]+$nl*$orb_per[$m];} else {$time=-$nl*$orb_per[$m]-$tr_t[$m];} } else { # hyperbola if(($nl==0)||($nl==-1)){if(($time=2*($nl+0.5)*$tr_t[$m])<0) {$time=$INF;}} else {$time=$INF;} } $mt[$m]=$time-$Dt; } for($m=1;$m<=$ob->{_ntheta};++$m) { ######## if((fabs($mt[$m])=0)&&($ecc[$m]>=0)&&($ecc[$m+1]>=0)) { $min_dt=$mt[$m]; $mm=$m; } # closest transit time if # tr. times left and right are both valid } # end for($m) $Tl=$Tfa[$mm-1]; $ttl=$mt[$mm-1]; $Tm=$Tfa[$mm+0]; $ttm=$mt[$mm+0]; $Tr=$Tfa[$mm+1]; $ttr=$mt[$mm+1]; if($ob->{_diag}==1) { open F,">exp.log"; print F "#nl = ".$nl."\n"; print F "#Dt = ".$Dt."\n"; print F "#Tl = ".$Tl."\n"; print F "#Tm = ".$Tm."\n"; print F "#Tr = ".$Tr."\n"; print F "#ttl = ".$ttl."\n"; print F "#ttm = ".$ttm."\n"; print F "#ttr = ".$ttr."\n"; } $expand=0; if($ttl*$ttr>0) {# no zero crossing between Tl and Tr ? --> expand interval if($ob->{_diag}==1) { print F "#min_dt = ".$min_dt."\n"; print F "#Dt = ".$Dt."\n"; print F "#ecc_l = ".$ecc[$mm-1]."\n"; print F "#ecc_m = ".$ecc[$mm]."\n"; print F "#ecc_r = ".$ecc[$mm+1]."\n"; } if(($ttl+$ttr)*($ttr-$ttl)>0) {$expand=-1;} # mean * 'slope' else {$expand=1;} # expand Theta interval to left if($ob->{_diag}==1) {print F "#expand = ".$expand."\n";} ($a,$e,$trt,$op)=$ob->ell_pars($Tl); if($e<0) {die;} ##############????? ($a,$e,$trt,$op)=$ob->ell_pars($Tr); if($e<0) {die;} $nit=0; $dT=($Tr-$Tl); # 2* Tf resolution do { # extrapolation ######### here $dT/=2; # 1* Tf resolution if($expand == 1) { # expand interval towards right # print ">"; ($a,$e,$trt,$op)=$ob->ell_pars($Tr+$dT); if($e==$E_ZZ) { $ob->{_complete}=$ORBIT_INV; if($ob->{_diag}==1) { print F "# ZZ>\n"; print F $Tl."\t".$Tr."\t".$a."\t".$e."\t".$trt."\t".$op."\n"; } return @res=(0,-1,-1,$INF); } if($e>=0) { # if valid ellipse/hyp. $Tr+=$dT; if($e<1) { # ellipse if($nl>=0) {$ttr= $trt+$nl*$op-$Dt;} else {$ttr=-$nl*$op-$trt-$Dt;} } else { # hyperbola if(($nl==0)||($nl==-1)) {if(($ttr=2*($nl+0.5)*$trt)<0) {$ttr=$INF;}} else {$ttr=$INF;} } } } else { # print "<"; ($a,$e,$trt,$op)=$ob->ell_pars($Tl-$dT); if($e==$E_ZZ) { $ob->{_complete}=$ORBIT_INV; if($ob->{_diag}==1) { print F "# ZZ<\n"; print F "# rf, rt = ".$ob->{_rfa}."\t".$ob->{_rta}."\n"; print F $Tl."\t".$Tr."\t".$a."\t".$e."\t".$trt."\t".$op."\n"; } return @res=(0,-1,-1,$INF); } if($e>=0) { $Tl-=$dT; if($e<1) { # ellipse if($nl>=0) {$ttl= $trt+$nl*$op-$Dt;} else {$ttl=-$nl*$op-$trt-$Dt;} } else { # hyperbola if(($nl==0)||($nl==-1)) {if(($ttl=2*($nl+0.5)*$trt)<0) {$ttl=$INF;}} else {$ttl=$INF;} } } } # 1-6 if($ob->{_diag}==1) { print F $Tl."\t".$Tr."\t".$a."\t".$e."\t".$trt."\t".$op. "\t".$ttl."\t".$ttr."\n"; } # 7-8 $nit+=1; if($nit>$maxit) {close F; return @res=(0,-1,-1,$INF);} # } while(($ttr+$ttl)*($ttr-$ttl)>0); } while(($ttl*$ttr>0) || ($ttl == $INF) || ($ttr == $INF)); if($ob->{_diag}==1) {close F;} } # end if($ttl*$ttr>0) # print "----------"; # now we need only to interpolate, not extrapolate $nit=0; # print "tt_{l,m,r} = ".$ttl.", ".$ttm.", ".$ttr."\n"; # print "T_{l,m,r}, ttm = ".$Tl.", ".$Tm.", ".$Tr." ".$ttm."\n"; if($ob->{_diag}==1) {open F, ">int.log"; print F "# Tl, Tr = ".$Tl.", ".$Tr."\n";} if($Tm<$Tl) {$Tm+=2*$pi;} # for wraparound 2*pi - \epsilon --> + \epsilon if($Tr<$Tl) {$Tr+=2*$pi;} # for wraparound 2*pi - \epsilon --> + \epsilon if(($Tl>$Tr) || ($Tm>$Tr) || ($Tm<$Tl)) {die $Tl.", ".$Tm.", ".$Tr;} if($ob->{_diag}==1) { print F "#expand = ".$expand."\n"; print F "#nl = ".$nl."\n"; print F (-1).", ".$Tl.", ".$Tm.", ".$Tr.", ". $ttl.", ".$ttm.", ".$ttr.", ".$ecc[$mm-1].", ". $ecc[$mm].", ".$ecc[$mm+1]."\n";} do { # we are entering this loop if $ttl*$ttr < 0 $Tm=0.5*($Tl+$Tr); ($a,$e,$trt,$op)=$ob->ell_pars($Tm); if($e<0) { $ob->{_complete}=$ORBIT_INV; return @res=(0,-1,-1,$INF); # (a, e, Tm, op) } if($a==-1) {die $e.", ".$trt;} if($e<1) { # ellipse if($nl>=0) {$ttm= $trt+$nl*$op-$Dt;} else {$ttm=-$nl*$op-$trt-$Dt;} } else { # hyperbola if(($nl==0)||($nl==-1)) {if(($ttm=2*($nl+0.5)*$trt)<0) {$ttm=$INF;}} else {$ttm=$INF;} } if($ttm*$ttr>0) {$Tr=$Tm; $ttr=$ttm;} else {$Tl=$Tm; $ttl=$ttm;} if($ob->{_diag}==1) {print F $nit.", ".$Tl.", ".$Tm.", ".$Tr.", ". $ttl.", ".$ttm.", ".$ttr."\n";} $nit+=1; } while(($nit<$maxit) && (fabs($ttm/$Dt) > 1.0e-12) && ($Tl != $Tr)); if($ob->{_diag}==1) {close F;} if($nit==$maxit) {print F "max no. of iterations ".$maxit."\n";} $ob->{_a}=$a; $ob->{_dadt}=0; $ob->{_e}=$e; $ob->{_dedt}=0; $ob->{_op}=$op; # calculate orbital parameters @pa=aux::rotate(@from,@obpl,-$Tm); @pa=aux::v_prod_s(@pa,1/aux::vabs(@pa)); # unit vector ... my $fa=aux::vabs(@from); @po=aux::cross(@obpl,@an); if(aux::sc_prod(@po,@pa)>0) {$ob->{_om}=acos(aux::sc_prod(@an,@pa));} else {$ob->{_om}=2*$pi-acos(aux::sc_prod(@an,@pa));} $ob->{_domdt}=0; $ob->{_ep}=$ob->{_tf}; $ta=$Tm; # true anomaly if($e<1) { # ellipse $ea=2*atan(tan($ta/2)*sqrt(1-$e)/sqrt(1+$e)); # eccentric anomaly if($ob->{_dir}==1) {$ob->{_M0}=$ea -$e*sin($ea);} else {$ob->{_M0}=2*$pi-($ea -$e*sin($ea));} $ob->{_op}=2*$pi*sqrt($a*$a*$a)/sqrt($ob->{_mu}); $ob->{_complete}=1; } @res=($a,$e,$Tm,$op); } ###################### # # # ###################### sub _transfer_trajectory { my $ME="transfer_trajectory"; my ($ob)=shift; my ($tfr,$tto,$rfx,$rfy,$rfz,$rtx,$rty,$rtz,$nl,$CF)=@_; my ($rfa,$rta,$Dt,$DT); my ($c,$e,$a,$ay); my (@from,@to,@obpl,@an); # $ob->{_rfx}=$rfx; $ob->{_rfy}=$rfy; $ob->{_rfz}=$rfz; $ob->{_rfa}=$rfa=aux::vabs($rfx,$rfy,$rfz); $ob->{_rtx}=$rtx; $ob->{_rty}=$rty; $ob->{_rtz}=$rtz; $ob->{_rta}=$rta=aux::vabs($rtx,$rty,$rtz); $ob->{_tf}=$tfr; $ob->{_tt}=$tto; $Dt=$tto-$tfr; $ob->{_DT}=$DT=acos(aux::sc_prod($rfx,$rfy,$rfz,$rtx,$rty,$rtz)/($rfa*$rta)); # @from=($rfx,$rfy,$rfz); @to=($rtx,$rty,$rtz); @obpl=aux::cross(@from,@to); @obpl=aux::v_prod_s(@obpl,1/aux::vabs(@obpl)); if($obpl[2]<0) {$nl=-$nl-1;} if($nl>-1) {$ob->{_dir}=1;} else {$ob->{_dir}=-1;} @an=aux::cross(0,0,1,@obpl); # vector towards ascending node @an=aux::v_prod_s(@an,1/aux::vabs(@an)); # unit vector ... $ay=aux::sc_prod(0,1,0,@an); if($ay>0) {$ob->{_an}=acos(aux::sc_prod(@an,1,0,0));} else {$ob->{_an}=2*$pi-acos(aux::sc_prod(@an,1,0,0));} $ob->{_dandt}=0; if($obpl[2]>0) {$ob->{_i}=asin(aux::vabs(aux::cross(0,0,1,@obpl)));} else {$ob->{_i}=$pi-asin(aux::vabs(aux::cross(0,0,1,@obpl)));} $ob->{_didt}=0; $Dt=$ob->{_tt}-$ob->{_tf}; } ################## # # ################### sub ell_pars { my $ME="ell_pars"; my ($ob)=shift; my ($Tf)=@_; my ($m,$a,$b,$tt,$e,$op,$den,@res); my (@r,@T,@A,@x,@y); # [0 ... 1] $T[0]=$Tf; $T[1]=$Tf+$ob->{_DT}; # --here $r[0]=$ob->{_rfa}; $r[1]=$ob->{_rta}; if($T[1]>2*$pi) {$T[1]-=2*$pi;} $den=$r[1]*cos($T[1])-$r[0]*cos($T[0]); # return invalid result if denominator is too large, especially for cases # of e ~ "0/0" if($den*$den/($r[0]*$r[1])<1.0e-25) {return @res=(0,$E_ZZ,-1,$INF);} $e=($r[0]-$r[1])/$den; if($e==1.0) {$e=1.0-1.0e-12;} $a=$r[0]*(1+$e*cos($T[0]))/(1-$e*$e); if($e>=1) {$a*=-1;} # hyperbola else {if($a<=1.0e-12) {$a=1;}} # kludge if(($e>=0) && ($a>0)) { # if $a<=0 Theta too large, no point on hyp. # if($e>=0) { if($e*$e<1) { # ellipse $b=$a*sqrt(1-$e*$e); for($m=0;$m<2;++$m) { $x[$m]=$r[$m]*cos($T[$m])+$a*$e; # this produces a glitch # if(($x[$m]>0) && (fabs($T[$m]-$pi)<0.1)) { # die $r[$m].", ".$T[$m].", ".cos($T[$m]).", ".$a.", ".$e; # } $y[$m]=$r[$m]*sin($T[$m]); if(fabs($x[$m]/$a)>1.0+1.0e-15) { # print "x, a, e, T = ".$x[$m].", ".$a.", ".$e.", ".$T[$m]."\n"; } if(fabs($x[$m]/$a)>1.0-1.0e-12) {if($x[$m]>0) {$x[$m]=$a;} else {$x[$m]=-$a;}} if(fabs($x[$m]/$a)>1) { die "x, a, e, r = ".$x[$m].", ".$a.", ".$e."".$r[$m]. ", x/a = ".($x[$m]/$a); } $A[$m]=0.5*(fabs($a*$b*acos($x[$m]/$a))-fabs($a*$e*$y[$m])); if($y[$m]<0) {$A[$m]=$pi*$a*$b-$A[$m];} # area of ellipse sector } # end for if($A[1]<$A[0]) {$A[1]+=$pi*$a*$b;} $op=2*$pi*sqrt($a*$a*$a/$ob->{_mu}); # orbital period } else { # hyperbola $b=$a*sqrt($e*$e-1); for($m=0;$m<2;++$m) { $x[$m]=$r[$m]*cos($T[$m])-$a*$e; if($x[$m]>0) {die;} $y[$m]=$r[$m]*sin($T[$m]); if((($x[$m]==0)) || (fabs($y[$m]/$x[$m])>fabs($b/$a))) {return @res=(0,-1,-1,$INF);} else {$A[$m]=0.5*(fabs($a*$e*$y[$m])-fabs($a*$b*acosh(-$x[$m]/$a)));} if($y[$m]<0) {$A[$m]*=-1;} } # end for $op=$INF; } # end else $tt=2*($A[1]-$A[0])/sqrt(fabs($a*$ob->{_mu}*(1-$e*$e))); } # end if(($e>=0) && ($a>=0)) { else {return @res=(-1,-1,-1,$INF);} if($tt>$INF/2) {die;} @res=($a,$e,$tt,$op); } return 1;