#! /usr/bin/env python
# -*- Encoding: utf-8 -*-

# Author: Laurent Le Guillou
# Intercalibration NIST/DKD


import sys, os, os.path
import subprocess
import getopt
import time
import numpy as np

import pyfits
import xmlrpclib
import struct

import dice.testbench.motor as dtm
import dice.testbench.multimeter.keithley6514 as multi
import dice.testbench.temperature.digisense as dttd
import dice.testbench.monochromator.sp_dk240 as mono

import dice.control.led.sndice_II_specs as specs

# following values defined for slits 600 microns - recalib 
setups = {
    1 : { 'grating' : 3,
          'dac' : 6000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 805.2,
          # 'lambda min': 700.,
          # 'lambda max': 870.,
          'lambda min': 790.,
          'lambda max': 820.,
          'step': 5. },
    2 : { 'grating' : 2,
          'dac' : 10000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 570.0,
          # 'lambda min': 500.,
          # 'lambda max': 640.,
          'lambda min': 550.,
          'lambda max': 585.,
          'step': 5. },
    3 : { 'grating' : 2,
          'dac' : 4000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 472.2,
          # 'lambda min': 400.,
          # 'lambda max': 580.,
          'lambda min': 465.,
          'lambda max': 500.,
          'step': 5. },
    4 : { 'grating' : 2,
          'dac' : 2000,
          'lambda peak' : 500.0,
          'lambda min': 400.,
          'lambda max': 600.,
          'range' : "2e-9",
          'step': 1. },
    5 : { 'grating' : 3,
          'dac' : 6000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 951.6,
          # 'lambda min': 840.,
          # 'lambda max': 1000.,
          'lambda min': 920.,
          'lambda max': 955.,
          'step': 5. },
    6 : { 'grating' : 3,
          'dac' : 14000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 721.8,
          # 'lambda min': 620.,
          # 'lambda max': 800.,
          'lambda min': 700.,
          'lambda max': 755.,
          'step': 5. },
    7 : { 'grating' : 2,
          'dac' : 8000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 535.8,
          # 'lambda min': 440.,
          # 'lambda max': 660.,
          'lambda min': 530.,
          'lambda max': 555.,
          'step': 5. },
    8 : { 'grating' : 3,
          'dac' : 12000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 880.0,
          # 'lambda min': 750.,
          # 'lambda max': 1010.,
          'lambda min': 880.,
          'lambda max': 900.,
          'step': 5. },
    9 : { 'grating' : 3,
          'NIST': {'x': 152.5,'y': 147.5},
          'dac' : 8000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 780.0,
          # 'lambda min': 680.,
          # 'lambda max': 870.,
          'lambda min': 750.,
          'lambda max': 795.,
          'step': 5. },
    10: { 'grating' : 1,
          'NIST': {'x': 152.5,'y': 147.5},
          'dac' : 6000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-9",
          'lambda peak' : 375,
          # 'lambda min': 335.,
          # 'lambda max': 415.,
          'lambda min': 345.,
          'lambda max': 395.,
          'step': 5. },
    11: { 'grating' : 3,
          'NIST': {'x': 152.5,'y': 147.5},
          'dac' : 3000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 831.6,
          # 'lambda min': 740.,
          # 'lambda max': 910.,
          'lambda min': 815.,
          'lambda max': 845.,
          'step': 5. },
    12: { 'grating' : 2,
          'dac' : 14000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 656.4,
          # 'lambda min': 580.,
          # 'lambda max': 720.,
          'lambda min': 640.,
          'lambda max': 675.,
          'step': 5. },
    13: { 'grating' : 2,
          'NIST': {'x': 152.5,'y': 147.5},
          'dac' : 4000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 526.8,
          # 'lambda min': 460.,
          # 'lambda max': 660.,
          'lambda min': 495.,
          'lambda max': 535.,
          'step': 5. },
    14: { 'grating' : 2,
          'dac' : 6000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-9",
          'lambda peak' : 407.8,
          # 'lambda min': 370.,
          # 'lambda max': 490.,
          'lambda min': 390.,
          'lambda max': 415.,
          'step': 5. },
    15: { 'grating' : 3,
          'dac' : 3000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 851.8,
          # 'lambda min': 750.,
          # 'lambda max': 920.,
          'lambda min': 840.,
          'lambda max': 890.,
          'step': 5. },
    16: { 'grating' : 2,
          'dac' : 14000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 422.2,
          # 'lambda min': 370.,
          # 'lambda max': 510.,
          'lambda min': 410.,
          'lambda max': 445.,
          'step': 5. },
    17: { 'grating' : 3,
          'dac' : 5000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 908.0,
          # 'lambda min': 800.,
          # 'lambda max': 1050.,
          'lambda min': 895.,
          'lambda max': 925.,
          'step': 5. },
    18: { 'grating' : 2,
          'dac' : 6000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 633.6,
          # 'lambda min': 550.,
          # 'lambda max': 710.,
          'lambda min': 610.,
          'lambda max': 650.,
          'step': 5. },
    19: { 'grating' : 1,
          'dac' : 8000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-9",
          'lambda peak' : 365,
          # 'lambda min': 325.,
          # 'lambda max': 405.,
          'lambda min': 355.,
          'lambda max': 370.,
          'step': 5. },
    20: { 'grating' : 2,
          'dac' : 14000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-9",
          'lambda peak' : 594.0,
          # 'lambda min': 520.,
          # 'lambda max': 650.,
          'lambda min': 580.,
          'lambda max': 615.,
          'step': 5. },
    21: { 'grating' : 3,
          'dac' : 14000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 959.2,
          'lambda min': 950.,
          'lambda max': 980.,
          'step': 5. },
    22: { 'grating' : 2,
          'dac' : 14000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 688.2,
          # 'lambda min': 590.,
          # 'lambda max': 780.,
          'lambda min': 670.,
          'lambda max': 705.,
          'step': 5. },
    23: { 'grating' : 2,
          'dac' : 10000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 464.2,
          # 'lambda min': 400.,
          # 'lambda max': 580.,
          'lambda min': 440.,
          'lambda max': 470.,
          'step': 5. },
    24: { 'grating' : 1,
          'dac' : 6000,
          'dxled' : 0.0,
          'dyled' : 0.0,
          'range' : "2e-10",
          'lambda peak' : 342.3,
          # 'lambda min': 310.,
          # 'lambda max': 410.,
          'lambda min': 335.,
          'lambda max': 355.,
          'step': 5.}
    }



#     iled = dice_sample(led)
#     iphd = dice_sample(led+33)
#     temp24 = dice_sample(58)
#     tempbe = dice_sample(59)
#     vref = dice_sample(61)

# # Data NIST

# if (nist_on != None) and (nist_off != None):
#     hdu.header.update('NISTON',   nist_on.mean(),  "[A] NIST ON current - mean")
#     hdu.header.update('SNISTON',  nist_on.std(),   "[A] NIST ON current - sigma")
#     hdu.header.update('NNISTON',  len(nist_on),   "[number] NIST ON current - npoints")
#     hdu.header.update('NISTOFF',  nist_off.mean(), "[A] NIST OFF current - mean")
#     hdu.header.update('SNISTOFF', nist_off.std(),  "[A] NIST OFF current - sigma")
#     hdu.header.update('NNISTOFF',  len(nist_off),   "[number] NIST OFF current - npoints")


# --------------------------------------------------------------------------
#
# LEDHEAD : moving LED head to put each LED in front
# of the monochromator entrance slit

def ledhead_position(led):
    x_ref = 73. #mm
    y_ref = 86. #mm
    # DO NOT CHANGE THIS !
    x = x_ref + specs.LEDS[led]['x']
    y = y_ref + specs.LEDS[led]['y']
    return x, y

dicehead_host = "dicehead"
def dice_led_on_off(led, current, on):
    on_str = "OFF"
    if on:
        on_str = "ON"

    command = ( "dice-led-on-off --force --current %f  %d  %s" % 
                (current, led, on_str) )
    remote_command = "ssh %s %s" % (dicehead_host, command)
    proc = subprocess.Popen(command, shell=True,
                            stdout = subprocess.PIPE,
                            stderr = subprocess.PIPE)
    (out, err) = proc.communicate()
    # print err

def dice_sample(channel):
    command = ( "dice-sample %d" % channel )
    remote_command = "ssh %s %s" % (dicehead_host, command)
    proc = subprocess.Popen(command, shell=True,
                            stdout = subprocess.PIPE,
                            stderr = subprocess.PIPE)
    (out, err) = proc.communicate()
    parts = out.strip().split()
    print parts
    if len(parts) !=5 :
        raise IOError("Failed to run dice-sample")
    #
    # 1332329172.153316     63    4681    -26.865627    4047.965067
    t = float(parts[0])
    c = int(parts[1])
    n = int(parts[2])
    mean = float(parts[3])
    variance = float(parts[4])
    #
    return t, c, n, mean, variance


def dice_samples(f, channel):
    print >>f, \
        "# time  channel  n   mean   variance"

    #     iled = dice_sample(led)
    #     iphd = dice_sample(led+33)
    #     temp24 = dice_sample(58)
    #     tempbe = dice_sample(59)
    #     vref = dice_sample(61)

    # for c in [channel, channel+33, 58, 59, 61]:
    for c in [channel, 58, 59, 61]:
        t, c, n, mean, variance = dice_sample(c)
        print >>f, "# ", t, c, n, mean, variance



# --------------------------------------------------------------------------

def Keithley6514_initialize(K, Krange, samples):
    K.reset()
    K.write("CURR:RANG %s" % Krange) 
    K.write("FUNC 'CURR:DC'")
    K.write("SYST:ZCH OFF")
    K.write("TRIG:COUN %d" % samples)
    K.write(":SENS:CURR:NPLC 1") # 1 seems to be the default
    K.write(":DISP:ENAB OFF")

def Keithley6514_read(K, samples):
    time.sleep(1.0)
    K.write("READ?")
    data_str = K.read()
    parts = data_str.split(',')
    # if (len(parts) != 3*samples):
    #     print >>sys.stderr, "No data. stop."
    #     return None
    values_str = parts[0::3]
    values = np.array([float(v) for v in values_str])
    return values

# --------------------------------------------------------------------------

def Keithley6482_initialize(K, Krange, samples):
    K.reset()
    K.write("CURR:RANG %s" % Krange) 
    # K.write("FUNC 'CURR:DC'")
    # K.write("SYST:ZCH OFF")
    # K.write("TRIG:COUN %d" % samples)
    # K.write(":SENS:CURR:NPLC 1") # 1 seems to be the default
    # K.write(":DISP:ENAB OFF")

def Keithley6482_read(K, samples):
    result = []
    for i in xrange(samples):
        time.sleep(1.0)
        K.write("READ?")
        data_str = K.read()
        parts = data_str.split(',')
        # if (len(parts) != 3*samples):
        #     print >>sys.stderr, "No data. stop."
        #     return None
        values_str = parts[0::2]
        values = np.array([float(v) for v in values_str])
        result.append(list(values.flatten()))
    return np.array(result)

# --------------------------------------------------------------------------

blocks = 2

leds = [18,2,3,7,12,13,14,16,20,22,23,1,5,6,8,9,11,15,17,21,24,10,19]
# leds = [18]

onlypeak=True

# frac = 0.1
fracs = [0.01, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1.0]

for arg in sys.argv[1:]:
    if '=' not in arg:
        print >>sys.stderr, "error: invalid argument [%s]" % arg
        sys.exit(2)
    eqparts = arg.split('=')
    if len(eqparts) != 2:
        print >>sys.stderr, "error: invalid argument format [%s]" % arg
        sys.exit(3)
    
    param = eqparts[0].lower()
    if param not in ['leds','fracs','onlypeak','blocks']: 
        print >>sys.stderr, "error: invalid parameter [%s]" % arg
        sys.exit(4)

    exec(arg)

# --------------------------------------------------------------------------
# output data file

filename = "intercalib-NIST-DKD-%f.data" % time.time()
datafile = open(filename, 'w')

# --------------------------------------------------------------------------
# Initialisation du banc (moteurs)

B = dtm.Bench()
B.open()
B.setup()
z=-1590.0 # mm
B.sensor_move_absolute("Z", z, unit="mm")

print >>datafile, "#-----------------------------------------------------"
print >>datafile, "# Bench zsensor = ", z

# --------------------------------------------------------------------------
# Initialisation du Keithley - NIST

NIST = multi.Multimeter('/dev/ttyS3')
NIST.open()
NIST.write("*IDN?")
NIST_id = NIST.read()

NIST.reset()
NIST_range = '2e-10'
NIST_samples = 20
Keithley6514_initialize(NIST, NIST_range, NIST_samples)

# Position reference NIST
xnist =  152.5 # mm
ynist =  147.5 # mm

print >>datafile, "#-----------------------------------------------------"
print >>datafile, "# NIST multimeter = ", NIST_id
print >>datafile, "#    range =  ", NIST_range
print >>datafile, "#    samples =  ", NIST_samples
print >>datafile, "# NIST position = (", xnist, ", ", ynist, ")"


# --------------------------------------------------------------------------
# Initialisation du Keithley - DKD

DKD = multi.Multimeter("/dev/ttyUSB-pl2303-0")
DKD.open()
DKD.write("*IDN?")
DKD_id = DKD.read()

DKD.reset()
DKD_range = '2e-10'
DKD_samples = 10
Keithley6482_initialize(DKD, DKD_range, DKD_samples)

# Position reference DKD
xdkd =  xnist
ydkd =  ynist - 45.0 # mm

print >>datafile, "#-----------------------------------------------------"
print >>datafile, "# DKD multimeter = ", DKD_id
print >>datafile, "#    range =  ", DKD_range
print >>datafile, "#    samples =  ", DKD_samples
print >>datafile, "# DKD position = (", xdkd, ", ", ydkd, ")"



# --------- Init Monochromator SP DK240 ------------------------------------

M = mono.Monochromator(port = '/dev/ttyS4')
M.open()

#slit_width = 625 # microns
#slit_width = 200 # microns
#slit_width = 1000 # microns
slit_width = 625 # microns
# step = 1.0 # nm
# step = 5.0 # nm

# choose the slit width
in_width, out_width, other = M.get_slits()
if (in_width  != slit_width):
    M.set_entrance_slit(slit_width)
if (out_width != slit_width):
    M.set_exit_slit(slit_width)
in_width, out_width, other = M.get_slits()

print >>datafile, "#-----------------------------------------------------"
print >>datafile, "# Monochromator SP DK240 "
print >>datafile, "# Slit width = ", in_width, out_width

datafile.flush()

# --------- Init DigiSense Temperature monitor -----------------------------

T = dttd.Temperature("/dev/ttyS0")
T.open()

print >>datafile, "#-----------------------------------------------------"
print >>datafile, "# Temperature Monitor DIGISENSE "
print >>datafile, "# Ambient Temperature = ", T.measure()
print >>datafile, "#-----------------------------------------------------"

# ==========================================================================
# Boucle principale sur toutes les LEDs

for led in leds:
    print "========================================================="
    print "Acquisition for LED #", led

    # ----------------------------------------------------------------------
    # Placer la LED

    print "Putting led %d in place by moving the DICE LED head..." % led

    xled, yled = ledhead_position(led)
    B.source_move_absolute("X", xled, unit="mm")
    B.source_move_absolute("Y", yled, unit="mm")        

    xsource, ysource = B.source_position(unit="mm")

    print >>datafile, "#-----------------------------------------------------"
    print >>datafile, "# LED #", led
    print >>datafile, "#     xsource = ", xsource
    print >>datafile, "#     ysource = ", ysource

    # ----------------------------------------------------------------------
    # Choisir le réseau pour cette LED

    grating = setups[led]['grating']
    print "Selecting the grating %d for LED #%d..." % (grating, led)
    ng, g, density, blaze = M.get_grating()
    print ng, g, grating
    if grating != g:
        M.set_grating(grating)
    print "Done."

    print >>datafile, "#     grating = ", grating

    # ======================================================================
    # Boucle sur les niveaux de courant LED (fracs)

    for frac in fracs:
        
        for block in xrange(blocks):

            datafile.flush()

            print >>datafile, \
                "#-----------------------------------------------------"
            print >>datafile, "Block #", block
            print >>datafile, "# LED #", led
            print >>datafile, "#  current frac = ", frac

            print >>datafile, \
                "#-----------------------------------------------------"
            print >>datafile, "# Temperature Monitor DIGISENSE "
            print >>datafile, "# Ambient Temperature = ", T.measure()

            # ------------------------------------------------------------------
            # Extinction LED
            dice_led_on_off(led, 0, False)  # Just to be sure
            time.sleep(1)

            # Samplings LED
            print >>datafile, \
                "#-----------------------------------------------------"
            dice_samples(datafile, led)
            
            print >>datafile, \
                "#-----------------------------------------------------"
            print >>datafile, "#  Dark measurements"

            # ------------------------------------------------------------------
            # Mettre la NIST dans le faisceau (OFF)
            
            B.sensor_move_absolute("X", xnist, unit="mm")
            B.sensor_move_absolute("Y", ynist, unit="mm")
            xsensor, ysensor, zsensor = B.sensor_position(unit="mm")
            
            print >>datafile, "# NIST in beam (OFF)"
            print >>datafile, "#     xsensor = ", xsensor
            print >>datafile, "#     ysensor = ", ysensor
            print >>datafile, "#     zsensor = ", zsensor

            # Mesures NIST OFF
            
            time.sleep(5)
            nist_off = Keithley6514_read(NIST, NIST_samples)
            nist_off_mean = nist_off.mean()
            nist_off_std  = nist_off.std()
            print nist_off
            print "NIST current OFF = ", nist_off_mean, nist_off_std
            
            print >>datafile, \
                "# time   LED    frac   current(ADU)  NIST/DKD   ON/OFF  grating  lambda(nm)   current(A)"

            for value in nist_off:
                print >>datafile, \
                    time.time(), led, 0.0, 0.0, \
                    "NIST", 0, grating, "--", value

            datafile.flush()

            # ------------------------------------------------------------------
            # Mettre la DKD dans le faisceau (OFF)
            
            B.sensor_move_absolute("X", xdkd, unit="mm")
            B.sensor_move_absolute("Y", ydkd, unit="mm")
            xsensor, ysensor, zsensor = B.sensor_position(unit="mm")

            print >>datafile, "# DKD in beam (OFF)"
            print >>datafile, "#     xsensor = ", xsensor
            print >>datafile, "#     ysensor = ", ysensor
            print >>datafile, "#     zsensor = ", zsensor

            # Mesures DKD OFF

            time.sleep(5)
            dkd_off = Keithley6482_read(DKD, DKD_samples)
            dkd_off_mean = dkd_off.mean()
            dkd_off_std  = dkd_off.std()
            print dkd_off
            print "DKD current OFF = ", dkd_off_mean, dkd_off_std
            
            print >>datafile, \
                "# time   LED    frac   current(ADU)  NIST/DKD   ON/OFF  grating  lambda(nm)   current(A)"

            for value in dkd_off.flatten():
                print >>datafile, \
                    time.time(), led, 0.0, 0.0, \
                    "DKD", 0, grating, "--", value

            datafile.flush()

            # -----------------------------------------------------------
            # Allumer la LED (avec le niveau de courant demandé)

            current = frac*specs.LEDS[led]['dac']
            # if led in [10,16,19,24]: # very faint ones
            #     current *= 10

            dice_led_on_off(led, current, True) # turn on the selected LED
            print "Turn on LED #", led, "with current", current, "... (stabilisation 60s)"
            time.sleep(60)  # stabilisation time
            print "Done."

            # Samplings LED
            print >>datafile, \
                "#-----------------------------------------------------"
            dice_samples(datafile, led)

            print >>datafile, \
                "#-----------------------------------------------------"
            print >>datafile, "#  Turn on LED #", led
            print >>datafile, "#      frac = ", frac
            print >>datafile, "#      current = ", current

            # ------------------------------------------------------------------
            # Boucle sur la longueur d'onde pour une LED donnée 
            # (et un courant donné)

            # wl_min = setups[led]['lambda peak'] - 20.0 
            # wl_max = setups[led]['lambda peak'] + 20.0 
            wl_min = setups[led]['lambda min']
            wl_max = setups[led]['lambda max']
            step = setups[led]['step']
            waverange = np.arange(wl_min, wl_max + step, step)

            if onlypeak:
                waverange = np.array([ setups[led]['lambda peak'] ])

            # --------------------------------------------------------------
            # Mettre la NIST dans le faisceau (ON)

            print >>datafile, \
                "#-----------------------------------------------------"
            print >>datafile, \
                "# NIST Spectra on wavelength range ", waverange
        
            print "NIST in the beam..."
            B.sensor_move_absolute("X", xnist, unit="mm")
            B.sensor_move_absolute("Y", ynist, unit="mm")
            xsensor, ysensor, zsensor = B.sensor_position(unit="mm")
            
            print >>datafile, "# NIST in beam (ON)"
            print >>datafile, "#     xsensor = ", xsensor
            print >>datafile, "#     ysensor = ", ysensor
            print >>datafile, "#     zsensor = ", zsensor
        
            # ------------------------------------------------------------------
            # Start spectra avec la NIST
            print "Starting lambda scan... (NIST)"
            print >>datafile, \
                "# time   LED    frac   current(ADU)  NIST/DKD   ON/OFF  grating  lambda(nm)   current(A)"
            for wave in waverange:
                wave = float(wave) # for numpy strange types
                print "Setting the monochromator wavelength to ", wave
                w = M.get_wavelength()
                # if wave != w:
                if abs(wave - w) > 0.1:
                    M.set_wavelength(wave)
         
                time.sleep(5)
                nist_on = Keithley6514_read(NIST, NIST_samples)
                nist_on_mean = nist_on.mean()
                nist_on_std  = nist_on.std()
                print "NIST current ON = ", \
                    nist_on_mean, nist_on_std
                time.sleep(1)

                for value in nist_on:
                    print >>datafile, \
                        time.time(), led, frac, current, \
                        "NIST", 1, grating, w, value

                datafile.flush()

            # --------------------------------------------------------------
            # Mettre la DKD dans le faisceau (ON)
                
            print >>datafile, \
                "#-----------------------------------------------------"
            print >>datafile, \
                "# DKD Spectra on wavelength range ", waverange

            print "DKD in the beam..."
            B.sensor_move_absolute("X", xdkd, unit="mm")
            B.sensor_move_absolute("Y", ydkd, unit="mm")
            xsensor, ysensor, zsensor = B.sensor_position(unit="mm")

            print >>datafile, "# DKD in beam (ON)"
            print >>datafile, "#     xsensor = ", xsensor
            print >>datafile, "#     ysensor = ", ysensor
            print >>datafile, "#     zsensor = ", zsensor
        
            # ------------------------------------------------------------------
            # Start spectra avec la DKD
            print "Starting lambda scan... (DKD)"
            print >>datafile, \
                "# time   LED    frac   current(ADU)  NIST/DKD   ON/OFF  grating  lambda(nm)   current(A)"
            for wave in waverange:
                wave = float(wave) # for numpy strange types
                print "Setting the monochromator wavelength to ", wave
                w = M.get_wavelength()
                # if wave != w:
                if abs(wave - w) > 0.1:
                    M.set_wavelength(wave)
         
                time.sleep(5)
                dkd_on = Keithley6482_read(DKD, DKD_samples)
                dkd_on_mean = dkd_on.mean()
                dkd_on_std  = dkd_on.std()
                print "DKD current ON = ", \
                    dkd_on_mean, dkd_on_std
                time.sleep(1)

                for value in dkd_on.flatten():
                    print >>datafile, \
                        time.time(), led, frac, current, \
                        "DKD", 1, grating, w, value

                datafile.flush()

        # ======================================================================
        # Fin boucle sur les niveaux de courant LED (fracs)

        # Extinction LED 
        dice_led_on_off(led, 0, False)

    # fin boucle sur les blocks

# Fin boucle sur les LED

# ==========================================================================

# turn all led off (in case)
dice_led_on_off(1, 0, False)

