text: # -*- coding: utf-8 -*-
  """
  	lantz.drivers.arduino.goniometer
  	~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  	Lantz interface to the arduino controlling the goniometer
  
  	Authors: Alexandre Bourassa
  	Date: 3/28/2017
  
  """
  
  import time
  
  from lantz.core import Action, Feat, MessageBasedDriver, Q_
  from lantz.core.errors import InstrumentError, LantzTimeoutError
  
  
  class Goniometer(MessageBasedDriver):
      comm_delay = 1
  
      DEFAULTS = {
          'ASRL': {
              'write_termination': '\n',
              'read_termination': '\r\n',
              'baud_rate': 9600,
              'timeout': 1600,
          }
      }
  
      errors = {
          -1: 'theta or phi were out of bounds',
          -2: 'alpha or beta were out of bounds',
          -3: 'attempted rotateTo while run_state != 0',
          -4: 'period is out of bound',
          -9: 'R is out of bounds',
          -10: 'Unknown cmd',
          -11: 'other error',
          -50: 'no error',
      }
  
      def initialize(self):
          super().initialize()
          time.sleep(1.6)
          self.query('stop')
  
      def check_error(self, err):
          if err != -50:
              raise InstrumentError(self.errors[err])
          else:
              return
  
      def query(self, cmd):
          self.write(cmd)
          ans = self.read()
          err = int(self.read())
          self.check_error(err)
          # time.sleep(self.comm_delay)
          return ans
  
      @Feat(limits=(70, 110, 0.01))
      def theta(self):
          return float(self.query('theta?'))
  
      @theta.setter
      def theta(self, val):
          return self.query('theta {}'.format(val))
  
      @Feat(limits=(25, 135, 0.01))
      def phi(self):
          return float(self.query('phi?'))
  
      @phi.setter
      def phi(self, val):
          return self.query('phi {}'.format(val))
  
      @Feat(units='mm', limits=(0., 100.))
      def R(self):
          return float(self.query('raxis?'))
  
      @R.setter
      def R(self, val):
          return self.query('rabs {}'.format(val))
  
      @Feat(values={'ready': '0', 'moving': '1'})
      def state(self):
          return self.query('state?')
  
      @Action()
      def stop(self):
          return self.query('stop')
  
      @Feat(units='us', limits=(0, 1000, 1))
      def period_alpha(self):
          return float(self.query('period? 0'))
  
      @period_alpha.setter
      def period_alpha(self, val):
          return self.query('pperiod 0 {}'.format(int(val)))
  
      @Feat(units='us', limits=(0, 1000, 1))
      def period_beta(self):
          return float(self.query('period? 1'))
  
      @period_beta.setter
      def period_beta(self, val):
          return self.query('pperiod 1 {}'.format(int(val)))
  
      @Feat(units='us', limits=(0, 1000, 1))
      def period_R(self):
          return float(self.query('period? 2'))
  
      @period_R.setter
      def period_R(self, val):
          return self.query('pperiod 2 {}'.format(int(val)))
  
      @Action()
      def wait_for_ready(self, max_iter=30):
          for i in range(max_iter):
              if self.state == 'ready':
                  return
              time.sleep(self.comm_delay)
          raise LantzTimeoutError('Could not get a ready state response...')
  
      @Action()
      def unsafe_R_relative_move(self, distance):
          if type(distance) == Q_:
              distance = distance.to('mm').m
          if abs(distance) >= 25:
              raise ValueError("Cannot do relative move of more then 25 mm")
          return self.query('rrel {}'.format(distance))
  
      # @Action()
      # def return_to_origin(self):
      # 	self.phi = 90
      # 	self.theta = 90
  
      @Action()
      def zero(self, axis):
          if not axis in [0, 1, 2]:
              raise ValueError('Axis must be 0, 1 or 2')
          else:
              return self.query('zero {}'.format(axis))
  
      @Feat(values={'on': '0', 'off': '1'})
      def emergency_switch(self):
          return self.query('switch?')
  
      @Action()
      def lock(self, state):
          if state:
              return self.query('lock')
          else:
              return self.query('unlock')
  