Exposure time ramp

Got an idea for something that SharpCap should do? Share it here.
Forum rules
'+1' posts are welcome in this area of the forums to indicate your support for a particular feature suggestion. Suggestions that get the most +1's will be seriously considered for inclusion in future versions of SharpCap.
javierdeelias
Posts: 8
Joined: Fri Sep 29, 2023 4:16 pm

Exposure time ramp

#1

Post by javierdeelias »

Hello,
The 12 December occultation of Betelgeuse is particularly interesting as the apparent diameter of Betelgeuse is similar to that of the asteroid that will occulte it. There are many professionals and amateurs organising teams to capture the occultation.
The aim is to generate a general light curve of the occultation with the difficulty that the luminosity will gradually decrease by 12 magnitudes during the occultation. It is very difficult to establish an exposure time that is valid both for the beginning (magnitude <1) and for the end after a few seconds, which will be magnitude 13. What I propose as a solution is to define an exposure time ramp by defining, for example, 5ms at the beginning and 100ms after 10s. Certainly there is an additional difficulty and that is that there should be a button to start the ramp because there is always uncertainty in the starting time. Of course, if the start of the ramp could be automatic when the magnitude decreases by 1 or 2 units, the solution would be even more powerful.
In this way, when doing the photometric reduction with the comparison stars, the whole range of variation can be covered.
Would it be possible to have this functionality?
Regards,
Javier
User avatar
admin
Site Admin
Posts: 13370
Joined: Sat Feb 11, 2017 3:52 pm
Location: Vale of the White Horse, UK
Contact:

Re: Exposure time ramp

#2

Post by admin »

Hi Javier,

thanks for the suggestion. It's obviously going to be a challenge to image this particular event, but it also seems like quite an unusual event and a niche request.

My usual suggestion for this sort of thing is that it may well be possible to implement this using SharpCap's built in Python scripting functionality - certainly that willl allow control of the exposure with a adjustments being made on a schedule and also allows for custom buttons to be added to the toolbar that can trigger functionality. Even if you don't have any Python programming skills, there may be someone among the wider community interested in this event who does. There is some documentation in the user manual (https://docs.sharpcap.co.uk/4.1/#!2!Scripting) and more help in the Scripting forum.

cheers,

Robin
Jean-Francois
Posts: 402
Joined: Sun Oct 13, 2019 10:52 am
Location: Germany

Re: Exposure time ramp

#3

Post by Jean-Francois »

Hello Javier and Robin,

I had the same idea, but I think that I have a better one now.

The problem of the exposure time ramp is ... it will be difficult to analyse with the different time.
Other problem ... you can not have the LED calibration with the QHY-174GPS camera. At least not easy, you can calibrate in advance the LED Start/End values for different exposure and then in the script change the exposure and the LED values.

My other idea is to have a constant exposure but to change the focus.
Start with a defocused star spot, detect the maximum value, when the value drops lower than a threshold, then perform a small focus step.
Repeat several time. The difficulty will be to defocus again the star when the intensity grows again.

Robin, I have already 2 scripts reading the statistics of a rectangle region.
But ... how to have the maximum value in the rectangle region ?

Regards,
Jean-Francois

Here a script detecting the pixel average over the rectangle region (note that is not the ROI), but that is the red rectangle visible with the histogram tool. The script shows the pixels average live in a graphic. Change if necessary the "wait_time = 0.05" line.
The script is not synchronised with the new image ... that should be an improvement in my script.

Code: Select all

# *******************************************************************************************
# 
# SharpCap script "S4_COL_monitoring.py"
# 2023/05/03 Jean-Francois
#
# Version: 1.0.0:   First version
#
# The script works only with a SharpCap PRO version.
#
# *******************************************************************************************


import time
import datetime
import math
import statistics
import clr
clr.AddReference("Oxyplot")
clr.AddReference("OxyPlot.WindowsForms ")
clr.AddReference("System.Drawing")
clr.AddReference("System.Windows.Forms")

import OxyPlot
import System.Drawing
import System.Drawing.Drawing2D 
import System.Windows.Forms

from System import Array
from System.Drawing import *
from System.Drawing.Drawing2D import GraphicsPath, CustomLineCap, SmoothingMode, LineJoin
from System.Windows.Forms import *
from System.Threading import Thread, ThreadStart, ApartmentState, ParameterizedThreadStart


def framehandler(sender, args):
    if (dumpdata):
        global Mean
        cutout = args.Frame.CutROI(SharpCap.Transforms.SelectionRect)
        Stat = cutout.GetStats()
        Mean = Stat.Item1
        cutout.Release()

def evthandler(sender, args):
    if (SharpCap.SelectedCamera != None):
        SharpCap.SelectedCamera.FrameCaptured -= framehandler

def monitorFrames():
    SharpCap.SelectedCamera.FrameCaptured += framehandler


# *****************************************************************************
def COL_measurement(self):
    global dumpdata
    #global Mean
    global Time_meas
    global COL_meas

    self.COL_Monitoring.Text = "Monitoring running"
    self.COL_Monitoring.BackColor = Color.Red
    self.COL_Monitoring.Enabled = False

    #SharpCap.Transforms.SelectionRect = Rectangle(10, 10, 3036, 2028)

    dumpdata = True
    SharpCap.CaptureEvent += evthandler
    monitorFrames()

    expos_ms = SharpCap.SelectedCamera.Controls.Exposure.ExposureMs
    
    wait_time = 0.05       # wainting time between 2 measurements [s]
    print("Waiting Time: %5.3f" % (wait_time))
    
    time.sleep(wait_time)

    print()
    print("S4 COL Monitoring:")
    print("******************")
    Time_meas = []
    COL_meas = []

    if (int(self.textBoxTime.Text) > 0.0):
        Max_time = int(self.textBoxTime.Text)

        t0 = datetime.datetime.now()
        dt = 0

        while  (dt < Max_time):
            dt = (datetime.datetime.now() - t0).total_seconds()
            Time_meas.append(dt)
            COL_meas.append(Mean)
            time.sleep(wait_time)
            self.ShowOxyGraphic()

        #self.ShowOxyGraphic()

        print("Time:", Time_meas)
        print("COL:", COL_meas)
        print()
        st_dev = statistics.pstdev(COL_meas)
        print("Standard deviation: " + str("%.4f" % round(st_dev,4)))
    else:
        print("Measurement time need to be larger than 0 s")

    dumpdata = False
    SharpCap.CaptureEvent -= evthandler
    print()
    print("Measurement done")

    self.COL_Monitoring.Text = "Start Measurement"
    self.COL_Monitoring.BackColor = Color.Gainsboro
    self.COL_Monitoring.Enabled = True

    file_path = "C:\\Projekte\\S4_OGSE\\Script_COL_Monitoring\\"
    file_name = file_path + "COL_monitoring_" + str(t0).split()[1].split(".")[0].replace(":","-") + ".txt"
    file = open(file_name, "w")
    for index in range(len(Time_meas)):
        file.write(str("%.4f" % Time_meas[index]) + " " + str("%.4f" % COL_meas[index]) + "\n")
    file.close()

    return()

# *****************************************************************************

class MonitoringCOLMenuForm(Form):
    def __init__(self):
        self.SuspendLayout()
        self.InitializeComponent()
        self.setupCheckButtons()
        self.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi
        self.AutoScaleDimensions = SizeF(96, 96)
        self.ResumeLayout()

    def InitializeComponent(self):
        self.Text = "S4 COL monitoring"
        self.ClientSize = System.Drawing.Size(700, 400)
        self.TopMost = True

        self.labelTime = Label()
        self.textBoxTime = TextBox()

        self.plot1 = OxyPlot.WindowsForms.PlotView()
        self.plot1.Location = System.Drawing.Point(10, 50)
        self.plot1.Size = System.Drawing.Size(680, 340)
        self.plot1.AutoSize = True

        self.Controls.Add(self.labelTime)
        self.Controls.Add(self.textBoxTime)
        self.Controls.Add(self.plot1)

        self.labelTime.AutoSize = True
        self.labelTime.Location = Point(150, 17)
        self.labelTime.Name = "labelTime"
        self.labelTime.Text = "Measurement time [s]:"

        self.textBoxTime.AutoSize = True
        self.textBoxTime.Location = Point(280, 16)
        self.textBoxTime.Name = "textBoxTime"
        self.textBoxTime.Size = Size(30, 20)
        self.textBoxTime.Text = "1"


    def setupCheckButtons(self):
        self.COL_Monitoring = Button()
        self.COL_Monitoring.Text = "Start Measurement"
        self.COL_Monitoring.Location = Point(20, 10)
        self.COL_Monitoring.Click += self.COL_monitor
        self.COL_Monitoring.AutoSize = True

        self.button_Exit = Button()
        self.button_Exit.Text = "Exit"
        self.button_Exit.Location = Point(350, 10)
        self.button_Exit.Click += self.exit
        self.button_Exit.AutoSize = True

        self.AcceptButton = self.COL_Monitoring
        self.CancelButton = self.button_Exit
        self.Controls.Add(self.COL_Monitoring)
        self.Controls.Add(self.button_Exit)

    def ShowOxyGraphic(self):
        self.plotmodel1 = OxyPlot.PlotModel()

        self.YAxis1 = OxyPlot.Axes.LinearAxis(Position = OxyPlot.Axes.AxisPosition.Left, Minimum = 0.999*min(COL_meas), Maximum = 1.001*max(COL_meas))

        self.points_start = []
        for i in range(0,len(Time_meas)):
            self.points_start.Add(OxyPlot.DataPoint(Time_meas[i],COL_meas[i]))


        self.serie_1 = OxyPlot.Series.LineSeries()
        self.serie_1.Color  = OxyPlot.OxyColors.Blue
        self.serie_1.MarkerSize = 2
        self.serie_1.MarkerType = OxyPlot.MarkerType.Circle
        self.serie_1.StrokeThickness = 1
        self.serie_1.ItemsSource = self.points_start

        self.plot1.Model = self.plotmodel1

        self.plotmodel1.Axes.Add(self.YAxis1)
        self.plotmodel1.Series.Add(self.serie_1)


    def COL_monitor(self, sender, event):
        th = Thread(ParameterizedThreadStart(COL_measurement))
        th.SetApartmentState(ApartmentState.STA)
        th.Start(self)

    def exit(self, sender, event):
        print("Stop Monitoring script")
        self.Close()


def launch_form():
    form = MonitoringCOLMenuForm()
    form.StartPosition = FormStartPosition.CenterScreen
    form.TopMost = True
    form.Show()

SharpCap.AddCustomButton(" COL__Monitoring  |", None, "COL__Monitoring", launch_form)

javierdeelias
Posts: 8
Joined: Fri Sep 29, 2023 4:16 pm

Re: Exposure time ramp

#4

Post by javierdeelias »

Hello Jean-Francois,

I find this an interesting alternative. Perhaps the problem I see is that the occultation will take only a few seconds and if you move the focus you will be losing time to capture the images only when the focus is at rest and you will have less images losing resolution in the light curve.

My idea is to generate a sequence of fits with different exposure times containing comparison stars. For reduction, when doing differential photometry the different exposure time in the images does not present a problem. In my case I don't use a GPS camera but a local GPS PPS time server that operates as a stratum 0.

I see that you have a lot of experience with Shapcap scripts and I would like to ask you if you could help me in the development of the exposure time change, so we would have two strategies and we could test both before occultation.

Regards,
Javier
Jean-Francois
Posts: 402
Joined: Sun Oct 13, 2019 10:52 am
Location: Germany

Re: Exposure time ramp

#5

Post by Jean-Francois »

Hello Javier,

I guess that the readout of the camera is independent of the other function of SharpCap.
So, if the focuser is moving a little bit, then it will not change the frame per second of the camera.

That is different with changing the exposure time. What will be the delay in the camera for changing the exposure time ?
Other words ... How many frames will be dropped until the camera "starts" again the capture with the new exposure time ?
During normal use of the camera, nobody is interested to know if the camera change the exposure in 1 µs or 100 ms.
But it will be different for the occultation, for example with 20 ms exposure time, then it could have 5 images dropped until the new exposure starts.

For Betelgeuse, it is not clear if it will be a total or a partial occultation (and it depends of the location).
If partial, nobody will know the minimum intensity. So the minimum could be 8 magnitude or 9, 10 or 12.
That is a problem for both methods. What will happen if the magnitude is lower than 12 ? ... you have maybe set the gain=0 and the result will be several seconds ... that will be no so good.


Concerning my script ...
- catch one image in memory and analyse the spot (if possible) or the small rectangle region
- repeat the analysis of the images until the value changes below a threshold value
- perform the focus shift or a longer exposure time (the steps have to be defined)
- continue the analysis of the images.
- if the value changes above a threshold, then it is time to move "back" the focus or to reduce the exposure time

If my script could be modified quickly, then yes, I could help you for the "exposure change" version.

Regards,
Jean-Francois
javierdeelias
Posts: 8
Joined: Fri Sep 29, 2023 4:16 pm

Re: Exposure time ramp

#6

Post by javierdeelias »

Hi Jean-Francois and Robin,

I think your comments make sense and I would definitely like to try your option too. I will study your script and if I have any doubts on how to adapt it to the change of exposure time I count on your help, thank you very much.

@Robin:
The problem with this development is that it is very difficult to test before occultation. My question is whether it would be possible to have a camera simulator in which the image is a star that is occulted (typically a ramp down, a minimum base, and a ramp up, all in few seconds) and this is reproduced cyclically. If in addition to the variable star there are fixed stars of different magnitudes it would be perfect.
I think this functionality might be of interest in general to Sharpcap users who use it for occultations, which I think are the majority.

Regards,
Javier
User avatar
admin
Site Admin
Posts: 13370
Joined: Sat Feb 11, 2017 3:52 pm
Location: Vale of the White Horse, UK
Contact:

Re: Exposure time ramp

#7

Post by admin »

Hi,

as far as testing is concerned, you can probably work with the 'Test Camera 2 (High Speed)'. This test camera can load either a still image and replay it every frame or a video in SER format.

So, the procedure might be like this...

* Load an image with a star into the test camera - you can adjust the brightness using the gain control to simulate the dimming/brightening.

* Record an SER video of the star dimming/brightening - either adjust gain manually for this or use python scripting

* Now load the recorded SER into the test camera - it will play the video of the star dimming/brightening on repeat

* Adjusting the exposure (or gain) on the test camera causes the brightness to vary as you would expect

cheers,

Robin
Jean-Francois
Posts: 402
Joined: Sun Oct 13, 2019 10:52 am
Location: Germany

Re: Exposure time ramp

#8

Post by Jean-Francois »

Hello Javier,

Do you know that you can model an occultation with Tangra ?
Tangra_Video_modelling.jpg
Tangra_Video_modelling.jpg (59.76 KiB) Viewed 6479 times
Regards,
Jean-Francois
javierdeelias
Posts: 8
Joined: Fri Sep 29, 2023 4:16 pm

Re: Exposure time ramp

#9

Post by javierdeelias »

Hello Jean-Francois,

Thank you very much for pointing me to this option. As far as I've seen it is not possible to generate a ramp-simulating occultation with Tangra, it only allows a total and sudden occultation, but I'm talking to the developer of Tangra to see if it would be possible to add the option of ramp down brightness.

Regards,
Javier
Jean-Francois
Posts: 402
Joined: Sun Oct 13, 2019 10:52 am
Location: Germany

Re: Exposure time ramp

#10

Post by Jean-Francois »

Hello Robin and Javier,

I have a new version of my script.

It uses the SharpCap CutROI() and GetStats() functions.

But I wish for the Betelgeuse occultation use a different function.
Do you have an intern function for the maximum value in the ROI rectangle ?

For the OpenCVStarDetector from SharpCap.ImageProcessing ... can you give me some help how to use it ?
I wrote some code, but when I used the Test Camera 1 and short exposure, the script crash with SharpCap.

Here the last version of my script with the Mean() function:

Code: Select all

# *******************************************************************************************
# 
# SharpCap script "Mean_monitoring.py"
# 2023/11/15 Jean-Francois
#
# Version: 1.0.0:   First version
#
# *******************************************************************************************

import time
import datetime
import math
import clr
clr.AddReference("Oxyplot")
clr.AddReference("OxyPlot.WindowsForms ")
clr.AddReference("System.Drawing")
clr.AddReference("System.Windows.Forms")

import OxyPlot
import System.Drawing
import System.Drawing.Drawing2D
import System.Windows.Forms

from System import Array
from System.Drawing import *
from System.Drawing.Drawing2D import GraphicsPath, CustomLineCap, SmoothingMode, LineJoin
from System.Windows.Forms import *
from System.Threading import Thread, ThreadStart, ApartmentState, ParameterizedThreadStart

Mean = 0.0
t0   = 0.0
Time_frame = []
Mean_frame = []
dumpdata = False

def framehandler(sender, args):
    global Mean
    global t0
    global Time_frame
    global Mean_frame
    global dumpdata

    if (dumpdata):
        cutout = args.Frame.CutROI(SharpCap.Transforms.SelectionRect)
        Stat = cutout.GetStats()
        Mean = Stat.Item1
        dt = (datetime.datetime.now() - t0).total_seconds()
        Time_frame.append(dt)
        Mean_frame.append(Mean)
        print(" Mean = ", Mean)
        form.ShowOxyGraphic()
        cutout.Release()

def evthandler(sender, args):
    if (SharpCap.SelectedCamera != None):
        SharpCap.SelectedCamera.FrameCaptured -= framehandler

def monitorFrames():
    SharpCap.SelectedCamera.FrameCaptured += framehandler

def mean(xs):
    return sum(xs) / len(xs)

def stddev(xs):
    m = mean(xs)
    var = sum(abs(x - m) for x in xs) / len(xs)
    return math.sqrt(var)

# *****************************************************************************
def Mean_measurement(self):
    global t0
    global Time_frame
    global Mean_frame
    global dumpdata

    self.Monitoring.Text = "Monitoring running"
    self.Monitoring.BackColor = Color.Red
    self.Monitoring.Enabled = False

    Time_frame = []
    Mean_frame = []

    dumpdata = True
    t0 = datetime.datetime.now()
    monitorFrames()

    print()
    print("Frame Mean Monitoring:")
    print("******************")

    if (int(self.textBoxTime.Text) > 0.0):
        time.sleep(int(self.textBoxTime.Text))
        print()
        print(" Time: ", Time_frame)
        print(" Mean: ", Mean_frame)
        print(" Number: ", len(Time_frame))
        print()
        print(" Standard deviation: " + str("%.4f" % round(stddev(Mean_frame),4)))
    else:
        print(" Measurement time need to be larger than 0 s")

    dumpdata = False
    #SharpCap.CaptureEvent -= evthandler
    SharpCap.SelectedCamera.FrameCaptured -= framehandler

    print()
    print(" Measurement done")

    self.Monitoring.Text = "Start Measurement"
    self.Monitoring.BackColor = Color.Gainsboro
    self.Monitoring.Enabled = True

    #file_path = "C:\\Temp\\Mean_COL_Monitoring\\"
    #file_name = file_path + "Mean_monitoring_" + str(t0).split()[1].split(".")[0].replace(":","-") + ".txt"
    #file = open(file_name, "w")
    #for index in range(len(Time_frame)):
    #    file.write(str("%.4f" % Time_frame[index]) + " " + str("%.4f" % Mean_frame[index]) + "\n")
    #file.close()

    return()

# *****************************************************************************

class MonitoringMeanMenuForm(Form):
    def __init__(self):
        self.SuspendLayout()
        self.InitializeComponent()
        self.setupCheckButtons()
        self.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi
        self.AutoScaleDimensions = SizeF(96, 96)
        self.ResumeLayout()

    def InitializeComponent(self):
        self.Text = "Frame Mean Monitoring"
        self.ClientSize = System.Drawing.Size(700, 400)
        self.TopMost = True

        self.labelTime = Label()
        self.textBoxTime = TextBox()

        self.plot1 = OxyPlot.WindowsForms.PlotView()
        self.plot1.Location = System.Drawing.Point(10, 50)
        self.plot1.Size = System.Drawing.Size(680, 340)
        self.plot1.AutoSize = True

        self.Controls.Add(self.labelTime)
        self.Controls.Add(self.textBoxTime)
        self.Controls.Add(self.plot1)

        self.labelTime.AutoSize = True
        self.labelTime.Location = Point(150, 17)
        self.labelTime.Name = "labelTime"
        self.labelTime.Text = "Measurement time [s]:"

        self.textBoxTime.AutoSize = True
        self.textBoxTime.Location = Point(280, 16)
        self.textBoxTime.Name = "textBoxTime"
        self.textBoxTime.Size = Size(30, 20)
        self.textBoxTime.Text = "1"

    def setupCheckButtons(self):
        self.Monitoring = Button()
        self.Monitoring.Text = "Start Measurement"
        self.Monitoring.Location = Point(20, 10)
        self.Monitoring.Click += self.Mean_monitor
        self.Monitoring.AutoSize = True

        self.button_Exit = Button()
        self.button_Exit.Text = "Exit"
        self.button_Exit.Location = Point(350, 10)
        self.button_Exit.Click += self.exit
        self.button_Exit.AutoSize = True

        self.AcceptButton = self.Monitoring
        self.CancelButton = self.button_Exit
        self.Controls.Add(self.Monitoring)
        self.Controls.Add(self.button_Exit)

    def ShowOxyGraphic(self):
        global Time_frame
        global Mean_frame

        self.plotmodel1 = OxyPlot.PlotModel()
        self.YAxis1 = OxyPlot.Axes.LinearAxis(Position = OxyPlot.Axes.AxisPosition.Left, Minimum = 0.999*min(Mean_frame), Maximum = 1.001*max(Mean_frame))

        self.points_start = []
        for i in range(0,len(Time_frame)):
            self.points_start.Add(OxyPlot.DataPoint(Time_frame[i],Mean_frame[i]))

        self.serie_1 = OxyPlot.Series.LineSeries()
        self.serie_1.Color  = OxyPlot.OxyColors.Blue
        self.serie_1.MarkerSize = 2
        self.serie_1.MarkerType = OxyPlot.MarkerType.Circle
        self.serie_1.StrokeThickness = 1
        self.serie_1.ItemsSource = self.points_start

        self.plot1.Model = self.plotmodel1

        self.plotmodel1.Axes.Add(self.YAxis1)
        self.plotmodel1.Series.Add(self.serie_1)

    def Mean_monitor(self, sender, event):
        th = Thread(ParameterizedThreadStart(Mean_measurement))
        th.SetApartmentState(ApartmentState.STA)
        th.Start(self)

    def exit(self, sender, event):
        print("Stop Monitoring script")
        self.Close()

#def launch_form():
form = MonitoringMeanMenuForm()
form.StartPosition = FormStartPosition.CenterScreen
form.TopMost = True
form.Show()

#SharpCap.AddCustomButton(" Mean__Monitoring  |", None, "Mean__Monitoring", launch_form)


One thing more ... I'm not sure that I use the correct way to stop the framehandler function.
In the past I used: SharpCap.CaptureEvent -= evthandler
But with my script today it seems to add an execution of the function at each start of the script.
I change to SharpCap.SelectedCamera.FrameCaptured -= framehandler

If you have a better solution, please say me.

Concerning the automatic focusing during the occultation ... I did some tests last Friday during ~ 1 hour clear sky.
With my Newton, if I set the camera gain and exposure for a star with 6 magnitude, then it was necessary to defocus the star with ~ 800 steps.
The star was a big donut. It will be not easy to perform the defocus in several steps. It depends on the intern delay in the focuser and his driver.
Alternative could be to move the focuser at a defined rate. It is possible to set different parameter in the driver of my SestoSenso2 focuser.
I will search a possibility to move a defined number of focus steps per second.

Javier ... so it means that if I have a solution, it will be very dependant on the hardware.
If you have a SestoSenso too, then it will simplify it.

Regards,
Jean-Francois
Post Reply