APSF //- This script determines the first and last dates for the current year that each object //- is visible above a specified altitude at the end of astronomical twilight for the chosen //- site. Also reports the maximum altitude the object attains, and on what date. //- //- The results can be saved in the user-defined fields, and/or output to a tab-separated //- text file. //- //- WARNING: //- a. This script might take a while to run, especially for a lot of objects. Also, //- AstroPlanner will need to be the frontmost (active) app otherwise the script might stall //- until the app is brought to the front again. //- b. If you set the First day and Last day times to be different, then the computation will take //- twice as long. e.g. First day above min alt at end of evening astronomical twilight and Last //- day above min alt at start of morning astronomical twilight. //- c. If you use a user-defined site horizon that isn't relatively smooth, then you might get //- odd results (as the object disappears and reappears behind parts of the horizon). //- //- Paul Rodman, Sep 2008 //- //- Version 1.7 10 Sep 2008 //- 1. Option to use site horizon instead of fixed minimum altitude setting (see warning (c) above). //- 2. Option to ensure First date < Last Date (by moving Last Date to the following year). //- 3. Allow YYYYMMDD date format (useful for Highlighting since there's a bug that forces all highlighting //- comparisons on user fields to be numeric. //- Version 1.6 9 Sep 2008 //- 1. Added option to have a different time setting for Last date. See warning (b) above. //- 2. Some optimisations to speed up the computation in some cases. //- Version 1.5 9 Sep 2008 //- 1. Fixed a problem that occurred on slower computers. //- 2. Re-arranged and annotated the source code for easier maintenance. //- Version 1.4 9 Sep 2008 //- 1. If an object never rises above specified altitude, "Never" is used for the //- start/end dates. Similarly if an object is always above the specified altitude, //- "Always" is used for the start/end dates. In previous versions, the start/end //- dates were left blank in these cases. //- 2. Option to select period: current year (default, as before), next year, one year //- from the current date, or a given period (from date to date). //- 3. Beep at the end to let you know the wait is over. //- Version 1.3 8 Sep 2008 //- 1. Better handles situation where twilight does not occur. //- Version 1.2 8 Sep 2008 //- 1. Added ability to choose time of night. //- Version 1.1 8 Sep 2008 //- 1. Fixed problem with daylight saving. //- 2. More control over what does into user-defined fields. //- 3. Option to process highlighted/unhighlighted objects only. //- 4. Saves parameters between runs. //- Version 1.0 5 Sep 2008 //- Initial release. //==================================================================================================== dim dt,minaltitude,prevalt1(-1),prevalt2(-1),firstday(-1),lastday(-1),minalt(-1),maxalt(-1),maxaltday(-1) as double dim saveDateTime,loctime(1) as double dim prevvis1(-1),prevvis2(-1) as string dim d,i,j,fields(6),PutInUserFields(3),udf(3),what,compute(-1),whattime(1),sdate,nDays as integer dim wasFixed, PutInTextFile, UserHorizon,adjustdates,yyyymmdd as boolean dim startdate,enddate as double const oneday = 86400.0 // One day in seconds //==================================================================================================== function DateToString(d as double) as string // Construct a date string in the form: YYYY-MM-DD if d<=10.0 then // Not a valid date return "" else dim s as string s=format(YearofDate(d),"0000")+"-"+format(MonthofDate(d),"00")+"-"+format(DayofDate(d),"00") if yyyymmdd then s=ReplaceAll(s,"-","") return s end if end function //==================================================================================================== sub WaitForVisibility() // Wait for the visibility update to complete. In a perfect world we wouldn't need to worry // about this. dim start as double, i as integer, waiting as boolean start=Microseconds do waiting=false // Wait until the rise/set times of all objects are non-zero (indicating that // they have all been recomputed) for i=1 to nObjects if Obj(i).Rise=0.0 and Obj(i).Set=0.0 then waiting=true exit end if next if waiting then Delay(0.1) loop until not waiting or Microseconds-start>30E6 if waiting then Delay(10.0) // Failsafe end sub //==================================================================================================== sub RestoreParameters() // Restore saved parameters from previous run dim i as integer SaveRestoreGlobal(true) what=RestoreIntegerValue("what",0) minaltitude=RestoreDoubleValue("minalt",20.0) for i=0 to 3 PutInUserFields(i)=RestoreIntegerValue("piuf"+str(i),1) udf(i)=RestoreIntegerValue("udf"+str(i),1) next PutInTextFile = RestoreBooleanValue("PutInTextFile",false) for i=0 to ubound(fields) fields(i)=RestoreIntegerValue("fields"+str(i),1) next for i=0 to 1 whattime(i)=RestoreIntegerValue("whattime"+str(i),3) loctime(i)=RestoreDoubleValue("loctime"+str(i),21.0) next sdate=RestoreIntegerValue("sdate",0) startdate=RestoreDoubleValue("startdate",MakeDate(YearOfDate(CurrentDate),1,1)) enddate=RestoreDoubleValue("enddate",MakeDate(YearOfDate(CurrentDate),12,31)) UserHorizon=RestoreBooleanValue("UserHorizon",false) adjustdates=RestoreBooleanValue("adjustdates",false) yyyymmdd=RestoreBooleanValue("yyyymmdd",false) end sub //==================================================================================================== sub SaveParameters() // Save parameters for next run dim i as integer SaveIntegerValue("what",what) SaveDoubleValue("minalt",minaltitude) for i=0 to 3 SaveIntegerValue("piuf"+str(i),PutInUserFields(i)) SaveIntegerValue("udf"+str(i),udf(i)) next SaveBooleanValue("PutInTextFile",PutInTextFile) for i=0 to ubound(fields) SaveIntegerValue("fields"+str(i),fields(i)) next for i=0 to 1 SaveIntegerValue("whattime"+str(i),whattime(i)) SaveDoubleValue("loctime"+str(i),loctime(i)) next SaveIntegerValue("sdate",sdate) SaveDoubleValue("startdate",startdate) SaveDoubleValue("enddate",enddate) SaveBooleanValue("UserHorizon",UserHorizon) SaveBooleanValue("adjustdates",adjustdates) SaveBooleanValue("yyyymmdd",yyyymmdd) end sub //==================================================================================================== function GetParameters() as boolean // Ask user for parameters via dialog dim i as integer dim fieldnames(6),ufieldnames(3),udfnames(3),times(8) as string fieldnames(0)="ID" fieldnames(1)="RA" fieldnames(2)="Dec" fieldnames(3)="First date" fieldnames(4)="Last date" fieldnames(5)="Max. Altitude" fieldnames(6)="Max. Altitude Date" ufieldnames(0)="First day" ufieldnames(1)="Last day" ufieldnames(2)="Maximum altitude" ufieldnames(3)="Date of max. alt." for i=0 to 3 udfnames(i)="User "+str(i+1) next times(0)="Sunset" times(1)="End of evening Civil twilight" times(2)="End of evening Nautical twilight" times(3)="End of evening Astronomical twilight" times(4)="Start of morning Astronomical twilight" times(5)="Start of morning Nautical twilight" times(6)="Start of morning Civil twilight" times(7)="Sunrise" times(8)="Specific local time" // Set up the parameter values in the dialog SetChoiceParameter("What objects to process",what,"All objects","Highlighted objects only","Unhighlighted objects only") SetDoubleParameter(true,"Min. altitude ("+DegreeSymbol+")",minaltitude,0.0,80.0) SetBooleanParameter("Adjust dates so that First < Last",adjustdates) SetBooleanParameter(true,"Use site horizon rather than minimum altitude",UserHorizon) SetPopupParameter("First day above min. alt at", whattime(0), times) SetPopupParameter(true,"Last day above min. alt at", whattime(1), times) SetTimeParameter("First day specific local time",loctime(0)) SetTimeParameter(true,"Last day specific local time",loctime(1)) SetCheckListParameter("Put results in user-defined fields",PutInUserFields,ufieldnames,false,ubound(PutInUserFields)+1) SetCheckListParameter(true,"User-defined fields to use",udf,udfnames,false,ubound(udf)+1) SetBooleanParameter("Put results into text file",PutInTextFile) SetBooleanParameter(true,"Use date format YYYYMMDD rather than YYYY-MM-DD",yyyymmdd) SetCheckListParameter("Text file fields",fields,fieldnames,false,ubound(fields)+1) ParameterDependency("Text file fields","Put results into text file") SetChoiceParameter(true,"Period to consider",sdate,"Current year ("+str(YearOfDate(CurrentDate))+")", _ "Next year ("+str(YearOfDate(CurrentDate)+1)+")", "One year starting from "+DoubleToDate(CurrentDate), _ "Given period (below)") SetDateParameter("Period start",startdate) SetDateParameter(true,"Period end",enddate) // Put up the dialog if not EditParameters("First and Last Dates") then return false // Get the parameter values from the dialog what=GetChoiceParameter("What objects to process") minaltitude=GetDoubleParameter("Min. altitude ("+DegreeSymbol+")") PutInUserFields=GetCheckListParameter("Put results in user-defined fields") PutInTextFile=GetBooleanParameter("Put results into text file") fields=GetCheckListParameter("Text file fields") udf=GetCheckListParameter("User-defined fields to use") whattime(0)=GetChoiceParameter("First day above min. alt at") whattime(1)=GetChoiceParameter("Last day above min. alt at") loctime(0)=GetTimeParameter("First day specific local time") loctime(1)=GetTimeParameter("Last day specific local time") sdate=GetChoiceParameter("Period to consider") startdate=GetDateParameter("Period start") enddate=GetDateParameter("Period end") UserHorizon=GetBooleanParameter("Also consider user-defined site horizon") adjustdates=GetBooleanParameter("Adjust dates so that First < Last") yyyymmdd=GetBooleanParameter("Use date format YYYYMMDD rather than YYYY-MM-DD") return true end function //==================================================================================================== function SelectObjects() as integer // Figure out which plan objects we are going to consider. Return the number found. dim i,ncompute as integer redim prevalt1(nObjects) redim prevalt2(nObjects) redim prevvis1(nObjects) redim prevvis2(nObjects) redim firstday(nObjects) redim lastday(nObjects) redim maxalt(nObjects) redim minalt(nObjects) redim maxaltday(nObjects) redim compute(nObjects) ncompute=0 for i=1 to nObjects minalt(i)=999.0 maxalt(i)=-999.0 select case what case 0 compute(i)=1 ncompute=ncompute+1 case 1 if Obj(i).IsHighlighted then compute(i)=1 ncompute=ncompute+1 end if case 2 if not Obj(i).IsHighlighted then compute(i)=1 ncompute=ncompute+1 end if end select next return ncompute end function //==================================================================================================== sub GetPeriod(byref stdate as double, byref nDays as integer) // Figure out the start date and period for the computation dim edate as double nDays=367 select case sdate case 0 stdate = MakeDate(YearOfDate(CurrentDate),1,1) // 12pm 1/1/200x case 1 stdate = MakeDate(YearOfDate(CurrentDate)+1,1,1) // 12pm 1/1/200x+1 case 2 stdate = MakeDate(YearOfDate(CurrentDate),MonthOfDate(CurrentDate),DayOfDate(CurrentDate)) // 12pm today else stdate = MakeDate(YearOfDate(startdate),MonthOfDate(startdate),DayOfDate(startdate)) // 12pm on start date edate = MakeDate(YearOfDate(enddate),MonthOfDate(enddate),DayOfDate(enddate)) // 12pm on end date nDays=round(abs(edate-stdate)/oneday)+2 if edate0 then alt=Obj(i).Altitude if IsFirstDay then alldone=false else if UserHorizon then vis=Obj(i).Visible if first and prevvis1(i)<>"Yes" and vis="Yes" then // First day firstday(i)=dt elseif not first and prevvis2(i)="Yes" and vis<>"Yes" then // Previous day was Last day lastday(i)=dt-oneday end if if first then prevvis1(i)=vis else prevvis2(i)=vis else if first and prevalt1(i)=minaltitude then // First day firstday(i)=dt elseif not first and prevalt2(i)>=minaltitude and altmaxalt(i) then maxalt(i)=alt maxaltday(i)=dt end if if alt0 then doUF=true next for i=0 to 3 if udf(i)<>0 then userfld.Append i+1 next if doUF then for iUF=0 to ubound(PutInUserFields) if PutInUserFields(iUF)<>0 then if ubound(userfld)<0 then exit // No more user fields to use for i=1 to nObjects if compute(i)<>0 then Obj(i).User(userfld(0))="" next select case iUF case 0 UserHeading(userfld(0))="First Day" case 1 UserHeading(userfld(0))="Last Day" case 2 UserHeading(userfld(0))="Maximum Alt." else UserHeading(userfld(0))="Max Day" end select for i=1 to nObjects if compute(i)<>0 then select case iUF case 0 if minalt(i)>=minaltitude then Obj(i).User(userfld(0))="Always" elseif maxalt(i)=minaltitude then Obj(i).User(userfld(0))="Always" elseif maxalt(i)nil then for i=1 to nObjects redim fld(-1) if fields(0)<>0 then fld.Append Obj(i).ID if fields(1)<>0 then fld.Append format(Obj(i).RA,"-0.00000") if fields(2)<>0 then fld.Append format(Obj(i).Dec,"-0.00000") if fields(3)<>0 then if minalt(i)>=minaltitude then fld.Append "Always" elseif maxalt(i)0 then if minalt(i)>=minaltitude then fld.Append "Always" elseif maxalt(i)0 then fld.Append format(maxalt(i),"-0.0") if fields(6)<>0 then fld.Append DateToString(maxaltday(i)) tf.WriteLine(Join(fld,chr(9))) next tf.Close end if end sub //==================================================================================================== sub ChronologicalOrder() // Adjust dates so that Firstlastday(i) then lastday(i)=MakeDate(YearOfDate(lastday(i))+1,MonthOfDate(lastday(i)),DayOfDate(lastday(i))) end if next end sub //==================================================================================================== RestoreParameters if not GetParameters then return SaveParameters if SelectObjects<=0 then print "No objects selected for computation!" return end if GetPeriod(dt,nDays) // Get the current plan date/time settings for later restoration wasFixed=FixedDate saveDateTime = PlanLocalDateTime StartProgress("Finding first and last dates", true, nDays) // Slog through the period, one day at a time dim done1,done2 as boolean for d = 1 to nDays done1=false done2=false if SetLocalTime(dt,0) then done1=CheckForAltitudeTransitions(d=1,true) end if if whattime(0)=whattime(1) and (whattime(0)<>8 or loctime(0)=loctime(1)) then done2=CheckForAltitudeTransitions(d=1,false) else if SetLocalTime(dt,1) then done2=CheckForAltitudeTransitions(d=1,false) end if end if if done1 and done2 and PutInUserFields(2)=0 and PutInUserFields(3)=0 then exit // Move to the next day dt=dt+oneday if UpdateProgress(d) then exit // If user clicks Cancel, abort next if adjustdates then ChronologicalOrder // Put results into user fields AddToUserFields StopProgress // Put results into text file if PutInTextFile then SaveResultsToFile // Restore plan date/time settings if wasFixed then PlanLocalDateTime=saveDateTime else FixedDate=false end if Bleep