Project

General

Profile

RE: image shift target rotated by 90 degree between targe... ยป presets.py

Anchi Cheng, 05/07/2014 02:12 PM

 
# The Leginon software is Copyright 2003
# The Scripps Research Institute, La Jolla, CA
# For terms of the license agreement
# see http://ami.scripps.edu/software/leginon-license
#
# $Source: /ami/sw/cvsroot/pyleginon/presets.py,v $
# $Revision: 1.270 $
# $Name: not supported by cvs2svn $
# $Date: 2008-02-26 00:34:19 $
# $Author: pulokas $
# $State: Exp $
# $Locker: $

import node
import calibrationclient
from leginon import leginondata
import event
import copy
import threading
import time
import unique
from pyami import ordereddict, imagefun, arraystats
import gui.wx.PresetsManager
import instrument
import random
import math
import numpy

## counter for dose images
import itertools
idcounter = itertools.cycle(range(100))

class PresetChangeError(Exception):
pass

class CurrentPresetData(leginondata.Data):
def typemap(cls):
t = leginondata.Data.typemap()
t += [('preset', leginondata.PresetData)]
return t
typemap = classmethod(typemap)

class CurrentPresetPublishEvent(event.PublishEvent):
dataclass = CurrentPresetData

class PresetsClient(object):
'''
client functions for nodes to access PresetsManager
'''
eventinputs = [event.PresetChangedEvent, event.PresetPublishEvent, event.DoseMeasuredEvent]
eventoutputs = [event.ChangePresetEvent, event.MeasureDoseEvent]
def __init__(self, node):
self.node = node
self.node.addEventInput(event.PresetChangedEvent, self.presetchanged)
self.node.addEventInput(event.PresetPublishEvent, self.onPresetPublished)
self.node.addEventInput(event.DoseMeasuredEvent, self.doseMeasured)
self.pchanged = {}
self.dose_measured = {}
self.currentpreset = None

def getPresetFromDB(self, name):
session = self.node.session
query = leginondata.PresetData(session=session, name=name)
try:
return self.node.research(datainstance=query, results=1)[0]
except IndexError:
raise ValueError('no preset \'%s\' in the database' % name)

def isPresetNameToAvoid(self,pname):
'''
Avoid derived presets. '-' is used for aligned dd sum image.
'Zproj' is the projection of full tomogram
'''
presets_to_avoid = ['-','Zproj']
avoid = False
for name_str in presets_to_avoid:
if name_str in pname:
avoid = True
break
return avoid

def getPresetsFromDB(self, session=None):
'''
get ordered list of presets for this session from DB
'''
if session is None:
session = self.node.session

## find presets that belong to this session
pquery = leginondata.PresetData(session=session)
plist = self.node.research(datainstance=pquery)
if not plist:
return {}

### only want most recent of each name, none that are removed
## index by number so we can sort
pdict = {}
done = {}
for p in plist:
pname = p['name']
if not pname or pname in done:
continue
if self.isPresetNameToAvoid(pname):
continue
done[pname] = None
if p['removed']:
continue
pnumber = p['number']
# to be backward compatible
# if number was not in DB or number is not unique
if pnumber is None or pnumber in pdict:
pdict[pname] = p
else:
pdict[pnumber] = p

## sort by number (maybe name if old, non-numbered data)
keys = pdict.keys()
keys.sort()
namedict = ordereddict.OrderedDict()
for key in keys:
p = pdict[key]
namedict[p['name']] = p
return namedict

def updatePreset(self, presetname, params):
evt = event.UpdatePresetEvent()
evt['name'] = presetname
evt['params'] = params
self.node.logger.info('Sending updated preset params: %s' % (presetname,))
self.node.outputEvent(evt, wait=True)
self.node.logger.info('Preset update done')

def toScope(self, presetname, emtarget=None, keep_shift=False):
'''
presetsclient function to
send the named preset to the scope
optionally send a target to the scope as well
'''
if not presetname:
self.node.logger.error('Invalid preset name')
return
evt = event.ChangePresetEvent()
evt['name'] = presetname
evt['emtarget'] = emtarget
evt['keep image shift'] = keep_shift
self.node.logger.info('Requesting preset change to \'%s\'...' % presetname)
self.pchanged[presetname] = threading.Event()
self.node.startTimer('preset toScope')
self.node.outputEvent(evt)
self.pchanged[presetname].wait()
self.node.stopTimer('preset toScope')
self.node.logger.info('Preset change to \'%s\' completed.' % presetname)

def presetchanged(self, ievent):
self.currentpreset = ievent['preset']
name = self.currentpreset['name']

# update node's instruments to match new preset
self.node.instrument.setTEM(self.currentpreset['tem']['name'])
self.node.instrument.setCCDCamera(self.currentpreset['ccdcamera']['name'])

# if waiting for this event, then set the threading event
if name in self.pchanged:
self.pchanged[name].set()

self.node.confirmEvent(ievent)

def measureDose(self, preset_name, em_target=None):
if not preset_name:
raise ValueError('invalid preset name')
request_event = event.MeasureDoseEvent()
request_event['name'] = preset_name
request_event['emtarget'] = em_target
self.dose_measured[preset_name] = threading.Event()
self.node.outputEvent(request_event)
self.dose_measured[preset_name].wait()

def doseMeasured(self, status_event):
self.currentpreset = status_event['preset']
name = self.currentpreset['name']
if name in self.dose_measured:
self.dose_measured[name].set()
self.node.confirmEvent(status_event)

def onPresetPublished(self, evt):
if hasattr(self.node, 'onPresetPublished'):
self.node.onPresetPublished(evt)

def getCurrentPreset(self):
return self.currentpreset

def getPresetByName(self, pname):
ps = self.getPresetsFromDB()
if pname in ps:
return ps[pname]
else:
return None

def uiSinglePresetSelector(self, label='', default='', permissions='rw', persist=False):
return SinglePresetSelector(self, label, default, permissions, persist)

def getPresetNames(self):
presetlist = self.getPresetsFromDB()
return presetlist.keys()

def correctorImageExists(self, pname, type, channel):
preset = self.getPresetFromDB(pname)
scope = leginondata.ScopeEMData()
scope.friendly_update(preset)
cam = leginondata.CameraEMData()
cam.friendly_update(preset)
return self.node.correctorImageExists(type, scope, cam, channel)

def getHighestMagPresetName(self):
session = self.node.session
presets = self.getPresetsFromDB(session)
names = presets.keys()
highest_mag_preset_name = names[0]
for name in names:
if presets[highest_mag_preset_name]['magnification'] < presets[name]['magnification']:
highest_mag_preset_name = name
return highest_mag_preset_name

class PresetsManager(node.Node):
panelclass = gui.wx.PresetsManager.Panel
settingsclass = leginondata.PresetsManagerSettingsData
defaultsettings = {
'pause time': 1.0,
'xy only': True,
'stage always': True,
'cycle': True,
'optimize cycle': True,
'mag only': True,
'apply offset': False,
'blank': False,
'smallsize': 1024,
'add pause in alignment': False,
}
eventinputs = node.Node.eventinputs + [event.ChangePresetEvent, event.MeasureDoseEvent, event.UpdatePresetEvent]
eventoutputs = node.Node.eventoutputs + [event.PresetChangedEvent, event.PresetPublishEvent, event.DoseMeasuredEvent, event.MoveToTargetEvent]

def __init__(self, name, session, managerlocation, **kwargs):
node.Node.__init__(self, name, session, managerlocation, **kwargs)

self.instrument = instrument.Proxy(self.objectservice,
self.session,
self.panel)
self.calclients = {
'pixel size':calibrationclient.PixelSizeCalibrationClient(self),
'image':calibrationclient.ImageShiftCalibrationClient(self),
'stage':calibrationclient.StageCalibrationClient(self),
'beam':calibrationclient.BeamShiftCalibrationClient(self),
'beam tilt':calibrationclient.BeamTiltCalibrationClient(self),
'modeled stage':calibrationclient.ModeledStageCalibrationClient(self),
}
self.dosecal = calibrationclient.DoseCalibrationClient(self)
import navigator
self.navclient = navigator.NavigatorClient(self)

self.presetsclient = PresetsClient(self)
self.locknode = None
self._lock = threading.Lock()
self.alignnext = threading.Event()

self.currentselection = None
self.currentpreset = None
self.selectedpreset = None
self.presets = ordereddict.OrderedDict()
self.selectedsessionpresets = None

# HACK: fix me
self.last_value = None
self.old_time = None

self.addEventInput(event.ChangePresetEvent, self.changePreset)
self.addEventInput(event.MeasureDoseEvent, self.measureDose)
self.addEventInput(event.UpdatePresetEvent, self.handleUpdatePresetEvent)

## this will fill in UI with current session presets
self.getPresetsFromDB()
self.start()

def lock(self, n):
'''many nodes could be waiting for a lock. It is undefined which
one will get it first'''
self.logger.info('%s requesting lock...' % n)
self._lock.acquire()
self.locknode = n
self.logger.info('%s acquired lock' % self.locknode)

def unlock(self, n):
if n == self.locknode:
self.logger.info('%s unlocking' % n)
self.locknode = None
self._lock.release()

def blankOn(self):
if self.settings['blank']:
self.instrument.tem.BeamBlank = 'on'

def blankOff(self):
if self.settings['blank']:
self.instrument.tem.BeamBlank = 'off'

def changePreset(self, ievent):
'''
callback for received PresetChangeEvent from client
'''
### limit access to this function if lock is in place
tmplock = False
if self.locknode is None:
self.lock(ievent['node'])
tmplock = True
if self.locknode is not None:
## only locking node, or node with proper key can proceed
if self.locknode not in (ievent['node'], ievent['key']):
self.lock(ievent['node'])
tmplock = True

self.setStatus('processing')
pname = ievent['name']
emtarget = ievent['emtarget']
failwait = 60
failtries = 3
succeed = False
for i in range(failtries):
try:
if emtarget is None or emtarget['movetype'] is None:
self.logger.info('Changing preset to "%s"' % pname)
if ievent['keep image shift']:
# figure out image shift offset from current preset
scope_ishift = self.instrument.tem.ImageShift
if self.currentpreset is None:
dx = scope_ishift['x']
dy = scope_ishift['y']
else:
dx = scope_ishift['x'] - self.currentpreset['image shift']['x']
dy = scope_ishift['y'] - self.currentpreset['image shift']['y']
self._cycleToScope(pname)
if ievent['keep image shift']:
self.logger.info('Keeping pre-existing image shift offset')
# send image shift offset to scope
scope_ishift = self.instrument.tem.ImageShift
ix = scope_ishift['x'] + dx
iy = scope_ishift['y'] + dy
self.instrument.tem.ImageShift = {'x': ix, 'y': iy}
else:
self.logger.info('Changing preset to "%s" and targeting' % pname)
self.targetToScope(pname, emtarget)
except PresetChangeError:
if i < failtries-1:
self.logger.warning('preset request to "%s" failed, waiting %d seconds to try again' % (pname,failwait))
time.sleep(failwait)
else:
self.logger.error('preset request to "%s" failed %d times' % (pname,failtries))
else:
self.logger.info('Preset changed to "%s"' % pname)
succeed = True
break

if not succeed:
self.logger.error('preset request to "%s" failed %d times' % (pname,failtries))

if tmplock:
self.unlock(ievent['node'])
## should we confirm if failure?
self.confirmEvent(ievent)
self.setStatus('idle')

def measureDose(self, request_event):
self.changePreset(request_event)
preset_name = request_event['name']
self.acquireDoseImage(preset_name, display=False)

def getPresetsFromDB(self):
'''
get presets from current session out of database
'''
self.presets = self.presetsclient.getPresetsFromDB()
self.setOrder()
if self.presets:
firstname = self.presets.keys()[0]
self.selectPreset(firstname)

def importPresets(self, pdict):
'''
takes a set of presets from any session and generates
an identical set for this session
'''
## make new presets with this session
#self.presets = ordereddict.OrderedDict()
for name, preset in pdict.items():
if not name:
continue
newp = leginondata.PresetData(initializer=preset, session=self.session)
## for safety, disable random defocus range
newp['defocus range min'] = newp['defocus range max'] = None
self.presetToDB(newp)
self.presets[name] = newp
self.setOrder()
self.panel.presetsEvent()

def presetToDB(self, presetdata):
'''
stores a preset in the DB under the current session name
'''
self.publish(presetdata, database=True, dbforce=True, pubevent=True)

def presetByName(self, name):
if name in self.presets.keys():
return self.presets[name]
else:
return None

def setOrder(self, names=None, setorder=True):
'''
set order of self.presets, and set numbers
if names given, use that to determine order
otherwise, current order is ok, just update numbers
'''
if names is None:
names = self.presets.keys()

d = ordereddict.OrderedDict()
number = 0
for name in names:
p = self.presets[name]
if p['number'] != number:
newp = leginondata.PresetData(initializer=p, number=number)
self.presetToDB(newp)
else:
newp = p
d[name] = newp
number += 1
self.presets = d

self.panel.setOrder(self.presets.keys(), setorder=setorder)

def setCycleOrder(self, namelist):
## make sure nothing is added or removed from the list
## only order changes are allowed
if namelist is None:
test1 = None
else:
test1 = list(namelist)
test1.sort()
test2 = list(self.presets.keys())
test2.sort()
if test1 == test2:
self.setOrder(namelist, setorder=False)
else:
namelist = self.presets.keys()
self.panel.presetsEvent()

def getOrder(self):
return self.presets.keys()

def removePreset(self, pname):
'''
remove a preset by name
'''
if pname not in self.presets.keys():
self.panel.presetsEvent()
return

## remove from self.presets, store in DB
premove = self.presets[pname]
if premove is self.currentpreset:
message = 'You may not remove the currently set preset, send another preset to scope first'
self.logger.info(message)
self.panel.presetsEvent()
return

del self.presets[pname]
pnew = leginondata.PresetData(initializer=premove, removed=1)
self.presetToDB(pnew)

## update order, selector list, etc.
self.setOrder()
self.panel.presetsEvent()

def getOffsetImageShift(self, presetdata):
q = leginondata.StageTiltAxisOffsetData(tem=presetdata['tem'],ccdcamera=presetdata['ccdcamera'])
offsets = self.research(q, results=1)

if not offsets:
self.logger.warning('No stage axis offset has been saved, not applying offset')
return presetdata['image shift']

## convert stage offset to pixel offset
stagey = offsets[0]['offset']

fakescope = leginondata.ScopeEMData()
fakescope.friendly_update(presetdata)
fakecam = leginondata.CameraEMData()
fakecam.friendly_update(presetdata)

fakescope['stage position'] = {'x':0, 'y':0}
fakescope['high tension'] = self.instrument.tem.HighTension

position = {'x':0, 'y':-stagey}
pixelshift = self.calclients['stage'].itransform(position, fakescope, fakecam)

## convert pixel shift to image shift
newscope = self.calclients['image'].transform(pixelshift, fakescope, fakecam)
ishift = newscope['image shift']
self.logger.info('calculated image shift to center tilt axis: %s' % (ishift,))
return ishift

def toScope(self, pname, magonly=False, final=False):
'''
'''
presetdata = self.presetByName(pname)
if presetdata is None:
message = 'Preset change failed: no such preset %s' % (pname,)
self.logger.error(message)
raise PresetChangeError(message)

self.blankOn()

mymin = presetdata['defocus range min']
mymax = presetdata['defocus range max']
if None in (mymin, mymax):
mydefocus = presetdata['defocus']
else:
# min <= defocus < max
mydefocus = random.uniform(mymin, mymax)
self.logger.info('Random defocus for preset %s: %s' % (presetdata['name'], mydefocus))

scopedata = leginondata.ScopeEMData()
cameradata = leginondata.CameraEMData()

name = presetdata['name']
beginmessage = 'Changing preset to "%s"' % (name,)
endmessage = 'Preset changed to "%s"' % (name,)

if magonly:
mag = presetdata['magnification']
beginmessage = beginmessage + ' (mag only: %s)' % (mag,)
endmessage = endmessage + ' (mag only: %s)' % (mag,)
scopedata['magnification'] = mag
cameradata = None
else:
scopedata.friendly_update(presetdata)
if self.settings['apply offset']:
scopedata['image shift'] = self.getOffsetImageShift(presetdata)
cameradata.friendly_update(presetdata)
if not final:
scopedata['tem energy filter'] = None
scopedata['tem energy filter width'] = None
cameradata['energy filter'] = None
cameradata['energy filter width'] = None

scopedata['aperture size'] = None
scopedata['defocus'] = mydefocus

self.logger.info(beginmessage)

if presetdata['tem'] is None:
message = 'Preset change failed: no TEM selected for this preset'
self.logger.error(message)
raise PresetChangeError(message)
if presetdata['ccdcamera'] is None:
message = 'Preset change failed: no CCD camera selection for this preset'
self.logger.error(message)
raise PresetChangeError(message)
if presetdata['tem']['name'] in self.instrument.getTEMNames():
try:
self.instrument.setTEM(presetdata['tem']['name'])
except Exception, e:
msg = 'Preset change failed: %s' % (e,)
self.logger.error(msg)
raise PresetChangeError(msg)
else:
if presetdata['tem']['name']:
msg = 'Preset change failed: cannot set TEM to %s' % presetdata['tem']['name']
else:
msg = 'Preset change failed: no TEM selection for this preset'
self.logger.error(msg)
raise PresetChangeError(msg)

if presetdata['ccdcamera']['name'] in self.instrument.getCCDCameraNames():
try:
self.instrument.setCCDCamera(presetdata['ccdcamera']['name'])
except Exception, e:
self.logger.error(e)
msg = 'Preset change failed: %s' % (e,)
self.logger.error(msg)
raise PresetChangeError(msg)
else:
if presetdata['ccdcamera']['name']:
msg = 'Preset change failed: cannot set CCD camera to %s' \
% presetdata['ccdcamera']['name']
else:
msg = 'Preset change failed: no CCD camera selection for this preset'
self.logger.error(msg)
raise PresetChangeError(msg)

try:
self.instrument.setData(scopedata)
if cameradata is not None:
self.instrument.setData(cameradata)
except Exception, e:
self.logger.error(e)
message = 'Preset change failed: unable to set instrument'
self.logger.error(message)
raise PresetChangeError(message)

self.startTimer('preset pause')
self.logger.info('Pause for %.1f s' % (self.settings['pause time'],))
time.sleep(self.settings['pause time'])
self.stopTimer('preset pause')
if magonly:
self.currentpreset = None
else:
self.currentpreset = presetdata
self.logger.info(endmessage)
if final:
self.blankOff()
self.outputEvent(event.PresetChangedEvent(name=name, preset=presetdata))

def _fromScope(self, name, temname=None, camname=None, parameters=None, copybeam=False):
'''
create a new preset with name
if a preset by this name already exists in my
list of managed presets, it will be replaced by the new one
also returns the new preset object
'''

if not name:
self.logger.error('Invalid preset name')
return

## figure out tem and ccdcamera

if temname is None and name in self.presets:
tem = self.presets[name]['tem']
if tem is not None and 'name' in tem:
temname = tem['name']

if camname is None and name in self.presets:
cam = self.presets[name]['ccdcamera']
if cam is not None and 'name' in cam:
camname = cam['name']

try:
self.instrument.setCCDCamera(camname)
except ValueError:
self.logger.error('Cannot access CCD camera selected for this preset')
return
try:
self.instrument.setTEM(temname)
except ValueError:
self.logger.error('Cannot access TEM selected for this preset')
return

if temname is None:
self.logger.error('No TEM selected for this preset')
return
if camname is None:
self.logger.error('No CCD camera selected for this preset')
return

self.logger.info('Preset from %s, %s' % (temname, camname))
try:
scopedata = self.instrument.getData(leginondata.PresetScopeEMData)
except Exception, e:
self.logger.error('Preset from instrument failed, unable to get TEM parameters: %s' % e)
return

try:
cameradata = self.instrument.getData(leginondata.PresetCameraEMData)
except Exception, e:
self.logger.error('Preset from instrument failed, unable to get CCD camera parameters: %s' % e)
return

newparams = {}
newparams.update(scopedata)
newparams.update(cameradata)

if parameters is not None:
for parameter in newparams.keys():
if parameter not in parameters:
del newparams[parameter]

# update old preset or create new one
if name in self.presets.keys():
if self.settings['apply offset']:
self.logger.info('removing tilt axis offset from image shift before saving to preset')
oldpreset = self.presets[name]
oldimageshift = oldpreset['image shift']
oldiswithoffset = self.getOffsetImageShift(oldpreset)
newparams['image shift']['x'] -= (oldiswithoffset['x']-oldimageshift['x'])
newparams['image shift']['y'] -= (oldiswithoffset['y']-oldimageshift['y'])
newpreset = self.updatePreset(name, newparams)
self.currentpreset = newpreset
# copy beam shift to other presets at the same mag
if copybeam:
self.updateSameMagPresets(name,'beam shift')
elif parameters is not None:
raise ValueError
else:
newpreset = self.newPreset(name, newparams)

# newpreset is None if preset is not created by self.newPreset
if newpreset:
self.logger.info('Set preset "%s" values from instrument' % name)
self.beep()
return newpreset

def presetNames(self):
return self.presets.keys()

def getSessionPresets(self, sessiondata):
return self.presetsclient.getPresetsFromDB(sessiondata)

def cycleToScope(self, presetname):
self.setStatus('processing')
self._cycleToScope(presetname)
self.setStatus('idle')
self.panel.presetsEvent()

def _cycleToScope(self, presetname, dofinal=True, keep_shift=False):
'''
prestename = target preset
force = True: cycle even if cycling to same preset
magrepeat = True: allow two or more consecutive presets
that have the same magnification
magonly = True: all presets in cycle (except for final)
will only send magnification to TEM
'''
errstr = 'Preset cycle failed: %s'
if not self.settings['cycle']:
if dofinal:
try:
self.toScope(presetname, final=True)
except PresetChangeError:
pass
self.beep()
return

order = self.presets.keys()
magonly = self.settings['mag only']
magshortcut = self.settings['optimize cycle']

if presetname not in order:
estr = 'final preset %s not in cycle order list' % (presetname,)
self.logger.error(errstr % estr)
return

### check if this is the first time a preset
### has been set for this PresetManager instance
### In that case, immediately go to the requested preset
### and force a cycle anyway.
if self.currentpreset is None:
self.logger.info('First preset change, changing preset and forcing cycle')
try:
self.toScope(presetname, final=False)
except PresetChangeError:
return
force = True
else:
force = False

currentname = self.currentpreset['name']
if currentname not in order:
estr = 'current preset %s not in cycle order list' % (currentname,)
self.logger.error(errstr % estr)
return

thiscycle = self.createCycleList(currentname, presetname, magshortcut)
### We can make one more possible shortcut if we are going
### to a preset such that there are no mag changes between
### the current preset and the final preset (even in the
### reverse cycle) In such case, we can go direct.
### If len(thiscycle) == 1 already, then there is no advantage,
### but if len(thiscycle) > 1 and len(reversecycle) == 1 and
### the final preset has the same mag as the current one,
### then we can go direct
if magshortcut and len(thiscycle) > 1:
reversecycle = self.createCycleList(currentname, presetname, magshortcut, reverse=True)
if len(reversecycle) == 1:
p = self.presetByName(reversecycle[0])
if p['magnification'] == self.currentpreset['magnification']:
thiscycle = reversecycle
self.logger.info(
'Magnification adjacency detected, going directly to final preset')

## go to every preset in thiscycle except the last
for pname in thiscycle[:-1]:
try:
self.toScope(pname, magonly, final=False)
except PresetChangeError:
return

## final preset change
if dofinal:
try:
self.toScope(thiscycle[-1], final=True)
except PresetChangeError:
return
self.logger.info('Cycle completed')
self.beep()

def createCycleList(self, current, final, magshortcut, reverse=False):
order = self.presets.keys()

# start with only final in the reduced list
reduced = [final]

## propose adding previous/next presets one by one
name2 = final
preset2 = self.presetByName(name2)
while 1:
if reverse:
name1 = self.nextNameInCycle(name2, order)
else:
name1 = self.previousNameInCycle(name2, order)
if name1 == current:
break
preset1 = self.presetByName(name1)

if not preset1['skip']:
if preset1['magnification'] != preset2['magnification'] or not magshortcut:
reduced.insert(0,name1)
name2 = name1
preset2 = preset1

if magshortcut and len(reduced) > 1:
currentmag = self.currentpreset['magnification']
firstp = self.presetByName(reduced[0])
firstmag = firstp['magnification']
if firstmag == currentmag:
del reduced[0]

return reduced

def nextNameInCycle(self, currentname, order):
index = order.index(currentname)
nextindex = index + 1
if nextindex == len(order):
nextindex = 0
return order[nextindex]

def previousNameInCycle(self, currentname, order):
index = order.index(currentname)
previndex = index - 1
return order[previndex]

def fromScope(self, newname, temname=None, camname=None, copybeam=False):
newpreset = self._fromScope(newname, temname, camname, None, copybeam)
if newpreset is None:
self.panel.presetsEvent()
return
self.setOrder()
self.panel.setParameters(newpreset)
self.logger.info('Preset from instrument: %s' % (newname,))
self.panel.presetsEvent()

def selectPreset(self, pname):
self.currentselection = self.presetByName(pname)
self.panel.setParameters(self.currentselection)
self.displayCalibrations(self.currentselection)

def getHighTension(self):
try:
return self.instrument.tem.HighTension
except:
return None

def displayCalibrations(self, preset):
mag = preset['magnification']
ht = self.getHighTension()
tem = preset['tem']
cam = preset['ccdcamera']
probe = preset['probe mode']

## not dependent on HT
ptime = str(self.calclients['pixel size'].time(tem, cam, mag))
modtimex = self.calclients['modeled stage'].timeModelCalibration(tem, cam, 'x')
modtimey = self.calclients['modeled stage'].timeModelCalibration(tem, cam, 'y')
modtime = 'x: %s, y: %s' % (modtimex, modtimey)

# dependent on HT
if ht is None:
message = 'Unknown (cannot get current high tension)'
modmagtime = beamtime = imagetime = stagetime = defocustime = message
else:
stagetime = self.calclients['stage'].time(tem, cam, ht, mag, 'stage position')
imagetime = self.calclients['image'].time(tem, cam, ht, mag, 'image shift')
beamtime = self.calclients['beam'].time(tem, cam, ht, mag, 'beam shift')
defocustime = self.calclients['beam tilt'].time(tem, cam, ht, mag, 'defocus',probe)
modmagtimex = self.calclients['modeled stage'].timeMagCalibration(tem, cam, ht,
mag, 'x')
modmagtimey = self.calclients['modeled stage'].timeMagCalibration(tem, cam, ht,
mag, 'y')
modmagtime = 'x: %s, y: %s' % (modmagtimex, modmagtimey)

times = {
'pixel size': str(ptime),
'image shift': str(imagetime),
'stage': str(stagetime),
'beam': str(beamtime),
'modeled stage': str(modtime),
'modeled stage mag only': str(modmagtime),
'defocus': str(defocustime),
}

self.panel.setCalibrations(times)

def renewPreset(self, p):
### this makes a copy of an existing preset
### so that dbid is not set and we can
### edit the values
if p.dbid is None:
return p
newpreset = leginondata.PresetData(initializer=p)
self.presets[newpreset['name']] = newpreset
if self.currentpreset is p:
self.currentpreset = newpreset
if self.currentselection is p:
self.currentselection = newpreset
return newpreset

def newPreset(self, presetname, newparams):
if self.presetsclient.isPresetNameToAvoid(presetname):
self.logger.error('Preset name not allowed. Try again')
return
newpreset = leginondata.PresetData()
newpreset['session'] = self.session
newpreset['name'] = presetname
newpreset['number'] = len(self.presets)
newpreset['removed'] = False
newpreset['film'] = False
newpreset['hasref'] = False
newpreset['pre exposure'] = 0.0
newpreset['skip'] = False
newpreset['alt channel'] = False
newpreset.friendly_update(newparams)
self.presets[presetname] = newpreset
self.presetToDB(newpreset)
self.currentpreset = newpreset
self.currentselection = newpreset
self.panel.setOrder(self.presets.keys())
self.panel.setParameters(newpreset)
return newpreset

def updatePreset(self, presetname, newparams, updatedose=True):
'''
called to change some parameters of an existing preset
'''
oldpreset = self.presetByName(presetname)
if oldpreset is None:
return
newpreset = self.renewPreset(oldpreset)
if 'tem' in newparams:
if isinstance(newparams['tem'], basestring):
try:
newparams['tem'] = self.instrument.getTEMData(newparams['tem'])
except:
newparams['tem'] = oldpreset['tem']
if 'ccdcamera' in newparams:
if isinstance(newparams['ccdcamera'], basestring):
try:
newparams['ccdcamera'] = self.instrument.getCCDCameraData(newparams['ccdcamera'])
except:
newparams['ccdcamera'] = oldpreset['ccdcamera']
newpreset.friendly_update(newparams)

### change dose if neccessary
if updatedose:
self.updateDose(oldpreset, newpreset)

## if this preset selected, refresh display
if self.currentselection and self.currentselection['name'] == newpreset['name']:
self.selectPreset(newpreset['name'])
self.presetToDB(newpreset)
return newpreset

def acquireDoseImage(self, presetname, display=True):
errstr = 'Acquire dose image failed: %s'

if not presetname:
e = 'invalid preset \'%s\'' % presetname
self.logger.error(errstr % e)
self.panel.presetsEvent()
return

if self.currentpreset is None or self.currentpreset['name'] != presetname:
self._cycleToScope(presetname)

if self.currentpreset is None or self.currentpreset['name'] != presetname:
e = 'cannot go to preset \'%s\'' % presetname
self.logger.error(errstr % e)
self.panel.presetsEvent()
return

self._acquireDoseImage(preset=self.currentpreset, display=display)

self.panel.presetsEvent()

self.outputEvent(event.DoseMeasuredEvent(name=presetname, preset=self.currentpreset))

def _acquireDoseImage(self, preset, display=True):
acquirestr = 'dose'
binning = preset['binning']
smallsize = self.settings['smallsize']
imagedata = self._acquireSpecialImage(preset, acquirestr, mode='center', imagelength=smallsize, binning=binning)
self.imagedata = imagedata
if imagedata is None:
return
## display
try:
dose = self.dosecal.dose_from_imagedata(imagedata)
pixel_framedose = self.dosecal.pixel_framedose_from_imagedata(imagedata)
except calibrationclient.NoPixelSizeError:
self.logger.error('No pixel size for this magnification')
return
except calibrationclient.NoSensitivityError:
self.logger.error('No sensitivity data for this magnification')
return
if dose is None:
self.logger.error('Invalid dose measurement result')
else:
if display:
self.panel.setDoseValue([dose,pixel_framedose])
self.setImage(imagedata['image'])
else:
self.saveDose(dose, self.currentpreset['name'])

def setDoseImageFilename(self, imagedata):
sessionname = self.session['name']
pname = imagedata['preset']['name']
smallsize = self.settings['smallsize']
timestamp = time.strftime('%d%H%M%S', time.localtime())
nextid = idcounter.next()
f = '%s_dose_%s_%s_%s_%s' % (sessionname, pname, smallsize, timestamp, nextid)
imagedata['filename'] = f

def saveDose(self, dose, presetname):
## store the dose in the current preset
params = {'dose': dose}
self.updatePreset(presetname, params)
self.old_time = None
self.saveDoseImage(presetname)

def saveDoseImage(self, presetname):
doseimage = leginondata.DoseImageData(initializer=self.imagedata)
doseimage['preset'] = self.presets[presetname]
self.setDoseImageFilename(doseimage)
doseimage.insert(force=True)

def matchDose(self,presetname,dose_to_match,old_dose):
preset = self.presetByName(presetname)
if old_dose is None:
return
camdata0 = leginondata.CameraEMData()
camdata0.friendly_update(preset)
old_time = camdata0['exposure time']
self.old_time = old_time
new_time = old_time * dose_to_match / old_dose
if new_time > 5000.0 or new_time <= 1.0:
self.logger.warning('Ignore unreasonable exposure time at %.1f ms' % float(new_time))
new_time = old_time

params = {'exposure time': new_time}
self.updatePreset(presetname, params)
self.acquireDoseImage(presetname)

def cancelDoseMeasure(self,presetname):
if self.old_time is None:
return
# revert matchDose result that was saved to the preset
preset = self.presetByName(presetname)
camdata0 = leginondata.CameraEMData()
camdata0.friendly_update(preset)
presettime = camdata0['exposure time']
if int(self.old_time) != presettime:
params = {'exposure time': self.old_time}
self.updatePreset(presetname,params)
self.old_time = None

def updateDose(self, oldpreset, newpreset):
'''
call this when preset params changed so that dose can
be scaled, mirrored, or reset as neccessary
This means:
If no existing dose:
do nothing
If mag, spot size, or intensity changed:
reset dose = None
If exposure time changed:
scale dose based on new exposure time
'''
## update dose when certain things change
if oldpreset['dose']:
dosekillers = []
for param in ('magnification', 'spot size', 'intensity'):
if oldpreset[param] != newpreset[param]:
dosekillers.append(param)
if dosekillers:
newpreset['dose'] = None
paramstr = ', '.join(dosekillers)
s = 'Dose of preset "%s" reset due to change in %s' % (newpreset['name'], paramstr)
self.logger.info(s)
elif newpreset['exposure time'] != oldpreset['exposure time']:
try:
scale = float(newpreset['exposure time']) / float(oldpreset['exposure time'])
except ZeroDivisionError:
scale = 0.0
newpreset['dose'] = scale * oldpreset['dose']
self.logger.info('Scaling dose of preset "%s" x%.3f due to change in exposure time' % (newpreset['name'], scale,))

## create list of similar presets
similarpresets = []
for pname, p in self.presets.items():
if pname == newpreset['name']:
continue
similar = True
# should also check lens mode (LM vs SA)...
for param in ('spot size', 'intensity'):
if p[param] != newpreset[param]:
similar = False
if similar:
similarpresets.append(p)

if similarpresets:
if not newpreset['dose']:
## set my dose from a similar preset
sim = similarpresets[0]
try:
scale = float(newpreset['exposure time']) / float(sim['exposure time'])
except ZeroDivisionError:
scale = 0.0
if sim['dose'] is None:
newpreset['dose'] = None
else:
newpreset['dose'] = scale * sim['dose']
self.logger.info('Copying and scaling dose from similar preset "%s" to preset "%s"' % (sim['name'], newpreset['name']))
elif oldpreset['dose'] != newpreset['dose']:
## my dose changed, now update dose in other similar presets
for sim in similarpresets:
try:
scale = float(sim['exposure time']) / float(newpreset['exposure time'])
except ZeroDivisionError:
scale = 0.0
self.logger.info('Copying and scaling dose from preset "%s" to similar preset "%s"' % (newpreset['name'], sim['name']))
simdose = scale * newpreset['dose']
self.updatePreset(sim['name'], {'dose': simdose}, updatedose=False)

def getSimilarLook(self,camname,magdict):
if len(magdict) != 2:
self.logger.warning('Error calculating similar look camera binning')
return None, None
keys = magdict.keys()
if magdict[keys[0]] >= magdict[keys[1]]:
highmag = magdict[keys[0]]
lowmag = magdict[keys[1]]
reverse = False
else:
highmag = magdict[keys[1]]
lowmag = magdict[keys[0]]
reverse = True
# This restrict the image to imagelength in x only
# and works only for camera dimension at power of 2
fullcamdim = min(self.instrument.camerasizes[camname]['x'],self.instrument.camerasizes[camname]['y'])
# smallsize may actually be too big for this camera
imagelength = min(self.settings['smallsize'], fullcamdim)

#assume highmag imag is binning of full camera to imagelength
maxbin = fullcamdim / imagelength
highbin = maxbin
highmaglook = float(highmag) / highbin
lowbin = int(math.pow(2,round(math.log(lowmag / highmaglook) / math.log(2))))
if lowbin < 1:
lowbin = 1
highbins = {'x':highbin,'y':highbin}
lowbins = {'x':lowbin,'y':lowbin}
if reverse == False:
return {keys[0]:highbins, keys[1]:lowbins},{keys[0]:'bin', keys[1]:'center'}
else:
return {keys[1]:highbins, keys[0]:lowbins},{keys[1]:'bin', keys[0]:'center'}
def acquireAlignImages(self, leftpreset=None, rightpreset=None):
if leftpreset is None or rightpreset is None:
leftpreset = self.alignpresets['left']
rightpreset = self.alignpresets['right']

refpreset = self.refpreset

errstr = 'Acquire align image failed: %s'
#self.alignpresets = [leftpreset, rightpreset, refpreset]
self.alignpresets = {'left': leftpreset, 'right': rightpreset, 'park':refpreset}
presetlabels = {leftpreset: 'left', rightpreset: 'right', refpreset:'park'}
# determine order to acquire images based on presets cycle
allpresetsorder = self.createCycleList(refpreset, refpreset, magshortcut=False)
notinpresetcycle = False
try:
leftindex = allpresetsorder.index(leftpreset)
rightindex = allpresetsorder.index(rightpreset)
except ValueError:
leftindex = -1
rightindex = -1
if leftindex < rightindex:
presetsorder = ['left', 'right', 'park']
else:
presetsorder = ['right', 'left', 'park']

acquirebin = {}
if self.panel.alignacquiremode == 'Similar look across mags':
alignmags = {}
for label in ['left','right']:
presetname = self.alignpresets[label]
preset = self.presetByName(presetname)
alignmags[label] = preset['magnification']
camname = preset['ccdcamera']['name']
acquirebin,acquiremode = self.getSimilarLook(camname,alignmags)
else:
acquiremode={'right':'bin','left':'bin'}
acquirebin['right'] = None
acquirebin['left'] = None
self.alignimages = {}
for label in presetsorder:
presetname = self.alignpresets[label]
if not presetname:
e = 'invalid preset \'%s\'' % presetname
self.logger.error(errstr % e)
self.panel.presetsEvent()
return

if self.currentpreset is None or self.currentpreset['name'] != presetname:
self._cycleToScope(presetname)

if self.currentpreset is None or self.currentpreset['name'] != presetname:
e = 'cannot go to preset \'%s\'' % presetname
self.logger.error(errstr % e)
self.panel.presetsEvent()
return
temp_mag = self.instrument.tem.Magnification
while temp_mag != self.currentpreset['magnification']:
time.sleep(1)
temp_mag = self.instrument.tem.Magnification
self.logger.warning('Preset change not completed. Wait...')
if label == 'park':
self.logger.info('Parked at preset "%s"' % (presetname,))
else:
imagedata = self._acquireAlignImage(self.currentpreset,
mode = acquiremode[label], binning = acquirebin[label])
self.alignimages[label] = imagedata
self.panel.setAlignImage(imagedata['image'], label)

self.panel.presetsEvent()

# self.outputEvent(event.AlignImagesAcquiredEvent())

def _acquireAlignImage(self, preset, mode='bin', binning=None):
acquirestr = 'align'
smallsize = self.settings['smallsize']
pause_time = self.settings['pause time']
if self.settings['add pause in alignment']:
self.logger.info('pausing for %s seconds before acquire' % (pause_time,))
time.sleep(pause_time)
return self._acquireSpecialImage(preset, acquirestr, mode=mode, imagelength=smallsize, binning=binning)

def modifyImageLength(self,fullcamdim,imagelength):
binning_values = [1,2,4,8]
for bin in binning_values:
minlength = min((fullcamdim['x']/bin,fullcamdim['y']/bin))
if minlength <= imagelength:
break
return minlength
def _acquireSpecialImage(self, preset, acquirestr='', mode='', imagelength=None, binning=None):
errstr = 'Acquire %s image failed: ' %(acquirestr) +'%s'
self.logger.info('Acquiring %s image' %(acquirestr))
camdata0 = leginondata.CameraEMData()
camdata0.friendly_update(preset)
## deactivate frame saving and align frame flags
camdata0['save frames'] = False
camdata0['align frames'] = False
## send preset binning to camera to get binned multiplier
self.instrument.ccdcamera.Binning = preset['binning']
camdata0['binned multiplier'] = self.instrument.ccdcamera.BinnedMultiplier

camdata1 = copy.copy(camdata0)
fullcamdim = self.instrument.camerasizes[camdata1['ccdcamera']['name']]


if mode == 'center':
imagelength = self.modifyImageLength(fullcamdim,imagelength)
## send new binning to camera to get binned multiplier
self.instrument.ccdcamera.Binning = binning
camdata1['binned multiplier'] = self.instrument.ccdcamera.BinnedMultiplier
## center of the preset without exposure adjustment
## figure out if we want to cut down to imagelength x imagelength
for axis in ('x','y'):
change = camdata0['dimension'][axis]*camdata0['binning'][axis] - imagelength*binning[axis]
if change > 0:
camdata1['dimension'][axis] = imagelength
camdata1['offset'][axis] = (camdata0['offset'][axis]*camdata0['binning'][axis]+(change / 2))/binning[axis]
camdata1['binning'][axis] = binning[axis]
camdata1['exposure time'] = camdata1['exposure time'] * (camdata0['binning']['x'] * camdata0['binning']['y'] / camdata0['binned multiplier'])
camdata1['exposure time'] = camdata1['exposure time'] / (camdata1['binning']['x'] * camdata1['binning']['y'] / camdata1['binned multiplier'])
if mode == 'bin':
## at maximum bin using as much camera as possible to at most imagelength x imagelength
## with exposure time adjustment
new_bin = max((fullcamdim['x']/imagelength,fullcamdim['y']/imagelength))

# keep new bin in the binning choices available to the camera
binnings = self.instrument.ccdcamera.CameraBinnings
if new_bin not in binnings:
binnings.append(new_bin)
binnings.sort()
bin_index= binnings.index(new_bin)
if bin_index == len(binnings) - 1:
# if the potential new bin is higher than any good ones, use the highest binning
new_bin = binnings[bin_index - 1]
else:
new_bin = binnings[bin_index + 1]
## need to delete the wrong one or it will add to the camera binnings in Camera config
del binnings[bin_index]
## send new binning to camera to get binned multiplier
self.instrument.ccdcamera.Binning = {'x': new_bin, 'y': new_bin}
camdata1['binned multiplier'] = self.instrument.ccdcamera.BinnedMultiplier

for axis in ('x','y'):
new_camdim = int(math.floor( fullcamdim[axis] / new_bin ))
extrabin = float(new_bin) / camdata0['binning'][axis]
camdata1['offset'][axis] = (fullcamdim[axis] / new_bin - new_camdim) / 2
camdata1['dimension'][axis] = new_camdim
camdata1['binning'][axis] = new_bin
camdata1['exposure time'] = camdata1['exposure time'] / extrabin
camdata1['exposure time'] = camdata1['exposure time'] * camdata1['binned multiplier'] / camdata0['binned multiplier']
if camdata1['exposure time']< 5.0:
camdata1['exposure time'] = 5.0

try:
self.instrument.setData(camdata1)
except:
self.logger.error(errstr % 'unable to set camera parameters')
return
try:
imagedata = self.acquireCorrectedCameraImageData()
except:
self.logger.error(errstr % 'unable to acquire corrected image data')
return
try:
self.instrument.setData(camdata0)
except:
estr = 'Return to orginial camera dimemsion failed: %s'
self.logger.error(estr % 'unable to set camera parameters')
return

self.logger.info('Returned to original camera dimensions')

if imagedata is None:
self.logger.error(errstr % 'unable to get corrected image')
return
return imagedata

def targetToScope(self, newpresetname, emtargetdata):
'''
This is like toScope, but this one is mainly called
by client nodes which request that presets and targets
be tightly coupled.
Stage position is always xy only
'''

## first cycle through presets before sending the final one
if self.currentpreset is None or self.currentpreset['name'] != newpresetname:
self.blankOn()
self._cycleToScope(newpresetname, dofinal=False)

self.logger.info('Going to target and to preset %s' % (newpresetname,))

newpreset = self.presetByName(newpresetname)
if newpreset is None:
msg = 'Preset change/target move failed: invalid preset name "%s"'
msg %= newpresetname
self.logger.error(msg)
raise PresetChangeError(msg)

## this is a hack to make this work with simulated targets which
## have no "old preset"
if emtargetdata['preset'] is None:
oldpreset = newpreset
else:
oldpreset = emtargetdata['preset']

## make copy of target stage and image shift
mystage = dict(emtargetdata['stage position'])
myimage = dict(emtargetdata['image shift'])
mybeam = dict(emtargetdata['beam shift'])

## This should be unnecessary if we have a check for minimum stage movement
## (currently in pyscope). It was a way to prevent moving the stage between
## targets which only require an image shift, but with queuing, we have to
## assume that stage should always be moved. If stage truly does not need to
## move, then the minimum stage movement threshold should take effect.
## decide if moving stage or not, and which axes to move
# movetype = emtargetdata['movetype']
# if movetype in ('image shift', 'image beam shift'):
# if not self.settings['stage always']:
# mystage = None

# 'xy only' settings is default to True and unexposed to user
# It means this always True
if mystage and self.settings['xy only']:
## only set stage x and y
for key in mystage.keys():
if key not in ('x','y'):
del mystage[key]
self.testprint('targetToScope used no z change')

## offset image shift to center stage tilt axis
if self.settings['apply offset']:
newimageshift = self.getOffsetImageShift(newpreset)
oldimageshift = self.getOffsetImageShift(oldpreset)
else:
newimageshift = newpreset['image shift']
oldimageshift = oldpreset['image shift']
# only transform imageshift if the movetype involves image shift so that we
# don't need to calibrate it when not needed
if emtargetdata['movetype'] == 'image beam shift' or emtargetdata['movetype'] == 'image shift':
## transform between microscope mode
fakescope1 = leginondata.ScopeEMData()
fakescope1.friendly_update(oldpreset)
fakecam1 = leginondata.CameraEMData()
fakecam1.friendly_update(oldpreset)
fakescope2 = leginondata.ScopeEMData()
fakescope2.friendly_update(newpreset)
fakecam2 = leginondata.CameraEMData()
fakecam2.friendly_update(newpreset)
new_tem = newpreset['tem']
new_ccdcamera = newpreset['ccdcamera']
old_tem = oldpreset['tem']
old_ccdcamera = oldpreset['ccdcamera']
ht = self.instrument.tem.HighTension
try:
pixelshift1 = self.calclients['image'].itransform(myimage, fakescope1, fakecam1)
pixrow = pixelshift1['row'] * oldpreset['binning']['y']
pixcol = pixelshift1['col'] * oldpreset['binning']['x']
pixvect1 = numpy.array((pixrow, pixcol))
###
if self.isLMtoSA(oldpreset['magnification'],newpreset['magnification']):
pixvect1 = self.specialTransform(pixvect1)
pixvect2 = self.calclients['image'].pixelToPixel(old_tem,old_ccdcamera,new_tem, new_ccdcamera, ht,oldpreset['magnification'],newpreset['magnification'],pixvect1)
pixelshift2 = {'row':pixvect2[0] / newpreset['binning']['y'],'col':pixvect2[1] / newpreset['binning']['x']}
newscope = self.calclients['image'].transform(pixelshift2, fakescope2, fakecam2)
myimage = newscope['image shift']
if emtargetdata['movetype'] == 'image beam shift':
beam_pixel_shift = {'row': -pixelshift2['row'], 'col': -pixelshift2['col']}
newscope = self.calclients['beam'].transform(beam_pixel_shift, fakescope2, fakecam2)
mybeam = newscope['beam shift']
except Exception, e:
self.logger.error('Image shift transform failed: %s' % (e,))
else:
## this assumes that image shift is preserved through a mag change
## although this may not always be true. In particular, I think
## that LM and M/SA mag ranges have different image shift coord systems
myimage['x'] -= oldimageshift['x']
myimage['y'] -= oldimageshift['y']
myimage['x'] += newimageshift['x']
myimage['y'] += newimageshift['y']
# Shouldn't have to make special case for non-beam shift, but for precaution
# of avoiding problem of random beam shift on 10apr29b, let's do this now.
if emtargetdata['movetype'] == 'beam shift':
mybeam['x'] -= oldpreset['beam shift']['x']
mybeam['x'] += newpreset['beam shift']['x']
mybeam['y'] -= oldpreset['beam shift']['y']
mybeam['y'] += newpreset['beam shift']['y']
elif emtargetdata['movetype'] == 'image beam shift':
pass
else:
mybeam['x'] = newpreset['beam shift']['x']
mybeam['y'] = newpreset['beam shift']['y']

mymin = newpreset['defocus range min']
mymax = newpreset['defocus range max']
if None in (mymin, mymax):
mydefocus = newpreset['defocus']
else:
# min <= defocus < max
mydefocus = random.uniform(mymin, mymax)
self.logger.info('Random defocus for preset %s: %s' % (newpreset['name'], mydefocus))


### create ScopeEMData with preset and target shift
scopedata = leginondata.ScopeEMData()
scopedata.friendly_update(newpreset)
scopedata['image shift'] = myimage
scopedata['beam shift'] = mybeam
scopedata['stage position'] = mystage
scopedata['defocus'] = mydefocus

### correct defocus for tilted stage
deltaz = emtargetdata['delta z']
if deltaz:
self.logger.info('Correcting defocus by %.2e for target on tilt' % (deltaz,))
scopedata['defocus'] += deltaz

### createCameraEMData with preset
cameradata = leginondata.CameraEMData()
cameradata.friendly_update(newpreset)

## set up instruments for this preset
try:
self.instrument.setTEM(newpreset['tem']['name'])
self.instrument.setCCDCamera(newpreset['ccdcamera']['name'])
except Exception, e:
msg = 'Preset change/target move failed: %s' % (e,)
self.logger.error(msg)
raise PresetChangeError(msg)

## send data to instruments
try:
self.instrument.setData(scopedata)
self.instrument.setData(cameradata)
newstage = self.instrument.tem.StagePosition
msg = '%s targetToScope %.6f' % (newpresetname,newstage['z'])
self.testprint('Presetmanager:' + msg)
self.logger.debug(msg)
except Exception, e:
self.logger.error(e)
message = 'Move to target failed: unable to set instrument'
self.logger.error(message)
raise PresetChangeError(message)

self.startTimer('preset pause')
self.logger.info('Pause for %.1f s' % (self.settings['pause time'],))
time.sleep(self.settings['pause time'])
self.stopTimer('preset pause')
name = newpreset['name']
self.currentpreset = newpreset
message = 'Preset (with target) changed to %s' % (name,)
self.logger.info(message)
self.blankOff()
self.outputEvent(event.PresetChangedEvent(name=name, preset=newpreset))

def getValue(self, instrument_type, instrument_name, parameter, event):
# HACK: fix me
try:
value = self._getValue(instrument_type, instrument_name, parameter)
self.last_value = value
finally:
event.set()

def _getValue(self, instrument_type, instrument_name, parameter):
try:
if instrument_type == 'tem':
return self.instrument.getTEMParameter(instrument_name, parameter)
elif instrument_type == 'ccdcamera':
if parameter == 'camera parameters':
return self.instrument.getData(leginondata.CameraEMData, ccdcameraname=instrument_name).toDict()
else:
return self.instrument.getCCDCameraParameter(instrument_name, parameter)
else:
raise ValueError('no instrument type \'%s\'' % instrument_type)
except Exception, e:
self.logger.error('Get value failed: %s' % (e,))
raise e

def onAlignImageClicked(self, label, xy):
p = self.alignpresets[label]
row = xy[1]
col = xy[0]
imagedata = self.alignimages[label]
acqimagedata = leginondata.AcquisitionImageData(initializer=imagedata)
acqimagedata['preset'] = self.presets[p]
target = leginondata.AcquisitionImageTargetData(image=acqimagedata)

dr = row - imagedata['image'].shape[0]/2 - 0.5
dc = col - imagedata['image'].shape[1]/2 - 0.5

target['delta row'] = dr
target['delta column'] = dc
if label == 'left':
movetype = 'stage position'
else:
movetype = 'image shift'
self.navclient.moveToTarget(target, movetype)
if label == 'right':
# save preset from the current microscope state if the right image is clicked
#
temp_mag = self.instrument.tem.Magnification
while temp_mag != self.currentpreset['magnification']:
time.sleep(1)
temp_mag = self.instrument.tem.Magnification
self.logger.warning('Preset change not completed. Wait...')
self.fromScope(p)
if self.panel.customalign == False:
self.updateSameMagPresets(p)
self.acquireAlignImages()

def unbinnedArea(self, presetname):
p = self.presets[presetname]
fov = p['binning']['x']*p['dimension']['x']*p['binning']['y']*p['dimension']['y']
return fov

def unbinnedAreaGreater(self, pname1, pname2):
if self.unbinnedArea(pname1) > self.unbinnedArea(pname2):
return -1
if self.unbinnedArea(pname1) < self.unbinnedArea(pname2):
return 1
return 0

def sortByUnbinnedArea(self, presetnames):
presetnames.sort(self.unbinnedAreaGreater)

def initAlignPresets(self, refname):
self.alignnext.clear()

self.logger.info('Aligning presets to reference: %s' % (refname,))
self.magpresets = {}
for p in self.presets.values():
if p['skip']:
continue
mag = p['magnification']
if mag in self.magpresets:
self.magpresets[mag].append(p['name'])
else:
self.magpresets[mag] = [p['name']]

## sort presets by largest field of view first
for presetnames in self.magpresets.values():
self.sortByUnbinnedArea(presetnames)

mags = self.magpresets.keys()
mags.sort()

if refname not in self.presets:
self.logger.warning('Overall reference preset not selected: Custom alignment assumed')
refname = self.magpresets[mags[-1]][0]
self.panel.customalign = True

refmag = self.presets[refname]['magnification']
self.refpreset = refname
try:
refindex = mags.index(refmag)
except ValueError:
self.logger.warning('Reference preset not in cycle List: Custom alignment assumed')
self.panel.customalign = True
if self.panel.customalign == True:
self.firstrightpreset = refname
self.panel.updatePresetLabels(refname,refname)
self.highmags = [refmag]
self.lowmags = []
return

self.highmags = mags[refindex+1:]
self.highmags.reverse()
self.lowmags = mags[:refindex]
self.logger.info('highmags: %s' % (self.highmags,))
self.logger.info('lowmags: %s' % (self.lowmags,))

if self.panel.customalign == False:
refishift = {'image shift': self.presets[refname]['image shift']}
for otherpreset in self.magpresets[refmag]:
if otherpreset == refname:
continue
self.logger.info('Updating image shift of %s to image shift of %s' % (otherpreset,refname))
self.updatePreset(otherpreset, refishift)

if self.highmags:
magright = self.highmags[-1]
elif self.lowmags:
magright = self.lowmags[-1]
else:
magright = None
return
presetsright = self.magpresets[magright]
presetright = presetsright[0]
self.firstrightpreset = presetsright[0]
self.panel.updatePresetLabels(self.refpreset, presetright)

def loopAlignPresets(self, refname):
self.initAlignPresets(refname)
self.alignclose = False
for i, maglist in enumerate([self.highmags, self.lowmags]):
presetleft = self.refpreset

while maglist:
magright = maglist[-1]
presetsright = self.magpresets[magright]
presetright = presetsright[0]

self.panel.updatePresetLabels(presetleft, presetright)

self.acquireAlignImages(presetleft, presetright)

if len(maglist) == 1 and i == 1:
# activate Done button if it is the last alignment
self.doneLastAlignPresets()
self.alignnext.wait()
# self.alignnext is clear if Continue is click before last alignment
# or if window is forced to close
self.alignnext.clear()
if len(maglist) == 1 or self.alignclose:
break
magleft = maglist.pop()
presetsleft = self.magpresets[magleft]
presetleft = presetsleft[0]
# If break from while loop is caused by closing the window.
# it should not continue to the next maglist
if self.alignclose:
break
self.doneAllAlignPresets()

def doneLastAlignPresets(self):
# This is called when last alignment Preset Labels
#are set and image acquired
self.panel.onDoneLastAlign()

def doneAllAlignPresets(self):
# THis is called when the dialog window is forced closed
# or when Done is clicked when all alignment is done
self.alignnext.set()
self.alignclose = True
self.panel.onDoneAllAlign()

def updateSameMagPresets(self, presetname,paramkey='image shift'):
newshift = {paramkey: self.presets[presetname][paramkey]}
mag = self.presets[presetname]['magnification']
for otherpreset in self.presets.keys():
if otherpreset == presetname:
continue
othermag = self.presets[otherpreset]['magnification']
if othermag == mag:
self.logger.info('Updating %s of %s to %s of %s' % (paramkey,otherpreset,paramkey,presetname))
self.updatePreset(otherpreset, newshift)

def onAlignNext(self):
self.alignnext.set()

def initBeamAdjust(self,selectedpreset):
# go to selected preset
if self.currentpreset is None:
preset = None
else:
preset = self.currentpreset['name']
if preset is None and selectedpreset is None:
return
if preset != selectedpreset:
self.logger.info('Send the selected preset to scope')
self._cycleToScope(selectedpreset)
self.getValidTempMag()

def getValidTempMag(self):
# go to selected preset
preset = self.currentpreset
if preset is None:
return
tem = preset['tem']
ccdcamera = preset['ccdcamera']
# calculate temporary mag
original_mag = preset['magnification']
temp_mag = original_mag / 5.0
allmags = self.instrument.tem.Magnifications
## find mag in list that is just greater than calculated mag
for mag in allmags:
if mag >= temp_mag:
temp_mag = mag
break
# look for magnification with beam shift calibration. Start from temp_mag above to the lowest, and the go down from preset mag
# This may not work between HM and LM mode
startindex = allmags.index(temp_mag)
while True:
try:
self.calclients['beam'].researchMatrix(tem, ccdcamera, 'beam shift', self.instrument.tem.HighTension, temp_mag)
except:
index = allmags.index(temp_mag)
if index < 1:
temp_mag = preset['magnification']
elif index == startindex + 1 or startindex == 0:
self.panel.disableBeamAdjust(preset['magnification'])
self.temp_mag = None
return
else:
temp_mag = allmags[index-1]
else:
beamshift = self.instrument.tem.BeamShift
self.panel.enableBeamAdjust(temp_mag,beamshift)
break
self.temp_mag = temp_mag

def acquireBeamImage(self):
# go to selected preset
preset = self.currentpreset
if preset is None:
return
temp_mag = self.temp_mag
if temp_mag is None:
return

## deactivate frame saving and align frame flags
try:
self.instrument.ccdcamera.SaveRawFrames = False
self.instrument.ccdcamera.AlignFrames = False
except:
# camera without frame saving capacity will give an attribut error
pass
# set preset binning so we can get binned multiplier
self.instrument.ccdcamera.Binning = preset['binning']
orig_mult = self.instrument.ccdcamera.BinnedMultiplier

# go to temporary mag
original_mag = preset['magnification']
self.instrument.tem.Magnification = temp_mag
time.sleep(self.settings['pause time'])
self.logger.info('Temporary mag: %d' % (temp_mag,))
# temp binning,dimension
fullcamdim = self.instrument.ccdcamera.CameraSize['x']
orig_dim = preset['dimension']['x']
orig_bin = preset['binning']['x']
temp_bin = 4
temp_dim = fullcamdim / temp_bin

self.instrument.ccdcamera.Dimension = {'x':temp_dim, 'y':temp_dim}
self.instrument.ccdcamera.Binning = {'x': temp_bin, 'y': temp_bin}
self.instrument.ccdcamera.Offset = {'x': 0, 'y': 0}
self.logger.info('Temporary Dimension, Binning: %d, %d' % (temp_dim, temp_bin,))
temp_mult = self.instrument.ccdcamera.BinnedMultiplier
# calculate temporary exposure time
orig_unbinned_dim = orig_bin * orig_dim
orig_zoom = fullcamdim / orig_unbinned_dim
expscale = (temp_mag / float(original_mag)) ** 2
binscale = (orig_bin / float(temp_bin)/orig_zoom) ** 2
binscale = binscale * temp_mult / orig_mult ## Is this right?
original_exptime = preset['exposure time']
temp_exptime = int(expscale * binscale * original_exptime)
if temp_exptime < 5:
temp_exptime = 5
self.instrument.ccdcamera.ExposureTime = temp_exptime
self.logger.info('Beam image using temporary exposure time: %.1f' % (float(temp_exptime),))
# acquire image
self.beamimagedata = self.acquireCorrectedCameraImageData()
im = self.beamimagedata['image']
self.panel.setBeamImage(im)
# display info
beamshift = self.instrument.tem.BeamShift
self.panel.displayBeamShift(beamshift)
# return to original preset
self.toScope(preset['name'], final=True)

def autoBeamCenter(self, cycle):
im = self.beamimagedata['image']
center, diameter = self.findBeamCenterDiameter(im)
scope = self.beamimagedata['scope']
tem = scope['tem']
cam = self.beamimagedata['camera']
ccdcamera = cam['ccdcamera']
pixelsize = self.calclients['pixel size'].retrievePixelSize(tem,ccdcamera,scope['magnification'])
diameter_micron = 1e6 * diameter * pixelsize * cam['binning']['x']
xy = center[1], center[0]
self.onBeamImageClicked(xy, cycle)

def findBeamCenterDiameter(self, im):
stats = arraystats.all(im)
thresh = (stats['max'] + stats['min']) / 2
mask = imagefun.threshold(im, thresh)
maxsize = 0.9 * im.shape[0]*im.shape[1]
blobs = imagefun.find_blobs(im, mask, maxblobs=1, minblobsize=100, maxblobsize=maxsize)
if not blobs:
return None
blob = blobs[0]
center = blob.stats['center']
area = blob.stats['n']
diameter = 2 * math.sqrt(area/3.14159)
return center, diameter

def onBeamImageClicked(self, xy, cycle):
row = xy[1]
col = xy[0]
imagedata = self.beamimagedata
acqimagedata = leginondata.AcquisitionImageData(initializer=imagedata)
fakepreset = leginondata.PresetData(initializer=self.currentpreset)
fakepreset['magnification'] = imagedata['scope']['magnification']
acqimagedata['preset'] = fakepreset
target = leginondata.AcquisitionImageTargetData(image=acqimagedata)

dr = row - imagedata['image'].shape[0]/2 - 0.5
dc = col - imagedata['image'].shape[1]/2 - 0.5

target['delta row'] = dr
target['delta column'] = dc
movetype = 'beam shift'
self.navclient.moveToTarget(target, movetype)

### get new beam shift here and apply to whatever
self.new_beamshift = self.instrument.tem.BeamShift

if cycle:
preset = self.currentpreset['name']
self._cycleToScope(preset)
self.instrument.tem.BeamShift = self.new_beamshift

self.acquireBeamImage()

def commitBeamAdjustment(self):
if self.new_beamshift is None:
return
presetname = self.currentpreset['name']
newbeamshift = {'beam shift': self.new_beamshift}
newpreset = self.updatePreset(presetname, newbeamshift)
self.updateSameMagPresets(presetname,'beam shift')

def handleUpdatePresetEvent(self, evt):
presetname = evt['name']
params = evt['params']
node = evt['node']
self.logger.info('%s requested update to %s' % (node, presetname))
self.updatePreset(presetname, params)
self.logger.info('completed update to %s' % (presetname,))
self.confirmEvent(evt)

def isLMtoSA(self,mag1,mag2):
return (mag1 <=350 and mag2 >=4700)

def specialTransform(self,pixelvect):
# This matrix gives a 90 degree rotation from +x to +y axis.
m = numpy.matrix([[0,1],[-1,0]])
rotated_vect = numpy.dot(pixelvect,numpy.asarray(m))
self.logger.info('rotate %s to %s') % (pixelvect, rotated_vect)
return rotated_vect
    (1-1/1)