Guest User

Eclipse Magic

a guest
Jun 19th, 2017
1,310
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- Eclipse Magic
  2. -- Programmed exposure sequence for a solar eclipse
  3.  
  4.  
  5. -- Eclipse Magic
  6. --
  7. -- Version 1.0.0
  8. --
  9. -- Copyright 2017 by Brian Greenberg, grnbrg@grnbrg.org.
  10. -- Distributed under the GNU General Public License.
  11. --
  12. --
  13. -- ***************************************************************************************************
  14. -- ***************************************************************************************************
  15. --
  16. -- **NOTE** At the current time, the "camera.burst()" functionality requires the "lua_fix"
  17. -- beta build of Magic Lantern which can be found at https://builds.magiclantern.fm/experiments.html.
  18. -- Hopefully this function will be merged into the mainline soon.  And no idea when support for newer
  19. -- bodies like the 70D will arrive.
  20. --
  21. -- ***************************************************************************************************
  22. -- ***************************************************************************************************
  23. --
  24. --
  25. -- Read through the comments at the top of the script, and modify the contact points to suit
  26. -- your location, and the exposure settings to suit your camera.  Copy the script to
  27. -- the ML/SCRIPTS directory on your camera's media, and run it from the menu.
  28. --
  29. -- If you have issues, (and you aren't running with the test flag set) you should be able to
  30. -- reboot your camera, and it will pick up where it left off.  Exposure events are tied to
  31. -- particular times, not a sequence that needs to start at a specific time.
  32. --
  33. -- See http://xjubier.free.fr/en/site_pages/SolarEclipseExposure.html for suggestions for
  34. -- exposure values.
  35. --
  36. --
  37. -- If this script is useful, feel free to send a PayPal donation to Eclipse-Magic@grnbrg.org,
  38. -- or a Bitcoin donation to 1grnbrg3Ea4t6bxHvQKRvorbBeLNDXv2N.
  39. --
  40. -- ***************************************************************************************************
  41. -- ***************************************************************************************************
  42. -- ***************************************************************************************************
  43.  
  44.  
  45. --
  46. --  If you are testing, set this to 1, and the shutter won't be used.
  47. --  Set it to 0 for the real event.
  48. --
  49. TestBeepNoShutter = 1
  50. TestStartTime = 0
  51.  
  52. -- Variable definitons that have to go here.  Ignore them.
  53. c1 = {}
  54. c2 = {}
  55. c3 = {}
  56. c4 = {}
  57.  
  58.  
  59. --
  60. -- Set the 4 contact times here.  Time zone is irrelevant, as long as your camera and these
  61. -- times are the same.  Make sure the times are correct for your location, and that your camera
  62. -- is accurately set to GPS time.
  63. --
  64. c1.hr = 0
  65. c1.min = 0
  66. c1.sec = 30
  67.  
  68. c2.hr = 0
  69. c2.min = 5
  70. c2.sec = 30
  71.  
  72. c3.hr = 0
  73. c3.min = 8
  74. c3.sec = 0
  75.  
  76. c4.hr = 0
  77. c4.min = 13
  78. c4.sec = 0
  79.  
  80.  
  81. --
  82. -- Partial phase settings.
  83. --
  84. PartialISO = 100
  85. PartialShutterSpeed = 0.0005    -- 1/2000 s  (Enter using decimal seconds.)
  86. PartialMarginTime = 8           -- Number of seconds after C1 or C3 and before C2 or C4 to start exposures
  87. PartialExposureCount = 3        -- Number of partial phase exposures before and after totality
  88. PartialDoBkt = 1                -- Do you want to do exposure bracketing?  1 - yes, 0 - not
  89. PartialBktStep = 1              -- Number of f-stops in each step.  Can 0.333333, 1, 2, etc
  90. PartialBktCount = 1             -- How many brackets on each side of the neutral exposure?
  91.  
  92.  
  93. --
  94. -- Do a fast burst of exposures at C2 and C3, to try to get Baily's beads and chromosphere.
  95. -- You will need to know how many exposures your camera will buffer, and how long it takes the
  96. -- buffer to fill.  At "StartOffset" seconds before C2 or C3, the camera will take "BurstCount"
  97. -- exposures, as fast as it can.  You should adjust C23StartOffset so that burst of image straddles
  98. -- the contact time.  Note that, between the setting of the camera clock and the jitter in this
  99. -- script, there will be some error in the timing.  +/- half a second or more is possible.
  100. --
  101. C23BurstCount = 14             
  102. C23BurstStartOffset = 2
  103. C23BurstISO = 100
  104. C23BurstShutterSpeed = 0.002 -- 1/500 sec
  105.  
  106. --
  107. -- During the time between C2 and C3, the script will run back and forth between the
  108. -- "MinShutterSpeed" and "MaxShutterSpeed" as quickly as possible, with an extra 2 long exposures
  109. -- at midpoint.  "ExpStep" is the size of the f-stop variation, and can be set to 0.333333, 1, 2, etc.
  110. --
  111. TotalityISO = 100
  112. TotalityMinShutterSpeed = 0.000123 -- 1/8000 sec  (MinShutterSpeed is the *fastest* speed to use.)
  113. TotalityMaxShutterSpeed = 1.0 -- 1/2 sec (MaxShutterSpeed is the *slowest*, longest speed used.)
  114. TotalityExpStep = 1
  115.  
  116. ---------------------------------------------------------------------------------------------------------
  117. ---------------------------------------------------------------------------------------------------------
  118. --
  119. --                                       HERE THERE BE DRAGONS
  120. --
  121. ---------------------------------------------------------------------------------------------------------
  122. ---------------------------------------------------------------------------------------------------------
  123.  
  124.  
  125. --
  126. -- Times are easiest to deal with in seconds.  This would be painful if they crossed over midnight,
  127. -- but late-night solar eclipses are rare.
  128. --
  129. c1_sec = c1.hr * 3600 + c1.min * 60 + c1.sec
  130. c2_sec = c2.hr * 3600 + c2.min * 60 + c2.sec
  131. c3_sec = c3.hr * 3600 + c3.min * 60 + c3.sec
  132. c4_sec = c4.hr * 3600 + c4.min * 60 + c4.sec
  133. max_sec = c2_sec + ((c3_sec - c2_sec) / 2)
  134.  
  135. tick_offset = 0
  136. tick_time = 0
  137.  
  138.  
  139. --
  140. -- Get the current time (in seconds) from the camera's clock.
  141. --
  142. function get_cur_secs ()
  143.  
  144.     local cur_time = dryos.date
  145.     local cur_secs = (cur_time.hour * 3600 + cur_time.min * 60 + cur_time.sec)
  146.    
  147.     if ( TestBeepNoShutter == 1 )
  148.     then
  149.    
  150.         cur_secs = (cur_secs - TestStartTime)       -- If we're testing, start the clock at
  151.                                                     -- now, not actual time.
  152.        
  153.     end
  154.    
  155.     return cur_secs
  156.  
  157. end
  158.  
  159.  
  160. --
  161. -- Take a time variable expressed in seconds (which is what all times are
  162. -- stored as) and convert it back to HH:MM:SS
  163. --
  164. function pretty_time (time_secs)
  165.  
  166.     local text_time = ""
  167.     local hrs = 0
  168.     local mins = 0
  169.     local secs = 0
  170.    
  171.     hrs = (math.floor(time_secs/3600) * 3600)
  172.     mins = (math.floor((time_secs - (hrs * 3600))/60)*60)
  173.     secs = (time_secs - (hrs*3600) - (mins * 60))
  174.    
  175.     text_time = string.format("%02d:%02d:%02d", hrs, mins, secs)
  176.    
  177.     return text_time
  178.  
  179. end
  180.  
  181.  
  182. --
  183. -- Every second, update the current time available to all functions.
  184. -- The camera maintains a millisecond timer since power-on.  We can use this to
  185. -- get close to the beginning of a given second.  I think.
  186. --
  187. event.seconds_clock = function (ignore)
  188.    
  189.     tick_time = get_cur_secs()
  190.     tick_offset = dryos.ms_clock
  191.    
  192.     return true
  193.    
  194. end
  195.  
  196.  
  197. --
  198. -- Hurry up and wait for the next important time to arrive.
  199. --
  200. function wait_until (done_waiting)
  201.  
  202.     local counter = get_cur_secs()
  203.     local next_sec = 0
  204.    
  205.     print ("Waiting for ", pretty_time(done_waiting), " in ",(done_waiting - counter), " seconds.")
  206.    
  207.     repeat
  208.  
  209.         task.yield (1000) -- Let the camera do other tasks for a second.
  210.    
  211.         counter = get_cur_secs()
  212.                    
  213.     until (counter >= (done_waiting - 1))
  214.            
  215.     if ( counter < done_waiting)   
  216.     then
  217.                                     -- Loop /should/ exit the second before we are done. But
  218.                                     --  It's possible that it could exit early in our target
  219.                                     --  second. If so, we don't want to wait around to
  220.                                     --  (done_waiting + 1) to exit.
  221.    
  222.         next_sec = (1000 - (dryos.ms_clock - tick_offset))     
  223.    
  224.         msleep (next_sec) -- Hard sleep, don't let anything else have priority.
  225.        
  226.     end
  227.                
  228. end
  229.  
  230.  
  231. --
  232. -- Say "CHEESE!".  Set up the camera, and take a picture.  Also deals with any requested
  233. -- bracketing.
  234. --
  235. function take_shot(iso, speed, dobkt, bktstep, bktcount)
  236.  
  237.     local bktspeed = 0.0
  238.    
  239.     camera.iso.value = iso
  240.  
  241.     if (dobkt == 0)
  242.     then    -- Single exposure
  243.  
  244.         camera.shutter.value = speed
  245.    
  246.         if (TestBeepNoShutter == 0)
  247.         then
  248.        
  249.             camera.shoot(false)
  250.             print ("Click!  Time: ", pretty_time(tick_time), " ISO: ", camera.iso.value,
  251.                 " shutter: 1/", 1/camera.shutter.value)
  252.             task.yield(10) -- Exposures can take time.  Give other stuff a chance to run.
  253.            
  254.         else
  255.        
  256.             print ("Click!  Time: ", pretty_time(tick_time), " ISO: ", camera.iso.value,
  257.                 " shutter: 1/", 1/camera.shutter.value)
  258.             beep(1,50)
  259.             task.yield (600 + camera.shutter.ms)
  260.            
  261.         end
  262.        
  263.     else    -- Bracketing exposure
  264.    
  265.         -- Loop through the requested number of exposure brackets.
  266.         for bktnum = PartialBktCount,(-1 * PartialBktCount),-1 do
  267.  
  268.             bktspeed = PartialShutterSpeed * (2.0^(bktnum * PartialBktStep))
  269.            
  270.             camera.shutter.value = bktspeed
  271.            
  272.             if (TestBeepNoShutter == 0) then
  273.        
  274.                 camera.shoot(false)
  275.                 print ("Click!  Time: ", pretty_time(tick_time), " ISO: ", camera.iso.value,
  276.                     " shutter: 1/", 1/camera.shutter.value)
  277.                 task.yield(10) -- Give other stuff a chance to run
  278.            
  279.             else
  280.        
  281.                 print ("Click!  Time: ", pretty_time(tick_time), " ISO: ", camera.iso.value,
  282.                     " shutter: 1/", 1/camera.shutter.value)
  283.                 beep(1,50)
  284.                 task.yield ((600 + camera.shutter.ms))
  285.                        
  286.             end
  287.  
  288.         end
  289.     end
  290. end
  291.  
  292.  
  293. --
  294. -- Burst is simpler than single shot, because no brackets.  Set the camera, pull the trigger.
  295. --
  296. -- I have tried replacing this with multiple calls to "camera.shoot()", but it is still considerably
  297. -- slower than "camera.burst()", and can also crash the camera -- nothing permanent, but still
  298. -- not something I want to put out.
  299. --
  300. function take_burst (count, iso, speed)
  301.  
  302.     camera.shutter.value = speed
  303.     camera.iso.value = iso
  304.    
  305.     if (TestBeepNoShutter == 0)
  306.     then
  307.    
  308.         camera.burst(count)
  309.         print ("Burst Click!  Time: ", pretty_time(tick_time), " ISO: ", camera.iso.value, " shutter: 1/",
  310.                     1/camera.shutter.value, "Count: ", count)
  311.         task.yield(10)
  312.        
  313.     else
  314.    
  315.         print ("Burst Click!  Time: ", pretty_time(tick_time), " ISO: ", camera.iso.value, " shutter: 1/",
  316.                     1/camera.shutter.value, "Count: ", count)
  317.         beep(3,50)
  318.         task.yield (4000 + (count * camera.shutter.ms))    
  319.        
  320.     end
  321.  
  322. end
  323.  
  324.  
  325. --
  326. -- Take the spaced exposures for the C1-C2 and C3-C4 periods.  Take the margin times off either
  327. -- end, split the time into the right intervals, and fire off take_picture()
  328. --
  329. function do_partial (start, stop)
  330.  
  331.     local margin_start = start + PartialMarginTime
  332.     local margin_end = stop - PartialMarginTime
  333.     local image_time = margin_start
  334.     local image_interval = math.floor((margin_end - margin_start) / (PartialExposureCount - 1))
  335.     local exposure_count = 0
  336.    
  337.     if ( tick_time >= margin_end ) -- Are we past this phase already?
  338.     then
  339.    
  340.         return
  341.        
  342.     end
  343.    
  344.     repeat
  345.    
  346.         if (tick_time <= image_time)
  347.         then
  348.        
  349.             wait_until(image_time)
  350.            
  351.             take_shot (PartialISO, PartialShutterSpeed, PartialDoBkt, PartialBktStep, PartialBktCount)
  352.            
  353.         end
  354.        
  355.         image_time = image_time + image_interval
  356.        
  357.         exposure_count = exposure_count + 1
  358.    
  359.     until (exposure_count >= PartialExposureCount)
  360.    
  361. end
  362.  
  363.  
  364. --
  365. -- Start the burst shot a little before C2, then start running through exposure settings, going from
  366. -- short, fast exposures to slow, long exposures and thenback to short, until just before the midpoint
  367. -- of the eclipse.  Take two long exposures at that point, for good measure.
  368. --
  369. function do_c2max()
  370.  
  371.     local cur_shutter_speed = 0
  372.    
  373.     if ( tick_time >= max_sec ) -- Are we past this phase already?
  374.     then
  375.    
  376.         return
  377.        
  378.     end
  379.    
  380.     if ( tick_time <= (c2_sec - C23BurstStartOffset) ) -- Have we past the burst for Baily's beads?
  381.     then
  382.    
  383.         wait_until (c2_sec - C23BurstStartOffset)
  384.        
  385.         take_burst (C23BurstCount, C23BurstISO, C23BurstShutterSpeed)
  386.        
  387.     end
  388.    
  389.     cur_shutter_speed = TotalityMinShutterSpeed
  390.    
  391.     repeat
  392.    
  393.         take_shot(TotalityISO, cur_shutter_speed, 0, 0, 0)
  394.        
  395.         cur_shutter_speed = cur_shutter_speed * 2.0^TotalityExpStep
  396.        
  397.         if (cur_shutter_speed > (1.5 * TotalityMaxShutterSpeed))
  398.         then
  399.        
  400.             cur_shutter_speed = TotalityMinShutterSpeed
  401.            
  402.         end
  403.    
  404.     until (tick_time >= math.max((max_sec - 2 ), (max_sec - (2 * TotalityMaxShutterSpeed + 1))))
  405.         -- Leave room for 2 long exposures
  406.        
  407.     take_shot(TotalityISO, TotalityMaxShutterSpeed, 0, 0, 0)
  408.     take_shot(TotalityISO, TotalityMaxShutterSpeed, 0, 0, 0)
  409.  
  410. end
  411.  
  412.  
  413. --
  414. -- Similar to do_c2max, but reversed.  Exposures run from longest to shortest (and then repeat),
  415. -- the burst starts just before C3, and there are no bonus exposures.
  416. --
  417. function do_maxc3()
  418.  
  419.     local cur_shutter_speed = 0
  420.  
  421.     if ( tick_time >= (c3_sec + C23BurstStartOffset) ) -- Are we past this phase already?
  422.     then
  423.    
  424.         return
  425.        
  426.     elseif ( tick_time < (c3_sec - C23BurstStartOffset) ) -- Do we have time for some totality exposures?
  427.     then
  428.    
  429.         cur_shutter_speed = TotalityMaxShutterSpeed
  430.    
  431.         repeat
  432.    
  433.             take_shot(TotalityISO, cur_shutter_speed, 0, 0, 0)
  434.        
  435.             cur_shutter_speed = cur_shutter_speed / (2.0^TotalityExpStep)
  436.        
  437.             if (cur_shutter_speed < (TotalityMinShutterSpeed / 1.5))
  438.             then
  439.        
  440.                 cur_shutter_speed = TotalityMaxShutterSpeed
  441.            
  442.             end
  443.    
  444.         until (tick_time >= (c3_sec - C23BurstStartOffset - 1))
  445.        
  446.     end
  447.    
  448.     wait_until (c3_sec - C23BurstStartOffset)
  449.    
  450.     take_burst (C23BurstCount, C23BurstISO, C23BurstShutterSpeed)
  451.  
  452. end
  453.  
  454.  
  455. --
  456. -- The ringleader.
  457. --
  458. function main()
  459.  
  460.     local starttime
  461.  
  462.     starttime = get_cur_secs()
  463.    
  464.     TestStartTime = starttime
  465.    
  466.     menu.close()
  467.     console.show()
  468.  
  469.     print ()
  470.     print ()
  471.     print ("-------------------------------------")
  472.     print ("  Eclipse Magic")
  473.     print ("  Copyright 2017, grnbrg@grnbrg.org")
  474.     print ("  Released under the GNU GPL")
  475.     print ("-------------------------------------")
  476.     print ()
  477.  
  478.     task.yield(1100)    -- Wait here for a full second.  If not, we can end up getting into timing
  479.                         -- loops before tick_time is updated.  Mostly only important when the
  480.                         -- TestBeepNoShutter flag is set, but it doesn't cost anything.
  481.  
  482.     -- If the camera is not in manual mode, trying to set the shutter speed throws errors.
  483.     -- Check to make sure we are in manual mode, and refuse to run if we're not.
  484.     if (camera.mode == MODE.M)
  485.     then
  486.    
  487.         do_partial (c1_sec, c2_sec)
  488.    
  489.         do_c2max()
  490.    
  491.         do_maxc3()
  492.    
  493.         do_partial (c3_sec, c4_sec)
  494.        
  495.     else
  496.        
  497.         beep (5, 100)
  498.        
  499.         print("Camera must be in manual (M) mode!!")
  500.         print()
  501.         print("Press any button to exit the script.  Change the mode and re-run.")
  502.        
  503.         key.wait()
  504.        
  505.     end
  506.        
  507.     event.seconds_clock = nil  
  508.     console.hide()
  509.    
  510. end -- Done.  Hope there were no clouds.
  511.  
  512.  
  513. main()
RAW Paste Data