Tunable Laser Command Structure

Overview

The module uses a 2-wire (+GND) serial connection based on a handshake model. The host initiates the communication with a 32-bit (4 bytes) command over the Rx line and receives 4 bytes back from the module over the Tx line.

Each interaction needs to complete these 8 byte sequence before starting a new interaction. If, for some reason, the sequence is not completed a communication reset is required.

Each 32 bit command contains management information in the first byte. Register information in the second byte and data in the third and fourth byte.

The serial communication configuration is 8 bit, no parity, 1 stop bit (high), 1 start bit (low), no echo, no flow control. The lowest significant bit is transmitted first (within each byte).

On this page

Communication structure

The packet of 32 bits to be sent to the module is configured as below.

Host 2 Module Packet Configuration

The more detailed breakdown is in the below image

Host 2 Module Packet Configuration

The first byte contains the checksum (first 4 bits) and a toggle between read and write operation. The second byte contains the register address and the third and fourth byte contains the data to be written (if appropriate toggle is set) to the register.

The packet of 32 bits to be received from the module is configured as below:

Module to host packet configuration

The more detailed breakdown is in the below image

Module to host packet configuration

The first byte contains the checksum (first 4 bits) and status information. The second byte contains the register address and the third and fourth byte contains the data read.

In case of a loss of synchronised communication, the host can send a sequence of individual 0 bytes (0x00). When the module responds with a 4 byte sequence, synchronisation is realized. If more than 4 bytes are sent, without a response, then a fatal error has occurred (possibly due to communicating at the wrong baud rate) and a device reset is needed.

The BIP-4 checksum is computed by xor’ing all the bytes in the packet together and then xor’ing the left nibble of the result with the right nibble of the result.

The checksum provides a basic level of consistency check for the communications transfer.

BIP8= (byte0 & 0x0f) ^ byte2 ^ byte3 ^ byte4

BIP4= ((BIP8 & 0xf0) >> 4) ^ (BIP8 & 0x0f)

For each command the module provides 2 bytes of return value. To output more information, such as multiple values or strings, the AEA mechanism is used. 

The register provides the number of bytes that should be downloaded and then the host can collect that number of bytes in increments of 2.

See the sample code at the bottom of the page for more details.

The host and module need to operate at the same baudrate to be able to communicate. The module is typically set to a baudrate of 9600, but it can be increased to 115200 baudrate. Also, this baudrate can be saved so that the module is in a different baudrate upon start-up.

Device Registers

Device Information (0x00 - 0x1F)

Read only

Provides error information and pending bits.

MRDY is always set to 1.

Read only / AEA register

Provides device type

Read only / AEA register

Provide manufacturer information

Read only / AEA register

Provides model information

Read only / AEA register

Provides serial number.

Read only / AEA register

Provides manufacturing date in DD-MMM-YYYY format.

Read only / AEA register

Provides release string, with information groups separated by semi-colon.  E.g. PV:2.0.0:FW 1.0.1:HW 3.2.1:AS A1;TS 030.033.0 with PV stands for protocol version, FW stands for firmware ID, HW stands for hardware version; AS stands for application space; TS stands for timestamp. The timestamp variable is most relevant for determining the firmware version.

Read only / AEA register

Provides release backwards compatibility string, with information groups separated by semi-colon, similar to the register 0x06. Note that this is not maintained by Pure Photonics. 

Write only

Saves the non-volatile parameters to permanent memory. Only allowed when the laser is not operating.

Read only

Provides configuration for AEA reading

Read only

Provides location where AEA data can be downloaded

Read only

Provides next 2 bytes for AEA read

Read / write

Provides the current and maximum baudrate for the module. Allows setting of the baudrate. After changing the baudrate of the module the module return packet is at the old baudrate. After that the baudrate of the host communication interface also needs to change accordingly.

For the baudrate field: 
    0: 9600 baud
    1: 19200 baud
    2: 38400 baud
    3: 57600 baud
    4: 115200 baud

The RMS bit controls the response to a change in the MS line. If set to 0 the baudrate will reset as well as clearing of the communication buffers. If set to 1 only the communication buffers are cleared.

Read/write

Provides ability to upload a new firmware version. Details to be found in the MSA.

Read only

Provides ability to upload a new firmware version. Details to be found in the MSA.

Write only

Provides ability to upload a new firmware version. Details to be found in the MSA.

Read only

Resends the last response packet

Read / write

Provides ability to upload a new firmware version. Details to be found in the MSA.

Read only

Provides status of firmware download. Details to be found in the MSA.

Device Health (0x20 - 0x2F, 0x63 - 0x64)

Read / write

Provides status of fatal flags and allows for clearing of latching flags.

Further detail

Read / write

Provides status of warning flags and allows for clearing of latching flags.

Detailed description

Read / write

Provides the power error in 0.01dB where the Fatal alarm is raised. Allows writing of the power error criterion in 0.01dB units.

Read / write

Provides the power error in 0.01dB where the Warning alarm is raised. Allows writing of the power error criterion in 0.01dB units.

Read / write

Provides the frequency error in 0.1GHz where the Fatal alarm is raised. Allows writing of the frequency error criterion in 0.1GHz

Read / write

Provides the frequency error in MHz where the Fatal alarm is raised. Allows writing of the frequency error criterion in 1MHz

Read / write

Provides the frequency error in 0.1GHz where the Warning alarm is raised. Allows writing of the frequency error criterion in 0.1GHz

Read / write

Provides the frequency error in MHz where the Warning alarm is raised. Allows writing of the frequency error criterion in 1MHz

Read / write

Provides the temperature error in 0.01C where the Fatal alarm is raised. Allows writing of the temperature error criterion in 0.01C

Read / write

Provides the temperature error in 0.01C where the Warning alarm is raised. Allows writing of the temperature error criterion in 0.01C

Read / write

Provides the elements in the status registers that trigger the SRQ line. Allows writing the triggers.

Read / write

Provides the elements in the status registers that trigger the FATAL flag Allows writing the triggers.

Read / write

Provides the elements in the status registers that trigger the alarm flag. Allows writing the triggers.

Device Settings (0x30 - 0x3F, 0x65 - 0x67)

Read / write

Provides the channel information and allows to select the channel. 

Note that this feature is mostly relevant for a device operating on a preset grid. The Pure Photonics tunable laser is grid agnostic and can be set to any frequency. Therefore we recommend to set the channel to 1 and set the First Channel Frequency fields to the desired frequency. 

Read / write

Provides the channel information and allows to select the channel. 

Note that this feature is mostly relevant for a device operating on a preset grid. The Pure Photonics tunable laser is grid agnostic and can be set to any frequency. Therefore we recommend to set the channel to 1 and set the First Channel Frequency fields to the desired frequency. 

Since MSA1.3 this is the upper nibble of the 32 bit channel number.

Read / write

Provides optical power setting in 0.01dB units. Allows setting of the target power.

Read / write

Provides on/off status of device and allows to turn the device on/off as well as start resets.

SENA is to activate the optical output
MR is module reset
SR is soft reset

Read / write

Provides the behavior of the module and allows to change the behavior of the module.

Read / write

Provides the 0.1GHz portion of the grid spacing and allow to set it. 

Read / write

Provides the 1MHz portion of the grid spacing and allow to set it. 

Read / write

Provides 1 THz portion of the frequency associated with channel 1. Allows to set it.

Read / write

Provides 0.1GHz portion of the frequency associated with channel 1. Allows to set it.

Read / write

Provides 1MHz portion of the frequency associated with channel 1. Allows to set it.

Read / write

Provides the in-operation frequency offset (Fine Tune Frequency). Allows for setting the frequency offset in units of MHz.

Read / write

Provides the low noise mode in operation and allows for setting the mode. 0 for dither mode; 2 for whispermode; (on PPCL700) 6 for enhanced whispermode.

Device Operating Information (0x40 - 0x43, 0x57 - 0x61, 0x68)

Read only

Provides operating frequency of the laser (including FTF offsets). THz part.

Read only

Provides operating frequency of the laser (including FTF offsets). 0.1GHz part.

Read only

Provides operating frequency of the laser (including FTF offsets). MHz part.

Read only

Provides laser output power in units of 0.01dBm

Read only 

Provices laser temperature in 0.01C (should always be 50C for PPCL600/PPCL700 laser generation)

Read only / AEA register

Provides laser current data in units of 0.1mA. First value is TEC current. Second value is gain current.

Read only / AEA register

Provides laser temperature data in units of 0.01C. First value is laser temperature. Second value is ambient temperature.

Not used by Pure Photonics modules.

Not used by Pure Photonics modules.

Not used by Pure Photonics modules.

Not used by Pure Photonics modules.

Not used by Pure Photonics modules.

Not used by Pure Photonics modules.

Read / write

Provides age threshold for fatal flag in %. Allows setting the criterion.

Read / write

Provides age threshold for warning flag in %. Allows setting the criterion.

Read only

Provides laser aging value

Device Capabilities (0x4F - 0x55, 0x69 - 0x6B)

Read only

Provides FTF capabiliy in MHz

Read only

Provides minimum power value in 0.01dBm units

Read only

Provides maximum power value in 0.01dBm units

Read only

Provides lower frequency level. THz part

Read only

Provides lower frequency level. 0.1GHz part

Read only

Provides lower frequency level. MHz part

Read only

Provides upper frequency. THz part

Read only

Provides upper frequency. 0.1GHz part

Read only

Provides upper frequency. MHz part

Read only

Provides minimum grid spacing. 0.1GHz part. Always 0 for Pure Photonics

Read only

Provides minimum grid spacing. MHz part. Always 1 for Pure Photonics

Clean Jump (0xD0 - 0xD2)

Read / Write

Provides the currently active channel and status of the Clean Jump. Allows to select the target channel and to enable/disable the Clean Jump.

Write 0 to disable Clean Jump. Write 1 to start Clean Jump. If value is higher than 1 the channel is set as value & 0x1f. So to set channel 0 and 1 write channel + 0x20.

Refer to Implementation Guide Clean Jump – PPCL600-700 vC

Read only

Provides frequency error estimate in units of 0.1GHz

Refer to Implementation Guide Clean Jump – PPCL600-700 vC

Read / write

Provides calibration status. The MSB (x8000) shows if the calibration is active. Value & 0x7fff shows the channel that currently is being calibrated. To start the calibration write the number of channels to calibrate. The unit will automatically start calibrating at the frequencies FCF + (channel-1) * Grid_spacing.

Refer to Implementation Guide Clean Jump – PPCL600-700 vC

Clean Sweep (0xE4 - 0xE7)

Read / write

Provides the Clean Sweep range in GHz. Allows writing the target clean sweep range to the module. To find out the device capabilities, incrementally write higher values to this register until an error is returned.

Read / write

Provides on / off status of Clean Sweep. Allows Clean Sweep to be started (1) and stopped (0)

Read only

Provides the Clean Sweep Offset in 0.1 GHz.

Read / write

Provides the speed of the Clean Sweep in MHz / sec. Allows to set the speed of the Clean Sweep in MHz /  sec.

Analog Inputs and Outputs (0x93 - 0x94, 0xF8)

Read only

Provides reading of the Clean Measurement input (ADC). 0 – 65535 for 0-2.5V. If data field is 0 CM1 is returned. If datafield is 1 CM2 is returned.

Read / write

Provides the current setting of Analog Output (DAC). 0 – 65535 for 0 – 2.5V. Allows for setting the output

Set FTF range for analog FTF. 

Input: Range in MHz. E.g. 500 is for -500 – + 500MHz analog FTF range

Output: Range in MHz

Description: default set to 0. Requires Clean Measurement (Analog input). If set to non-zero value, then the analog input voltage is measured every 5ms. The voltage is converted to an FTF setting. E.g. for PPCL700 the range 0-2.5V is converted to -RANGE – +RANGE MHz. For PPCL300 the range 0-6V is converted to -RANGE – +RANGE MHz.

Debug (0xFD)

Internal use only. 

Use of debug register may be communicated on an as-needed basis.

Python Sample Code

The below code can be used as a python module (e.g. import ITLA as it) to start and maintain communication with the ITLA. It contains the checksum calculation, package composition, package sending and error recovery.

				
					#example on how to use ITLA_reference.py
import ITLA_reference as itla

sercon=itla.ITLAConnect('com1',9600)
print('Serial connection %s' %sercon)
print('NOP %d; Flags %d' %(itla.ITLA(sercon,0x00,0,0),itla.ITLA(sercon,0x00,0,0)>>8))
print('Serial %s' %(itla.ITLA(sercon,0x04,0,0)))
print('Power setpoint %d *0.01dBm' %(itla.ITLA(sercon,0x31,0,0)))
itla.ITLA(sercon,0x31,itla.ITLA(sercon,0x31,0,0)-75,1)
print('New power setpoint %d * 0.01dBm' %(itla.ITLA(sercon,0x31,0,0)))
print('Laser temcperature %d * 0.01C' %(itla.ITLASplitDual(itla.ITLA(sercon,0x58,0,0),0)))
print('Ambient temcperature %d * 0.01C' %(itla.ITLASplitDual(itla.ITLA(sercon,0x58,0,0),1)))
sercon.close()

				
			
				
					#save as ITLA_reference.py to use with sample code
import serial
import time
import struct
import threading

ITLA_NOERROR=0x00
ITLA_EXERROR=0x01
ITLA_AEERROR=0x02
ITLA_CPERROR=0x03
ITLA_NRERROR=0x04
ITLA_CSERROR=0x05

ITLA_ERROR_SERPORT=0x01
ITLA_ERROR_SERBAUD=0x02

READ=0
WRITE=1

latestregister=0
tempport=0
raybin=0
queue=[]
maxrowticket=0
AEA_reference=[]

_error=ITLA_NOERROR
seriallock=0

def byteconv(number):
    #Converts a number to a byte for serial communications
    if number>127: number=number-256
    if number>127 or number <-128: return(struct.pack('b',0))
    return(struct.pack('b',number))
    
def ITLALastError():
    #returns the error status from the last communication
    return(_error)

def SerialLock():
    #returns status of serial communications
    global seriallock
    return seriallock

def SerialLockSet():
    #locks serial communications until a response is received (for multi-thread applications)
    global seriallock
    seriallock=1

def SerialLockUnSet():
    #releases serial communications until a response is received (for multi-thread applications)
    global seriallock,queue
    seriallock=0
    queue.pop(0)
    
def checksum(byte0,byte1,byte2,byte3):
    #calculates checksum
    bip8=(byte0&0x0f)^byte1^byte2^byte3
    bip4=((bip8&0xf0)>>4)^(bip8&0x0f)
    return bip4
    
def Send_command(sercon,byte0,byte1,byte2,byte3):
    #sends command on serial interface
    global CoBrite
    sercon.write(byteconv(byte0))
    sercon.write(byteconv(byte1))
    sercon.write(byteconv(byte2))
    #double check that the module has not sent any response after 3 bytes. If it did we are out of sync and we need to fix
    if sercon.inWaiting()>0:
        sercon.flushInput()
        counter=0
        while sercon.inWaiting()<4 and counter<8:
            sercon.write(byteconv(0)) #send 0 command (NOP)
            time.sleep(0.02)
            counter=counter+1
        if counter<8:     #if counter is 8 we have not recovered           
            sercon.flushInput()
            Send_command(sercon,byte0,byte1,byte2,byte3)
        return
    sercon.write(byteconv(byte3))

def Receive_response(sercon):
    #receive response on serial interface
    global _error,queue,CoBrite,CoBrite_AEA,commlog
    reftime=time.perf_counter()
    while sercon.inWaiting()<4:
        if time.perf_counter()>reftime+0.25: #timeout
            _error=ITLA_NRERROR
            return(0xFF,0xFF,0xFF,0xFF) #default response; indicates error
        time.sleep(0.001)
    try:
        byte0=ord(sercon.read(1))
        byte1=ord(sercon.read(1))
        byte2=ord(sercon.read(1))
        byte3=ord(sercon.read(1))
    except:        
        byte0=0xFF
        byte1=0xFF
        byte2=0xFF
        byte3=0xFF
    if checksum(byte0,byte1,byte2,byte3)==byte0>>4: #verify checksum
        _error=byte0&0x03
        return(byte0,byte1,byte2,byte3)
    else:
        _error=ITLA_CSERROR
        return(byte0,byte1,byte2,byte3)       

def ITLAConnect(port,baudrate=9600):
    #connects to the unit, checks for communication and returns handle to serial interface; alternatively user can make own serial connection
    try:
        conn = serial.Serial('\\\\.\\'+str(port),baudrate , timeout=1)
    except serial.SerialException:
        return(ITLA_ERROR_SERPORT)
    #try out different baudrates until it works
    baudrate2=4800
    while baudrate2<=115200:
        ITLA(conn,0x00,0,READ) #check if we get a response on NOP command
        if ITLALastError()!=ITLA_NOERROR:
            #go to next baudrate
            if baudrate2==4800:baudrate2=9600
            elif baudrate2==9600: baudrate2=19200
            elif baudrate2==19200: baudrate2=38400
            elif baudrate2==38400:baudrate2=57600
            elif baudrate2==57600:baudrate2=115200
            elif baudrate2==115200: #not workng 
                conn.close()
                return(ITLA_ERROR_SERBAUD)
            conn.close()
            conn = serial.Serial('\\\\.\\'+str(port),baudrate2 , timeout=1)
            teller3=0
            while teller3<5 and conn.inWaiting()<4:
                conn.write(chr(0).encode())
                time.sleep(0.01)
                teller3=teller3+1
            conn.read(conn.inWaiting())
        else:
            return(conn)
    conn.close()
    return(ITLA_ERROR_SERBAUD)

def ITLA(sercon,register,data,rw):
    #main routine to communicate with the unit
    global latestregister,commlog,AEA_reference,queue,maxrowticket
    lock=threading.Lock()
    lock.acquire() #execution halted while other thread is being queue'ed (allows for timeout)
    rowticket=maxrowticket+1
    starttime=time.perf_counter()
    maxrowticket=rowticket
    queue.append(rowticket)
    lock.release() #queue updated, now release for next thread
    while queue[0]!=rowticket: #only start communication if previous communication has finished
        if time.perf_counter()-starttime>5: #timeout
            teller=0
            while teller<len(queue):
                if queue[teller]==rowticket: queue.pop(teller) #cleanup
                else: teller=teller+1                       
            return 65535
    if data<0: data=data+65536 #convert signed number to non-signed integer
    if rw==READ:
        byte2=int(data/256)
        byte3=int(data-byte2*256)
        latestregister=register
        Send_command(sercon,int(checksum(0,register,byte2,byte3))*16+READ,register,byte2,byte3)
        test=Receive_response(sercon)
        if (test[0]&0x03)==ITLA_AEERROR: #if AEA response
            AEA_reference.append(test[0])
            AEA_reference.append(test[1])
            AEA_reference.append(test[2])
            AEA_reference.append(test[3]) 
            response=AEA(sercon,test[2]*256+test[3])
        else: response= test[2]*256+test[3]
    else:
        byte2=int(data/256)
        byte3=int(data-byte2*256)
        Send_command(sercon,int(checksum(1,register,byte2,byte3))*16+WRITE,register,byte2,byte3)
        test=Receive_response(sercon)
        response= test[2]*256+test[3]
    lock.acquire()
    queue.pop(0)
    lock.release()
    return(response)
         
def AEA(sercon,bytes):
    #read AEA string
    global AEA_reference
    outp=''
    if (bytes>100): #mostly to capture errors, e.g. where the response is 65535
        print('Excessive AEA number encountered')
        return(outp)
    while bytes>0:
        Send_command(sercon,int(checksum(0,0x0B,0,0))*16,0x0B,0,0)
        test=Receive_response(sercon)
        outp=outp+chr(test[2])
        if bytes>1:outp=outp+chr(test[3]) #to catch case of odd number of bytes
        bytes=bytes-2
    return outp

def ITLASplitDual(input,rank):
    #For currenst and temps registers sequences of 16 bit integers are output as AEA; allows to extract the desired element
    teller=rank*2
    try:
        return(ord(input[teller])*256+ord(input[teller+1]))
    except:
        return(0)