Sunday, July 5, 2020

Automating Startup and Shutdown

When we start imaging, we have to:
  1. Turn on the Power Supply (and the mount, though we normally don't turn it off)
  2. Check if the mount is still up, and if not, start it through the MGBox v2 pulse (this only sends a pulse that will turn on the mount if it's off and turn it off if it's on, that's why we have to check first).
  3. Start the Planewave Heater Control Program (PWI3)
  4. Make sure that Dimension4 is running (to keep computer time exact)
  5. Check if the clocksync program is running, and if not, start it (to keep mount time synchronized to the exact computer time)
And at the end, do the opposite:
  1. Close clocksync
  2. Close PWI3
  3. Shutdown mount
  4. Turn off power supply
I found a couple of useful utilities/libraries:
  • dlipower - a python library to interact with the power web switch
  • MountCMD - a Java library to interact with the mount
With these, could whip up some Python code to automate both:

startup.py
import dlipower
import time
import subprocess
import socket

POWER_SWITCH_IP = "192.168.254.22"
POWER_SWITCH_USERNAME = "<username>"
POWER_SWTICH_PASSWORD = "<password>"
MOUNT_POWER_PORT_NO = 1
POWER_SUPPLY_PORT_NO = 2

MOUNT_IP_ADDRESS = '192.168.254.21'
MOUNT_PORT = 3490

MGBOX_PATH = "C:\\Program Files (x86)\\MGBox V2\\MGBox.exe"
MGBOX_COM_PORT = "COM5"

PWI_PATH = "C:\\Program Files (x86)\\PlaneWave Instruments\\PWI3\\PWI3.exe";

DIMENSION4_EXE = "D4.exe"
DIMENSION4_PATH = "C:\\Program Files (x86)\\D4\\D4.exe"

CLOCKSYNC_EXE = "clocksync_w.exe"
CLOCKSYNC_PATH = "C:\\Program Files (x86)\\10micron\\ClockSyncW\\clocksync_w.exe"

def process_exists(process_name):
    call = 'TASKLIST', '/FI', 'imagename eq %s' % process_name
    # use buildin check_output right away
    output = subprocess.check_output(call).decode()
    # check in last line for process name
    last_line = output.strip().split('\r\n')[-1]
    # because Fail message could be translated
    return last_line.lower().startswith(process_name.lower())

# --------- Turn on Mount and Power Supply ---------

# Connection parameters to web UI of power switch
switch = dlipower.PowerSwitch(hostname=POWER_SWITCH_IP, userid=POWER_SWITCH_USERNAME, password=POWER_SWTICH_PASSWORD)

print("Turning on Mount (it should not have been off, but just in case)")
switch.on(MOUNT_POWER_PORT_NO)

print("Turning on the Power Supply for all equipment")
switch.on(POWER_SUPPLY_PORT_NO)

print("Wait a little to make sure...")
time.sleep(5)


# --------- Starting Mount ----------

# We can only send an on/off pulse, i.e. we first have to check if the mount is already powered on

print("Checking if mount is started by pinging IP address")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mount_is_running = sock.connect_ex((MOUNT_IP_ADDRESS, MOUNT_PORT))
if mount_is_running != 0:
   print("Starting mount")
   subprocess.call([MGBOX_PATH, "-P", MGBOX_COM_PORT, "-T", "1500"])
   print("Wait quite a while ...")
   time.sleep(30)
else:
   print("Mount is already started!")


# --------- Starting Planewave Heater control app ---------

# The planewave app is reentrnt, i.e. we don't have to check if it's already running
print("Starting Planewave Heater Control app")
subprocess.Popen([PWI_PATH])


# --------- Making sure that Dimension4 is up and running ---
print("Check if Dimension4 is running")
if not process_exists(DIMENSION4_EXE):
   print("Start Dimension4")
   subprocess.Popen(DIMENSION4_PATH)
   print("Wait a little time to make sure ...")
   time.sleep(5)
else:
   print("Dimension4 already running. moving along ...")


# --------- Starting mount time sync -------------------------

# First, check if it's already running
print("Check if time sync program is already running")
if not process_exists(CLOCKSYNC_EXE):
   print("Starting time sync program")
   subprocess.Popen([CLOCKSYNC_PATH])
   print("Wait a little to make sure ...")
   time.sleep(5)
else:
   print("Time Sync program is already started")

print("Done")

shutdown.py
import dlipower
import time
import subprocess
import socket

POWER_SWITCH_IP = "192.168.254.22"
POWER_SWITCH_USERNAME = "<username>"
POWER_SWTICH_PASSWORD = "<password>"
MOUNT_POWER_PORT_NO = 1
POWER_SUPPLY_PORT_NO = 2

MOUNT_IP_ADDRESS = '192.168.254.21'
MOUNT_PORT = 3490

MGBOX_PATH = "C:\\Program Files (x86)\\MGBox V2\\MGBox.exe"
MGBOX_COM_PORT = "COM5"

PWI_EXE = "PWI3.exe"
PWI_PATH = "C:\\Program Files (x86)\\PlaneWave Instruments\\PWI3\\PWI3.exe";

DIMENSION4_EXE = "D4.exe"
DIMENSION4_PATH = "C:\\Program Files (x86)\\D4\\D4.exe"

CLOCKSYNC_EXE = "clocksync_w.exe"
CLOCKSYNC_PATH = "C:\\Program Files (x86)\\10micron\\ClockSyncW\\clocksync_w.exe"

def process_exists(process_name):
    call = 'TASKLIST', '/FI', 'imagename eq %s' % process_name
    # use buildin check_output right away
    output = subprocess.check_output(call).decode()
    # check in last line for process name
    last_line = output.strip().split('\r\n')[-1]
    # because Fail message could be translated
    return last_line.lower().startswith(process_name.lower())

# --------- Stopping mount time sync -------------------------

# First, check if it's running
print("Check if time sync program is running")
if process_exists(CLOCKSYNC_EXE):
   print("Stopping time sync program")
   subprocess.call(["taskkill", "/im", CLOCKSYNC_EXE])
else:
   print("Time Sync program isn't running ...  moving on ...")


# --------- Stopping Planewave Heater control app ---------

#First, check if it's running
print("Check if Planewave Heater Control app is running")
if process_exists(PWI_EXE):
   print("Stopping Planewave Heater Control app")
   subprocess.call(["taskkill", "/im", PWI_EXE])
else:
   print("Planewave Heater Control app isn't running ...  moving on ...")

# --------- Shutdown Mount -------------------------
print("Shutdown mount")
subprocess.Popen("C:\\Users\\namid\\bin\\MountCMD\\MountCMD-shutdown.bat") 
print("Wait to make sure that the mount is really turned off")
time.sleep(30)

# --------- Turn off Power Supply ---------------------

# Connection parameters to web UI of power switch
switch = dlipower.PowerSwitch(hostname=POWER_SWITCH_IP, userid=POWER_SWITCH_USERNAME, password=POWER_SWTICH_PASSWORD)

print("Turning off the Power Supply for all equipment")
switch.off(POWER_SUPPLY_PORT_NO)

print("Done")

Works like c charm! That was a fun activity!!!

Wednesday, July 1, 2020

The ML50100

The FLI ML50100 is an amazing camera. With a pixel size of 6 microns and 8176 x 6132 pixels!!!

But, boy, is it tricky to calibrate the images. There are several challenges:
  • Because of the large pixel size, the shutter is also very large - and hence slow. Flats need to be at least 8-10 seconds - otherwise the shutter will show up.
  • Being front illuminated, the chip has RBI.
  • The chip is so big, that it actually has two readouts - one for each half. And each readout has slightly different characteristics (bias, gain)
  • Richard thinks that there could be bias drift depending on Ambient temperature
Here is a (stretched) 10 min dark master frame:

And a bias master frame:


The seams are clearly visible. 

First, calibrate light frames using the master dark:

Pre-dark calibration:
If you look closely, you can see the seam in the middle ...

Post-dark calibration:

Looks pretty clean. But if I now stack my (40) luminance frames (without alignment - I just want to check if the seam is gone):


The seam is clearly still there, i.e. the individual frames weren't completely cleared up. And this was even more complicated with the flat frames. Because our flat panel broke, we had to use sky flats, i.e. the exposure time is different for each. And because of the two different halves, we can't scale our darks, i.e. we have to take darks at the same duration as the flats ...

We have three theories where this could come from:
  1. The RBI issue - though Richard said at 10 minute exposure and -15C we should see much if any
  2. The bias is dependent on the ambient temperature
  3. Our darks aren't clean (we had some darks with clear light in them - maybe they all suffer from minor light leaks ...)
I ran two experiments to check this:

1. Darks with roof closed vs. open
I took several 10 min dark frames with the roof close and the roof open (with some moonlight):
OverallLeftRight
Roof OpenRoof ClosedRoof OpenRoof ClosedRoof OpenRoof Closed
101710161011101010211019

The levels are just so slightly higher when the roof was open...

2. Bias at different ambient temperatures
I took a stack of 20 bias frames at various ambient temperatures:

Ambient Temp [C]Level - OverallLevel - LeftLevel Right
20.91,0121,0071,016
23.51,0121,0071,016
26.31,0121,0071,015
281,0101,0051,013

Not much difference - except the first step from 28 to 26.3 degrees. The difference here was that the 28 degree bias were taken with the roof close, the other when it was open...

This seems to indicate that we have some light leaks, but not so much that ambient temperature plays a role.

If this is indeed the case, then there isn't much that we can do about it now... But in a month, I'll be at the site again (for some leftover work). At that time I could take better darks by either covering the scope (in particular the front) or taking the camera off completely and take darks with the front fully covered.