Files
gst-plugin-linescan/yass/yass4.lua
yair 30e695f961 Add YASS (Yet Another Sunset Script) v4.8 for CHDK cameras
This commit introduces third-party work by waterwingz, based on scripts
by Fbonomi and soulf2. YASS is an advanced intervalometer script for
CHDK-enabled Canon cameras that automatically adjusts exposure settings
during sunset, sunrise, or other changing light conditions.

Key features:
- Automatic exposure ramping (shutter speed and ISO)
- Intelligent ND filter control
- Focus lock options (AFL/MF at infinity)
- Configurable shot intervals and exposure compensation
- CSV logging of exposure parameters
- Battery-saving display blanking options

Original work released under GPL license.
Requires CHDK 1.2.0 build 3276 or higher (1.3.0+ recommended).

Files added:
- yass/yass4.lua: Main script (v4.8)
- yass/README.md: Documentation and usage guide
2025-11-17 00:26:28 +02:00

515 lines
19 KiB
Lua

--[[
Yet Another Sunset Script v4.8
@title yass 4.8
@chdk_version 1.3
-- Based on scripts by Fbonomi and soulf2
-- Released under GPL by waterwingz
@param t Shot Rate (sec)
@default t 10
@range t 1 3600
@param o Compensation (f-stops)
@default o 8
@values o -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0
@param e Tv normal limit (sec)
@default e 9
@values e Off 2.0 1.6 1.3 1.0 0.8 0.6 0.5 0.4 0.3 1/4 1/5 1/6 1/8 1/10 1/13 1/15
@param b Tv low limit (sec)
@default b 5
@values b 32 25 20 16 12 10 8.0 6.0 5.0 4.0 3.2 2.5 2.0 1.6 1.3 1.0
@param c ISO default
@default c 1
@values c 80 100 125 160 200 250 320 400 500 640 800 1250 1600
@param d ISO upper limit
@default d 7
@values d 80 100 125 160 200 250 320 400 500 640 800 1250 1600
@param f Exposure ramp rate
@default f 2
@values f VSlow Slow Medium Fast VFast
@param a Aperture Setting
@default a 0
@values a min_Av f1.8 f2.0 f2.8 f3.2 f4.0 f5.6 f6.3 f8 f11 f16
@param m First Shot Delay (min)
@default m 0
@range m 0 240
@param z Zoom step position
@default z -1
@range z -1 100
@param w Use ND Filter?
@default w 1
@range w 0 1
@param x Focus @ Infinity Mode
@default x 0
@values x None AFL MF
@param r RAW enable?
@default r 0
@range r 0 1
@param h Free Disk MB Shut down
@default h 15
@range h 10 100
@param y Status LED
@default y 2
@values y Off 0 1 2 3 4 5 6 7 8
@param n Display blanking mode
@default n 0
@values n None BKLite DispKey ShrtCut
@param l Logging
@default l 2
@values l Off Screen SDCard Both
--]]
props=require("propcase")
tv_ref = { -- note : tv_ref values set 1/2 way between shutter speed values
-608, -560, -528, -496, -464, -432, -400, -368, -336, -304,
-272, -240, -208, -176, -144, -112, -80, -48, -16, 16,
48, 80, 112, 144, 176, 208, 240, 272, 304, 336,
368, 400, 432, 464, 496, 528, 560, 592, 624, 656,
688, 720, 752, 784, 816, 848, 880, 912, 944, 976,
1008, 1040, 1072, 1096, 1129, 1169, 1192, 1225, 1265, 1376 }
av_ref = {176,208,240,272,304,336,368,400,432,464,496,512,528,560,592,624,656,688,720,752}
sv_ref = {378,420,440,462,486,531,576,612,645,678,709,739,772,805}
-- translate user parameter into usable values & names
interval = t*1000
if (e >0) then tv_normal_limit = tv_ref[e+15] else tv_normal_limit = 1192 end
tv96_low_limit = tv_ref[b+4]
apex96_ramp_rate =(f+1) * math.min(1+(t/8),10)
sv96_default = sv_ref[c+1]
sv96_max = sv_ref[d+1]
av_table = {0, 171,192,288,320,384,480,512,576,672,768}
av96 = av_table[a+1]
zoom_setpoint = z
focus_mode = x
min_disk_space = h*1024
compensation = (o-8)*48
nd_filter = w
log_mode = l
display_mode = n
status_led = y-1
-- constants
nd96=288 -- ND filter's number of equivalent f-stops
nd_tv_limit = 960 -- engage ND filter if tv < 1/1000 sec
SHORTCUT="print" -- edit this if using shortcut key to enter sleep mode
-- camera configuration
shot=1
done=0
timestamp=0
led_state = 0
led_timer = 10
display_state = 1
display_hold_timer = 0
function restore()
set_backlight(1)
set_led(status_led,0)
set_raw(original_raw)
unlock_focus()
end
function update_zoom()
local count = 0
zsteps=get_zoom_steps()
if(zoom_setpoint>zsteps) then zoom_setpoint=zsteps end
print("setting zoom to step",zoom_setpoint,"of",zsteps)
sleep(2000)
set_zoom(zoom_setpoint)
sleep(2000)
press("shoot_half")
repeat
sleep(100)
count = count + 1
until (get_shooting() == true ) or (count > 40 )
release("shoot_half")
end
-- focus lock and unlock
function lock_focus()
if (focus_mode > 0) then -- focus mode requested ?
if ( focus_mode == 1 ) then -- method 1 : set_aflock() command enables MF
if (chdk_version==120) then
set_aflock(1)
set_prop(props.AF_LOCK,1)
else
set_aflock(1)
end
if (get_prop(props.AF_LOCK) == 1) then print(" AFL enabled") else print(" AFL failed ***") end
else -- mf mode requested
if (chdk_version==120) then -- CHDK 1.2.0 : call event proc or levents to try and enable MF mode
if call_event_proc("SS.Create") ~= -1 then
if call_event_proc("SS.MFOn") == -1 then
call_event_proc("PT_MFOn")
end
elseif call_event_proc("RegisterShootSeqEvent") ~= -1 then
if call_event_proc("PT_MFOn") == -1 then
call_event_proc("MFOn")
end
end
if (get_prop(props.FOCUS_MODE) == 0 ) then -- MF not set - try levent PressSw1AndMF
post_levent_for_npt("PressSw1AndMF")
sleep(500)
end
elseif (chdk_version >= 130) then -- CHDK 1.3.0 : set_mf()
if ( set_mf(1) == 0 ) then set_aflock(1) end -- as a fall back, try setting AFL is set_mf fails
end
if (get_prop(props.FOCUS_MODE) == 1) then print(" MF enabled") else print(" MF enable failed ***") end
end
sleep(1000)
if(set_focus(50000)==0) then print("infinity focus error") end
sleep(1000)
end
end
function unlock_focus()
if (focus_mode > 0) then -- focus mode requested ?
if (focus_mode == 1 ) then -- method 1 : AFL
if (chdk_version==120) then
set_aflock(0)
set_prop(props.AF_LOCK,0)
else
set_aflock(0)
end
if (get_prop(props.AF_LOCK) == 0) then print("AFL unlocked") else print("AFL unlock failed") end
else -- mf mode requested
if (chdk_version==120) then -- CHDK 1.2.0 : call event proc or levents to try and enable MF mode
if call_event_proc("SS.Create") ~= -1 then
if call_event_proc("SS.MFOff") == -1 then
call_event_proc("PT_MFOff")
end
elseif call_event_proc("RegisterShootSeqEvent") ~= -1 then
if call_event_proc("PT_MFOff") == -1 then
call_event_proc("MFOff")
end
end
if (get_prop(props.FOCUS_MODE) == 1 ) then -- MF not reset - try levent PressSw1AndMF
post_levent_for_npt("PressSw1AndMF")
sleep(500)
end
elseif (chdk_version >= 130) then -- CHDK 1.3.0 : set_mf()
if ( set_mf(0) == 0 ) then set_aflock(0) end -- fall back so reset AFL is set_mf fails
end
if (get_prop(props.FOCUS_MODE) == 0) then print("MF disabled") else print("MF disable failed") end
end
sleep(100)
end
end
function print_tv(val)
local i = 1
local tv_str = {
">64",
"64", "50", "40", "32", "25", "20", "16", "12", "10", "8.0",
"6.0", "5.0", "4.0", "3.2", "2.5", "2.0", "1.6", "1.3", "1.0", "0.8",
"0.6", "0.5", "0.4", "0.3", "1/4", "1/5", "1/6", "1/8", "1/10", "1/13",
"1/15", "1/20", "1/25", "1/30", "1/40", "1/50", "1/60", "1/80", "1/100", "1/125",
"1/160", "1/200", "1/250","1/320","1/400","1/500","1/640","1/800","1/1000","1/1250",
"1/1600","1/2000","1/2500","1/3200","1/4000","1/5000","1/6400","1/8000","1/10000","hi" }
while (i <= #tv_ref) and (val > tv_ref[i]-1) do i=i+1 end
return tv_str[i]
end
function print_av(val)
local i = 1
local av_str = {"n/a","2.0","2.2","2.5","2.8","3.2","3.5","4.0","4.5","5.0",
"5.6","5.9","6.3","7.1","8.0","9.0","10.0","11.0","13.0","14.0","16.0"}
while (i <= #av_ref) and (val > av_ref[i]-1) do i=i+1 end
return av_str[i]
end
function print_sv(val)
local i = 1
local sv_str = {"n/a","80","100","125","160","200","250","320","400","500","640","800","1250","1600","3200"}
while (i <= #sv_ref) and (val > sv_ref[i]-1) do i=i+1 end
return sv_str[i]
end
function sd_log(logmsg)
if ( log_mode > 1 ) then
local logname="A/yass4.csv"
retry = 0
repeat
log=io.open(logname,"a")
if ( log~=nil) then
log:write(os.date(s,tm),": ",logmsg,"\n")
log:close()
return
end
sleep(250)
retry = retry+1
until(retry > 7)
print("Error : log file open fault!")
end
end
function lprint(logmsg)
if ( log_mode > 0) then
if (( log_mode == 1) or (log_mode == 3)) then print(logmsg) end
sd_log(logmsg)
end
end
function sleep_mode(m) -- press user shortcut key to toggle sleep mode
press(SHORTCUT)
sleep(1000)
release(SHORTCUT)
sleep(500)
end
function set_display_key(m) -- click display key to get to desire LCD display mode 0=off, 1=on
if (m==0) then m=2 else m=0 end
local count=5
local clicks=0
sleep(200)
repeat
disp = get_prop(props.DISPLAY_MODE)
if ( disp ~= m ) then
click("display")
clicks=clicks+1
sleep(500)
end
count=count-1
until (( disp==m ) or (count==0))
if (clicks>0 and count==0) then
print("unable to change display")
end
end
function set_display(m) -- m=0 for turn off display, m>0 turn on for m seconds
if (display_mode>0) then
if ( display_hold_timer>0) then
if (m>1) then display_hold_timer = display_hold_timer+m end
else
if (m>1) then
display_hold_timer = m
m=1
end
local st="off"
if (m>0) then st="on" end
if ( display_mode==1) then
if (display_state ~= m) then print("set backlight "..st) end
sleep(1000)
set_backlight(m)
elseif ( display_mode==2) then
if( display_state ~= m) then
print("set display "..st)
set_display_key(m)
if ( focus_mode > 0 ) then
lock_focus()
end
end
elseif ( display_mode==3) then
if (display_state ~= m) then
print("toggle sleep mode")
sleep_mode(m)
end
end
display_state=m
end
end
end
--[[ ========================== Main Program ================================= --]]
set_console_layout(2, 0, 44, 10)
print("Yet Another Sunset Script V4.8")
bi=get_buildinfo()
chdk_version= tonumber(string.sub(bi.build_number,1,1))*100 + tonumber(string.sub(bi.build_number,3,3))*10 + tonumber(string.sub(bi.build_number,5,5))
if ( tonumber(bi.build_revision) > 0 ) then
build = tonumber(bi.build_revision)
else
build = tonumber(string.match(bi.build_number,'-(%d+)$'))
end
if (chdk_version<120) then
printf("CHDK 1.2.0 or higher required")
elseif((chdk_version==120)and(build<3276)) then
printf("CHDK 1.2.0 build 3276 or higher required")
elseif((chdk_version==130)and(build<3383)) then
printf("CHDK 1.3.0 build 3383 or higher required")
else
print("CHDK version okay")
lprint(" tvl="..print_tv(tv96_low_limit).." tvh="..print_tv(tv_normal_limit).." ISOd="..print_sv(sv96_default).." ISOm="..print_sv(sv96_max))
lprint(" shot rate="..(interval/1000).." ramp:"..apex96_ramp_rate)
sleep(200)
-- setup - disable flash, image stabilization and AF assist lamp
original_raw = get_raw()
set_raw(r)
set_prop(props.FLASH_MODE, 2) -- flash off
set_prop(props.IS_MODE, 3) -- IS_MODE off
set_prop(props.AF_ASSIST_BEAM,0) -- AF assist off if supported for this camera
-- startup delay ?
if ( m>0 ) then
lprint("delaying startup for",m,"minutes")
sleep(10)
set_display(0)
sleep(m*60000)
end
-- switch to shooting mode if necessary
if ( get_mode() == false ) then
set_record(1)
while ( get_mode() == false ) do sleep(100) end
end
-- set zoom position
if ( zoom_setpoint > -1 ) then update_zoom() end
-- lock focus at infinity ?
lock_focus()
-- set initial shooting values (values are with ND filter out)
press("shoot_half")
repeat sleep(50) until get_shooting() == true
release("shoot_half")
repeat sleep(50) until get_shooting() == false
sleep(100)
dof=get_dofinfo()
if ((dof.focus == -1) or (dof.focus>20000)) then
print(" focus is at infinity")
elseif ( dof.focus>dof.hyp_dist ) then
print(" focus is past hyperfocal")
else
print(" focus is at "..dof.focus.." mm")
end
if (av96 == 0) then -- are we in min_AV mode ?
av96 = get_prop(props.MIN_AV)
end
sv96 = sv96_default
-- put titles into log file
sd_log("YASS 4.3 "..os.date(s,tm))
sd_log("Shot,Image,Compensation,Tv_target,Tv,Tv+,ND,ISO_target,ISO,f-stop,focus,Tv96,Sv96,Av96,Bv96")
sleep(2000)
-- initialize timers
ticsec = 0
ticmin = 0
set_display(20)
set_console_layout(2, 0, 44, 3)
print("started")
-- loop here until a key is pressed - exit if its the Menu key, else enable the display for 20 seconds
repeat
repeat
now = get_tick_count()
if ( now > timestamp + interval ) then
timestamp=get_tick_count()
if ( interval > 5000 ) then -- check if in slow mode
press("shoot_half") -- get current exposure readings
repeat sleep(50) until get_shooting() == true
release("shoot_half")
repeat sleep(50) until get_shooting() == false
end
bv96=get_bv96()
bv96c=bv96-compensation -- calulate compensated exposure needed
tv96needed=sv96_default+bv96c-av96 -- calculate desired shutter speed at default ISO
if (tv96==nil) then tv96=tv96needed end
if ( compensation >= 0 ) then
compstr = "+"..compensation
else
compstr = "-"..compensation
end
if(tv96needed>tv_normal_limit) then
tv96=(tv96+tv96needed)/2 -- use that shutter speed if above Tv normal limit
sv96=sv96_default -- use default ISO
sv96needed=sv96_default --
else
if( tv96 > tv96needed) then
tv96=math.max(tv96-apex96_ramp_rate,tv96needed) -- lower the shutter speed at the APEX ramp rate
else
tv96=math.min(tv96+apex96_ramp_rate,tv96needed) -- increase the shutter speed at the APEX ramp rate
end
if (tv96>tv96_low_limit) then
sv96=sv96_default -- use default ISO value while Tv above lower limit
sv96needed=sv96_default --
else
tv96=tv96_low_limit -- if Tv at low limit then recalculate ISO value
sv96needed=tv96+av96-bv96c
if (sv96needed>sv96_max) then
sv96needed=sv96_max -- clamp ISO at high limit
end
if( sv96 > sv96needed) then
sv96=math.max(sv96-apex96_ramp_rate,sv96needed) -- lower the ISO at the APEX ramp rate
else
sv96=math.min(sv96+apex96_ramp_rate,sv96needed) -- increase the ISO at the APEX ramp rate
end
end
end
if (nd_filter==1) then
if (tv96 > nd_tv_limit ) then
tv96m = tv96-nd96
set_nd_filter(1) -- activate the ND filter
nd_string=" ["..print_tv(tv96m).."+NDin] "
nd_state=1
else
tv96m = tv96
set_nd_filter(2) -- make sure the ND filter does not activate
nd_string=" [NDout] "
nd_state=0
end
else
tv96m = tv96
set_nd_filter(2) -- make sure the nd filter does not activate
nd_string=" "
nd_state=0
end
set_sv96(sv96)
set_av96_direct(av96)
set_tv96_direct(tv96m)
shoot()
if ( interval > 5000 ) then -- check if in slow mode
sleep(500)
local dof=get_dofinfo()
local imgname=string.format('IMG_%04d.JPG',get_exp_count())
sd_log(shot..","..imgname..","..compstr..","..print_tv(tv96needed)..","..print_tv(tv96)..","..print_tv(tv96m)..","..nd_state..","
.. print_sv(sv96needed)..","..print_sv(sv96)..","..print_av(av96)..","..dof.focus..","..get_tv96()..","..get_sv96()..","..get_av96()..","..get_bv96()..",")
end
print(shot.." Bv:"..bv96..compstr.." Tv*:"..print_tv(tv96needed).." Tv:"..print_tv(tv96)..nd_string.."ISO"..print_sv(sv96))
shot=shot+1
if ( display_mode ~=2 ) then display_state = 1 end -- shooting re-enables backlight and exits sleep mode (if used)
set_display(0)
end
-- process things that happen once every 15 seconds
if ( now > ticmin ) then
ticmin = now+15000
set_display(0)
collectgarbage()
end
-- process things that happen once per second
if ( now > ticsec ) then
ticsec = now+1000
if( display_hold_timer>0) then display_hold_timer=display_hold_timer-1 end
end
-- status LED
led_timer = led_timer -1
if (led_timer == 1) then
set_led(status_led,1)
elseif (led_timer == 0) then
set_led(status_led,0)
led_timer = 20
end
-- check for user input
wait_click(100)
until not( is_key("no_key"))
print("key pressed")
set_display(20)
until ( is_key("menu") or ( get_free_disk_space() < min_disk_space))
if ( get_free_disk_space() < min_disk_space) then
print("SD card full")
else
print("menu key exit")
end
restore()
end