optics-rs 0.13.6

Rust port of EPICS synApps optics module
Documentation
# This database implements a generic transformation where motors aren't 
# necessarily orthogonal or parallel.  This is most useful for NSLS-II 
# style slits where a horiontal and diagonal stage are used to produce 
# x, y motions.
#
#
# Given two real motors $(P)$(M1DRV) and $(P)$(M1DRV), as (summed) 
# components of the two real motors.  The transformation is as follows:
# 
# HOR = (( $(P)$(M1DRV) * $(P)$(HOR)C1 ) + ( $(P)$(M2DRV) * $(P)$(HOR)C2 )) 
# VERT = (( $(P)$(M1DRV) * $(P)$(VERT)C1 ) + ( $(P)$(M2DRV) * $(P)$(VERT)C2 )) 
# 
# Where:
#	$(P)$(HOR)C1 = cos(theta_1)
#	$(P)$(HOR)C2 = cos(theta_2)
#	$(P)$(VERT)C1 = sin(theta_1)
#	$(P)$(VERT)C2 = sin(theta_2)
#	and
#	theta_1 = angle between motor 1 axis and x-axis
#	theta_2 = angle between motor 2 axis and x-axis
#
# These two records are where the soft motors write their output.
# They are needed because writing to a transform record field does not
# cause the record to process?
# We use forward links to the transform record (rather than making the INPA and
# INPB fields of the transform record CP) so that the scan record waits 
# for the motors to move.
grecord(ao,"$(P)$(HOR)DVAL") {
	field(FLNK, "$(P)$(T)Drive")
	field(PREC, "8")
}
grecord(ao,"$(P)$(VERT)DVAL") {
	field(FLNK, "$(P)$(T)Drive")
	field(PREC, "8")
}

# This record is processed whenever the soft motor records HOR or VERT write
# new values to the DVAL records above.
# Note: the INPA and INPB fields get their values directly from the .DVAL
# fields of the soft motor records, rather than from the DVAL record above.
# This is because the .DVAL fields are always correct, while the DVAL records
# do not get updated when the soft motor .DVAL fields are modified in SET mode.
grecord(transform,"$(P)$(T)Drive") {
	field(INPA,"$(P)$(HOR).DVAL NPP NMS")
	field(INPB,"$(P)$(VERT).DVAL NPP NMS")
	field(INPC,"$(P)$(HOR)C1.VAL NPP NMS")
	field(INPD,"$(P)$(HOR)C2.VAL NPP NMS")
	field(INPE,"$(P)$(VERT)C1.VAL NPP NMS")
	field(INPF,"$(P)$(VERT)C2.VAL NPP NMS") 
	field(CLCG,"(A*F-B*D)/(C*F-D*E)")
    field(OUTG,"$(P)$(M1DRV) PP MS")
	field(CLCH,"(A*E-B*C)/(D*E-C*F)")
    field(OUTH,"$(P)$(M2DRV) PP MS")
	field(PREC,"$(PREC)")
}

# This record calculates the readback positions of the soft motors.  It 
# processes whenever the readbacks of the real motors change, or whenever
# one of the the geometry constants changes.
grecord(transform,"$(P)$(T)Readback") {
	field(INPA,"$(P)$(M1RBV) CP MS")
	field(INPB,"$(P)$(M2RBV) CP MS")
	field(INPC,"$(P)$(HOR)C1.VAL CP NMS")
	field(INPD,"$(P)$(HOR)C2.VAL CP NMS")
	field(INPE,"$(P)$(VERT)C1.VAL CP NMS")
	field(INPF,"$(P)$(VERT)C2.VAL CP NMS") 
	field(CLCG,"A*C+B*D")
	field(CLCH,"A*E+B*F")
	field(IVLA,"Do Nothing")
	field(PREC,"$(PREC)")
}

# This record processes if either of the soft motor STOP fields is set.
grecord(dfanout,"$(P)$(T)Stop") {
        field(VAL,"1")
        field(OUTA,"$(P)$(M1STOP) PP MS")
        field(OUTB,"$(P)$(M2STOP) PP MS")
}

# This record computes whether the compound motor is done moving.  
# It goes to 1 when both real motors are done moving.
# positions of the real motors.
grecord(calcout,"$(P)$(T)Done") {
        field(INPA,"$(P)$(M1DONE) CP MS")
        field(INPB,"$(P)$(M2DONE) CP MS")
        field(CALC,"A & B")
}