#!/usr/bin/env python
"""
.. module:: leicameasureunit.py
:platform: Unix, Windows
:synopsis: Ulyxes - an open source project to drive total stations and
publish observation results. GPL v2.0 license Copyright (C)
2010- Zoltan Siki <siki.zoltan@epito.bme.hu>
.. moduleauthor:: Zoltan Siki <siki.zoltan@epito.bme.hu>,
Daniel Moka <mokadaniel@citromail.hu>
"""
import re
import logging
from measureunit import MeasureUnit
from angle import Angle
[docs]class LeicaMeasureUnit(MeasureUnit):
""" This class contains the Leica robotic total station specific functions
common to all leica robot TS
:param name: name of ts (str), default 'Leica generic'
:param type: type of ts (str), default 'TPS'
"""
# Constants for message codes
codes = {
'SWITCHON': 111,
'SWITCHOFF': 112,
'SETPC': 2024,
'GETPC': 2023,
'INSTRNO': 5003,
'INSTRNAME': 5004,
'INTTEMP': 5011,
'SETATR': 9018,
'GETATR': 9019,
'SETLOCK': 9020,
'GETLOCK': 9021,
'LOCKIN': 9013,
'SETATMCORR': 2028,
'GETATMCORR': 2029,
'SETREFCORR': 2030,
'GETREFCORR': 2031,
'GETSTN': 2009,
'SETSTN': 2010,
'SETEDMMODE': 2020,
'GETEDMMODE': 2021,
'SETORI': 2113,
'MOVE': 9027,
'MEASURE': 2008,
'GETMEASURE': 2108,
'MEASUREANGDIST': 17017,
'COORDS': 2082,
'GETANGLES': 2003,
'CHANGEFACE': 9028,
'CLEARDIST': 2082,
'SETSEARCHAREA': 9043,
'POWERSEARCH': 9052,
'SEARCHNEXT': 9051,
'SETREDLASER': 1004,
'GETPT': 17009,
'SETPT': 17008,
'GETSPIRAL': 9040,
'SETSPIRAL': 9041,
'SEARCHTARGET': 17020
}
# Constants for EMD modes
edmModes = {'STANDARD': 0, 'PRECISE': 1, 'FAST': 2, 'TRACKING': 3,
'AVERAGING': 4, 'FASTTRACKING': 5}
# Constants for EDM programs
edmProg = {'STOP': 0, 'DEFAULT': 1, 'TRACKING': 2, 'CLEAR': 3}
def __init__(self, name='Leica generic', typ='TPS'):
""" Constructor to leica generic ts
"""
# call super class init
super().__init__(name, typ)
[docs] @staticmethod
def GetCapabilities():
""" Get instrument specialities
:returns: empty list, do not use generic instrument
"""
return []
[docs] def Result(self, msgs, anss):
""" Parse answer from message
:param msgs: messages sent to instrument
:param anss: aswers got from instrument
:returns: dictionary
"""
msgList = re.split('\|', msgs)
ansList = re.split('\|', anss)
res = {}
for msg, ans in zip(msgList, ansList):
# get command id form message
msgBufflist = re.split(':|,', msg)
try:
commandID = int(msgBufflist[1])
except Exception:
commandID = -1
# get error code from answer
ansBufflist = re.split(':|,', ans)
try:
errCode = int(ansBufflist[3])
if commandID == self.codes['GETMEASURE']:
res['hz'] = Angle(float(ansBufflist[4]))
res['v'] = Angle(float(ansBufflist[5]))
res['distance'] = float(ansBufflist[6])
# MeasureDistAng
elif commandID == self.codes['MEASUREANGDIST']:
res['hz'] = Angle(float(ansBufflist[4]))
res['v'] = Angle(float(ansBufflist[5]))
res['distance'] = float(ansBufflist[6])
# Prism constant
elif commandID == self.codes['GETPC']:
res['pc'] = float(ansBufflist[4])
# Prism type
elif commandID == self.codes['GETPT']:
res['pt'] = int(ansBufflist[4])
# ATR
elif commandID == self.codes['GETATR']:
res['atrStatus'] = int(ansBufflist[4])
#GetLockStatus()
elif commandID == self.codes['GETLOCK']:
res['lockStat'] = int(ansBufflist[4])
#GetAtmCorr()
elif commandID == self.codes['GETATMCORR']:
res['lambda'] = ansBufflist[4]
res['pressure'] = float(ansBufflist[5])
res['dryTemp'] = float(ansBufflist[6])
res['wetTemp'] = float(ansBufflist[7])
#GetRefCorr()
elif commandID == self.codes['GETREFCORR']:
res['status'] = ansBufflist[4]
res['earthRadius'] = float(ansBufflist[5])
res['refractiveScale'] = float(ansBufflist[6])
elif commandID == self.codes['GETSTN']:
res['east'] = float(ansBufflist[4])
res['north'] = float(ansBufflist[5])
res['elev'] = float(ansBufflist[6])
res['ih'] = float(ansBufflist[7])
elif commandID == self.codes['GETEDMMODE']:
res['edmMode'] = int(ansBufflist[4])
#Coords()
elif commandID == self.codes['COORDS']:
res['east'] = float(ansBufflist[4])
res['north'] = float(ansBufflist[5])
res['elev'] = float(ansBufflist[6])
#GetAngles()
elif commandID == self.codes['GETANGLES']:
res['hz'] = Angle(float(ansBufflist[4]))
res['v'] = Angle(float(ansBufflist[5]))
res['crossincline'] = Angle(float(ansBufflist[8]))
res['lengthincline'] = Angle(float(ansBufflist[9]))
# GetSpiral()
elif commandID == self.codes['GETSPIRAL']:
res['hzRange'] = Angle(float(ansBufflist[4]))
res['vRange'] = Angle(float(ansBufflist[5]))
elif commandID == self.codes['INSTRNO']:
res['instrNo'] = ansBufflist[4]
elif commandID == self.codes['INSTRNAME']:
res['instrName'] = ansBufflist[4]
elif commandID == self.codes['INTTEMP']:
res['intTemp'] = float(ansBufflist[4])
except ValueError:
errCode = -1 # invalid answer
except IndexError:
errCode = -2 # instrument off?
if errCode != 0:
logging.error(" error from instrument: %d (command %d)", errCode, commandID)
res['errorCode'] = errCode
#if not errCode in (1283, 1284, 1285, 1288):
# do not stop if accuracy is not perfect
return res
[docs] def SetPcMsg(self, pc):
""" Set prism constant
:param pc: prism constant [mm]
:returns: set prism constant message
"""
return f"%R1Q,{self.codes['SETPC']}:{pc}"
[docs] def GetPcMsg(self):
""" Get prism constant
:returns: get prism constant message
"""
return f"%R1Q,{self.codes['GETPC']}:"
[docs] def SetPrismTypeMsg(self, typ):
""" Set prism type
:param typ: prism type (0/1/2/3/4/5/6/7 round/mini/tape/360/user1/user2/user3/360 mini)
"""
return f"%R1Q,{self.codes['SETPT']}:{typ}"
[docs] def GetPrismTypeMsg(self):
""" Get prism type
:returns: prism type (0/1/2/3/4/5/6/7 round/mini/tape/360/user1/user2/user3/360 mini)
"""
return f"%R1Q,{self.codes['GETPT']}:"
[docs] def SetATRMsg(self, atr):
""" Set ATR status on/off
:param atr: 0/1 = off/on
:returns: set atr message string
"""
return f"%R1Q,{self.codes['SETATR']}:{atr}"
[docs] def GetATRMsg(self):
""" Get ATR status
:returns: get atr message
"""
return f"%R1Q,{self.codes['GETATR']}:"
[docs] def SetLockMsg(self, lock):
""" Set Lock status
:param lock: 0/1 = off/on
:returns: set lock status message
"""
return f"%R1Q,{self.codes['SETLOCK']}:{lock}"
[docs] def GetLockMsg(self):
""" Get Lock status
:returns: get lock status message
"""
return f"%R1Q,{self.codes['GETLOCK']}:"
[docs] def LockInMsg(self):
""" Activate lock
:returns: active lock message
"""
return f"%R1Q,{self.codes['LOCKIN']}:"
[docs] def SetAtmCorrMsg(self, valueOfLambda, pres, dry, wet):
""" Set atmospheric correction settings
:param valueOfLambda: Constant for the instrument not changeable, use GetAtmCorr to get value
:param pres: pressure value
:param dry: dry temperature
:param wet: wet temperature
:returns: set atmospheric correction message
"""
return f"%R1Q,{self.codes['SETATMCORR']}:{valueOfLambda:.8f},{pres},{dry},{wet}"
[docs] def GetAtmCorrMsg(self):
""" Get atmospheric correction settings
:returns: iget atmospheric settings message
"""
return f"%R1Q,{self.codes['GETATMCORR']}:"
[docs] def SetRefCorrMsg(self, status, earthRadius, refracticeScale):
""" Set refraction correction settings
:param status: 0/1 = off/on
:param earthRadius: radius ot the Earth
:param refracticeScale: refractice scale
:returns: set refraction message
"""
return f"%R1Q,{self.codes['SETREFCORR']}:{status},{earthRadius},{refracticeScale}"
[docs] def GetRefCorrMsg(self):
""" Get refraction correction setting
:returns: get refraction correction message
"""
return f"%R1Q,{self.codes['GETREFCORR']}:"
[docs] def SetStationMsg(self, e, n, z, ih=0.0):
""" Set station coordinates
:param e: easting
:param n: northing
:param z: elevation
:param ih: instrument height (optional, default 0)
:returns: set station coordinates message
"""
return f"%R1Q,{self.codes['SETSTN']}:{e},{n},{z},{ih}"
[docs] def GetStationMsg(self):
""" Get station coordinates
:returns: get station coordinates message
"""
return f"%R1Q,{self.codes['GETSTN']}:"
[docs] def SetEDMModeMsg(self, mode):
""" Set EDM mode
:param mode: string name/code
:returns: set edm mode message
"""
if type(mode) is str:
imode = self.edmModes[mode]
else:
imode = mode
return f"%R1Q,{self.codes['SETEDMMODE']}:{imode}"
[docs] def GetEDMModeMsg(self):
""" Get EDM mode
:returns: get edm mode message
"""
return f"%R1Q,{self.codes['GETEDMMODE']}:"
[docs] def SetOriMsg(self, ori):
""" Set orientation angle
:param ori: bearing of direction (Angle)
:returns: 0 or error code
"""
ori_rad = ori.GetAngle('RAD')
return f"%R1Q,{self.codes['SETORI']}:{ori_rad}"
[docs] def MoveMsg(self, hz, v, atr=0):
""" Rotate instrument to direction with ATR or without ATR
:param hz: horizontal direction (Angle)
:param v: zenith angle (Angle)
:param atr: 0/1 atr off/on, default off
:returns: rotate message
"""
# change angles to radian
hz_rad = hz.GetAngle('RAD')
v_rad = v.GetAngle('RAD')
return f"%R1Q,{self.codes['MOVE']}:{hz_rad},{v_rad},0,{atr},0"
[docs] def MeasureMsg(self, prg=1, incl=0):
""" Measure distance
:param prg: measure program 1/2/3/... = default/track/clear..., optional (default 1, mode set before)
:param incl: inclination calculation - 0/1/2 = measure always (slow)/calculate (fast)/automatic, optional (default 0)
:returns: measure message
"""
return f"%R1Q,{self.codes['MEASURE']}:{prg},{incl}"
[docs] def GetMeasureMsg(self, wait=15000, incl=0):
""" Get measured distance
:param wait: time in ms, optional (default 15000), it must be greater than 12000, the default on the instrument
:param incl: inclination calculation - 0/1/2 = measure always (slow)/calculate (fast)/automatic, optional (default 0)
:returns: get simple measurement message
"""
return f"%R1Q,{self.codes['GETMEASURE']}:{wait},{incl}"
[docs] def MeasureDistAngMsg(self, prg):
""" Measure angles and distance
:param prg: EDM program
:returns: measure angle distance message
"""
if type(prg) is str:
prg = self.edmProg[prg]
return f"%R1Q,{self.codes['MEASUREANGDIST']}:{prg}"
[docs] def CoordsMsg(self, wait=15000, incl=0):
""" Get coordinates
:param wait: wait-time in ms, optional (default 15000), it must be greater than 12000, the default on instrument
:param incl: inclination calculation - 0/1/2 = measure always (slow)/calculate (fast)/automatic, optional (default 0)
:returns: get coordinates message
"""
return f"%R1Q,{self.codes['COORDS']}:{wait},{incl}"
[docs] def GetAnglesMsg(self):
""" Get angles
:returns: get angles message
"""
return f"%R1Q,{self.codes['GETANGLES']}:0"
[docs] def ClearDistanceMsg(self):
""" Clearing distance
:returns: clear distance message
"""
return self.MeasureMsg(self, 3)
[docs] def ChangeFaceMsg(self):
""" Change face
:returns: change face message
"""
return f"%R1Q,{self.codes['CHANGEFACE']}:"
[docs] def GetSpiralMsg(self):
""" Get search spiral parameters
:returns: get spiral message
"""
return f"%R1Q,{self.codes['GETSPIRAL']}:"
[docs] def SetSpiralMsg(self, dRangeHz, dRangeV):
""" Set search priral parameters
:param dRangeHz: horizontal range of search (Angle)
:param dRangeV: vertical range of search (Angle)
:returns: set search spiral message
"""
return f"%R1Q,{self.codes['SETSPIRAL']}:{dRangeHz.GetAngle('RAD')},{dRangeV.GetAngle('RAD')}"
[docs] def SearchTargetMsg(self):
""" Search target using user spiral
:returns: Search target message
"""
return f"%R1Q,{self.codes['SEARCHTARGET']}:"
[docs] def SwitchOnMsg(self, mode=1):
""" Switch on instrument or wake up and change to remote mode
:param mode: startup mode 0/1 local/remote
:returns: switch on message
"""
return f"%R1Q,{self.codes['SWITCHON']}:{mode}"
[docs] def SwitchOffMsg(self):
""" Switch off instrument
"""
return 'b'
[docs] def GetInstrumentNoMsg(self):
""" Get instrument factory number
:returns: get instrument factory number message
"""
return f"%R1Q,{self.codes['INSTRNO']}:"
[docs] def GetInstrumentNameMsg(self):
""" Get instrument name
:returns: get instrument name
"""
return f"%R1Q,{self.codes['INSTRNAME']}:"
[docs] def GetInternalTemperatureMsg(self):
""" Get instrument internal temperature
:returns: instrument internal temperature
"""
return f"%R1Q,{self.codes['INTTEMP']}:"