Created
November 27, 2018 09:05
-
-
Save jn0/cc5c78f4a0f447a6fb2e45a5d9efa13d to your computer and use it in GitHub Desktop.
PELCO-D Protocol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
'''The Pelco-D Protocol | |
Standard Number TF-0002 | |
Version 2 Revision 1 Release Date August 15, 2003 | |
Transmitters will format a single character and receivers will be able to | |
decipher a single character as: 1 start bit, 8 data bits, 1 stop bit, | |
and no parity. | |
Intended use: | |
import serial | |
import pelco_d | |
CAM = 1 | |
with serial.Serial(PORT) as com: | |
command, parser = pelco_d.get(pelco_d.CameraOn, CAM) | |
com.write(command) | |
response = com.read() | |
print parser(response, command[-1]) | |
THE MESSAGE FORMAT | |
The format for a message is: | |
Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7 | |
Sync Byte Address Command 1 Command 2 Data 1 Data 2 Checksum | |
Note that values in this document prefixed with “0x” are hexadecimal numbers. | |
The synchronization byte (Sync Byte) is always 0xFF. | |
The Address is the logical address of the receiver/driver device being controlled. | |
The Checksum is calculated by performing the 8 bit (modulo 256) sum of the | |
payload bytes (bytes 2 through 6) in the message. | |
''' | |
def get(proc, *args): | |
command = proc(*args) | |
parser = get_parser_for(proc) | |
return command, parser | |
SYNC = 0xff | |
def command(*av): | |
assert len(av) == 5, av | |
assert len(filter(lambda x: x < 0 or x > 255, l)) == 0, av | |
checksum = sum(av) & 255 | |
return bytearray((SYNC,) + tuple(av) + (checksum,)) | |
__doc__ += ''' | |
STANDARD COMMANDS | |
Command 1 and 2 are represented as follows: | |
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 | |
Command 1 Sense Rsrvd Rsrvd Auto/ Camera Iris Iris Focus | |
Manual On Close Open Near | |
Scan Off | |
Command 2 Focus Zoom Zoom Down Up Left Righ Always 0 | |
Far Wide Tele | |
A value of ‘1’ entered in the bit location for the function desired will enable | |
that function. A value of ‘0’ entered in the same bit location will disable or | |
‘stop’ the function. | |
The sense bit (command 1 bit 7) indicates the meaning of bits 4 and 3. | |
If the sense bit is on (value of ‘1’), and bits 4 and 3 are on, | |
the command will enable auto-scan and turn the camera on. | |
If the sense bit is off (value of ‘0’), and bits 4 and 3 are on the command | |
will enable manual scan and turn the camera off. | |
Of course, if either bit 4 or bit 3 are off then no action will be taken for | |
those features. | |
The reserved bits (6 and 5) should be set to 0. | |
Byte 5 contains the pan speed. | |
Pan speed is in the range of ‘0x00’ to ‘0x3F’ (high speed) and ‘0x40’ for “turbo” speed. | |
Turbo speed is the maximum speed the device can obtain and is considered | |
separately because it is not generally a smooth step from high speed to turbo. | |
That is, going from one speed to the next usually looks smooth and will provide | |
for smooth motion with the exception of going into and out of turbo speed. A pan | |
speed value of ‘0x00’ results in very slow motion, not cessation of motion. | |
To stop pan motion both the Left and Right direction bits must be turned off | |
– set to ‘0’ – regardless of the value set in the pan speed byte. | |
Byte 6 contains the tilt speed. | |
Tilt speed is in the range of ‘0x00’ to ‘0x3F’ (maximum speed). | |
Turbo speed is not allowed for the tilt axis. | |
A tilt speed value of ‘0x00’ results in very slow motion, not cessation of motion. | |
To stop tilt motion both the Down and Up direction bits must be turned off | |
– set to ‘0’ – regardless of the value set in the tilt speed byte. | |
Byte 7 is the checksum. | |
The checksum is the 8 bit (modulo 256) sum of the payload bytes (bytes 2 through | |
6) in the message. | |
''' | |
# 76543210 | |
SENSE = 0b10000000 | |
RSRV6 = 0b01000000 | |
RSRV5 = 0b00100000 | |
SCAN = 0b00010000 | |
CAMERA= 0b00001000 | |
IRISCL= 0b00000100 | |
IRISOP= 0b00000010 | |
FOCUSN= 0b00000001 | |
FOCUSF= 0b10000000 | |
ZOOMWD= 0b01000000 | |
ZOOMTL= 0b00100000 | |
DOWN = 0b00010000 | |
UP = 0b00001000 | |
LEFT = 0b00000100 | |
RIGHT = 0b00000010 | |
RSRV0 = 0b00000001 | |
FAKE_PAN_SPEED = 0x1200 | |
FAKE_TILT_SPEED = 0x0012 | |
def Standard_Command(addr, cmd1, cmd2, data=0): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= cmd1 <= 255, cmd1 | |
assert cmd1 & (RSRV6 | RSRV5) == 0, cmd1 | |
assert 0 <= cmd2 <= 255, cmd2 | |
assert cmd2 & RSRV0 == 0, cmd2 | |
assert 0 <= data <= 65535, data1 | |
d1, d2 = (data >> 8) & 255, data & 255 | |
return command(addr, cmd1, cmd2, d1, d2) | |
def Camera_On(addr): | |
'General Reponse' | |
return Standard_Command(addr, SENSE|CAMERA, 0) | |
def Camera_Off(addr): | |
'General Reponse' | |
return Standard_Command(addr, CAMERA, 0) | |
def Camera(addr, on): | |
'General Reponse' | |
return Camera_On(addr) if on else Camera_Off(addr) | |
def Scan_Auto(addr): | |
'General Reponse' | |
return Standard_Command(addr, SENSE|SCAN, 0) | |
def Scan_Manual(addr): | |
'General Reponse' | |
return Standard_Command(addr, SCAN, 0) | |
def Scan(addr, auto): | |
'General Reponse' | |
return Scan_Auto(addr) if auto else Scan_Manual(addr) | |
def Iris_Close(addr): | |
'General Reponse' | |
return Standard_Command(addr, IRISCL, 0) | |
def Iris_Open(addr): | |
'General Reponse' | |
return Standard_Command(addr, IRISOP, 0) | |
def Iris(addr, open): | |
'General Reponse' | |
return Iris_Open(addr) if open else Iris_Close(addr) | |
def Focus_Near(addr): | |
'General Reponse' | |
return Standard_Command(addr, FOCUSN, 0) | |
def Focus_Far(addr): | |
'General Reponse' | |
return Standard_Command(addr, 0, FOCUSF) | |
def Focus(addr, far): | |
'General Reponse' | |
return Focus_Far(addr) if far else Focus_Near(addr) | |
def Zoom_Wide(addr): | |
'General Reponse' | |
return Standard_Command(addr, 0, ZOOMWD) | |
def Zoom_Tele(addr): | |
'General Reponse' | |
return Standard_Command(addr, 0, ZOOMTL) | |
def Zoom(addr, tele): | |
'General Reponse' | |
return Zoom_Tele(addr) if tele else Zoom_Wide(addr) | |
def Pan_Left(addr, speed): | |
'General Response' | |
assert 0 <= speed <= 0x40, speed | |
return Standard_Command(addr, 0, LEFT, speed << 8) | |
def Pan_Right(addr, speed): | |
'General Response' | |
assert 0 <= speed <= 0x40, speed | |
return Standard_Command(addr, 0, RIGHT, speed << 8) | |
def Pan_Stop(addr): | |
'General Response' | |
return Standard_Command(addr, 0, 0, FAKE_PAN_SPEED) | |
def Pan(addr, speed): | |
'General Response' | |
if not speed: | |
return Pan_Stop(addr) | |
elif speed > 0: | |
return Pan_Right(addr, speed) | |
else: | |
return Pan_Left(addr, -speed) | |
def Tilt_Up(addr, speed): | |
'General Response' | |
assert 0 <= speed <= 0x3f, speed | |
return Standard_Command(addr, 0, UP, speed) | |
def Tilt_Down(addr, speed): | |
assert 0 <= speed <= 0x3f, speed | |
return Standard_Command(addr, 0, DOWN, speed) | |
def Tilt_Stop(addr): | |
'General Response' | |
return Standard_Command(addr, 0, 0, FAKE_TILT_SPEED) | |
def Tilt(addr, speed): | |
'General Response' | |
if not speed: | |
return Tilt_Stop(addr) | |
elif speed > 0: | |
return Tilt_Up(addr, speed) | |
else: | |
return Tilt_Down(addr, -speed) | |
__doc__ += ''' | |
Set Preset (0x03) | |
Clear Preset (0x05) | |
Go To Preset (0x07) | |
The parameter in byte 6 of these commands is the ID of the preset to be acted on. | |
Valid preset IDs begin at 1. | |
Most devices support at least 32 presets. | |
Refer to the manual of the device under use for information about | |
what range of presets are valid for that equipment. | |
''' | |
AUTO, ON, OFF = 0, 1, 2 | |
def Set_Preset(addr, presetId): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 1 <= presetId <= 255, presetId | |
return command(addr, 0, 0x03, 0, presetId) | |
def Clear_Preset(addr, presetId): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 1 <= presetId <= 255, presetId | |
return command(addr, 0, 0x05, 0, presetId) | |
def Go_To_Preset(addr, presetId): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 1 <= presetId <= 255, presetId | |
return command(addr, 0, 0x07, 0, presetId) | |
def Flip_180_about(addr): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x07, 0, 0x21) | |
def Go_To_Zero_Pan(addr): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x07, 0, 0x22) | |
def Set_Auxiliary(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 1 <= value <= 8, value | |
return command(addr, 0, 0x09, 0, value) | |
def Clear_Auxiliary(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 1 <= value <= 8, value | |
return command(addr, 0, 0x0b, 0, value) | |
def Remote_Reset(addr): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x0f, 0, 0) | |
def Set_Zone_Start(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 1 <= value <= 8, value | |
return command(addr, 0, 0x11, 0, value) | |
def Set_Zone_End(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 1 <= value <= 8, value | |
return command(addr, 0, 0x13, 0, value) | |
__doc__ += ''' | |
Write Character To Screen (0x15) | |
The parameter in byte 5 of this command indicates the column to write to. | |
This parameter is interpreted as follows: | |
- Columns 0-19 are used to receive zone labels. | |
- Columns 20-39 are used to receive preset labels. | |
''' | |
def Write_Character_to_Screen(addr, column, char): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= column <= 39, column | |
assert 0 <= char <= 255, char | |
return command(addr, 0, 0x15, column, char) | |
def Write_Zone_Label(addr, column, char): | |
'General Response' | |
assert 0 <= column <= 19, column | |
return Write_Character_to_Screen(addr, column, char) | |
def Write_Preset_Lebel(addr, column, char): | |
'General Response' | |
assert 0 <= column <= 19, column | |
return Write_Character_to_Screen(addr, 20 + column, char) | |
def Clear_Screen(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x17, 0, 0) | |
def Alarm_Acknowledge(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 1 <= value <= 8, value | |
return command(addr, 0, 0x19, 0, value) | |
def Zone_Scan_On(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x1b, 0, 0) | |
def Zone_Scan_Off(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x1d, 0, 0) | |
__doc__ += ''' | |
Set Pattern Start (0x1F) | |
Run Pattern (0x23) | |
The parameter in byte 6 of these commands indicates the pattern to be set/run. | |
Platform dependent. | |
''' | |
def Set_Pattern_Start(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 255, value | |
return command(addr, 0, 0x1f, 0, value) | |
def Set_Pattern_Stop(addr): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x21, 0, 0) | |
def Run_Pattern(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 255, value | |
return command(addr, 0, 0x23, 0, value) | |
def Set_Zoom_Speed(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 3, value | |
return command(addr, 0, 0x25, 0, value) | |
def Set_Focus_Speed(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 3, value | |
return command(addr, 0, 0x27, 0, value) | |
def Reset_Camera_to_defaults(addr): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x29, 0, 0) | |
def Auto_focus(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert value in (AUTO, ON, OFF), value | |
return command(addr, 0, 0x2b, 0, value) | |
def Auto_Iris(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert value in (AUTO, ON, OFF), value | |
return command(addr, 0, 0x2d, 0, value) | |
def AGC(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert value in (AUTO, ON, OFF), value | |
return command(addr, 0, 0x2f, 0, value) | |
def Backlight_compensation(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert value in (ON, OFF), value | |
return command(addr, 0, 0x31, 0, value) | |
def Auto_white_balance(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert value in (ON, OFF), value | |
return command(addr, 0, 0x33, 0, value) | |
def Enable_device_phase_delay_mode(addr): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x35, 0, 0) | |
def Set_shutter_speed(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x37, d1, d2) | |
def Adjust_line_lock_phase_delay_0(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x39, d1, d2) | |
def Adjust_line_lock_phase_delay_1(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 1, 0x39, d1, d2) | |
def Adjust_white_balance_RB0(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x3b, d1, d2) | |
def Adjust_white_balance_RB1(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 1, 0x3b, d1, d2) | |
def Adjust_white_balance_MG0(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x3d, d1, d2) | |
def Adjust_white_balance_MG1(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 1, 0x3d, d1, d2) | |
def Adjust_gain_0(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x3f, d1, d2) | |
def Adjust_gain_1(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 1, 0x3f, d1, d2) | |
def Adjust_auto_iris_level_0(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x41, d1, d2) | |
def Adjust_auto_iris_level_1(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 1, 0x41, d1, d2) | |
def Adjust_auto_iris_peak_0(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x43, d1, d2) | |
def Adjust_auto_iris_peak_1(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 1, 0x43, d1, d2) | |
def Query(addr, value): | |
'''This command can only be used in a point to point application. | |
A device being queried will respond to any address. | |
If more than one device hears this command, | |
multiple devices will transmit at the same time. | |
Query45 Response''' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x45, d1, d2) | |
def Reserved_Opcode(addr, value): | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 255, addr | |
return command(addr, 0, value, 0, 0) | |
def Reserved_Opcode_47(addr): return Reserved_Opcode(addr, 0x47) | |
__doc__ += ''' | |
Set Zero Position (0x49) | |
This command is used to set the pan position that the unit uses as a zero | |
reference point for the azimuth on-screen display. | |
The unit’s current pan position when this command is received becomes the zero | |
reference point. | |
This command performs the same function as the “Set Azimuth Zero” menu item. | |
''' | |
def Set_Zero_Position(addr): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x49, 0, 0) | |
__doc__ += ''' | |
Set Pan Position (0x4B) | |
This command is used to set the pan position of the device. | |
The position is given in hundredths of a degree and has a range from 0 to 35999 | |
(decimal). | |
Example: the value to use to set the pan position to 45 degrees is 4500. | |
Note that the value used here is always the “absolute” pan position. | |
It does not take into account any adjustment to the screen display that may | |
have been made by using the “Set Zero Position”, opcode (0x49) command or | |
the “Set Azimuth Zero” menu item. | |
''' | |
def Set_Pan_Position(addr, degrees): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= degrees <= 35999, degrees | |
d1, d2 = (degrees >> 8) & 255, degrees & 255 | |
return command(addr, 0, 0x4b, d1, d2) | |
__doc__ += ''' | |
Set Tilt Position (0x4D) | |
This command is used to set the tilt position of the device. | |
The position is given in hundredths of a degree and has a range from 0 to 35999 | |
(decimal). Generally these values are interpreted as follows: | |
- Zero degrees indicates that the device is pointed horizontally (at the horizon). | |
- Ninety degrees indicates that the device is pointed straight down. | |
Examples: | |
1) the value used to set the tilt position to 45 degrees below the horizon, is 4500. | |
2) the value used to set the tilt position 30 degrees above the horizon, is 33000. | |
Note that different equipment will have different ranges of motion. | |
To determine the abilities of a specific piece of equipment, | |
refer to that device’s operation manual. | |
''' | |
TILT_NONE = TITL_HORIZON = 0 | |
TILT_DOWN = 9000 | |
TILT_UP = 27000 | |
def Set_Tilt_Position(addr, degrees): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= degrees <= 35999, degrees | |
d1, d2 = (degrees >> 8) & 255, degrees & 255 | |
return command(addr, 0, 0x4d, d1, d2) | |
__doc__ += ''' | |
Set Zoom Position (0x4F) | |
This command is used to set the zoom position of the device. | |
The position is given as a ratio based on the device’s Zoom Limit setting. | |
The position is calculated as follows: | |
Position = (desired_zoom_position / zoom_limit) * 65535 | |
Where desired_zoom_position and zoom_limit are given in units of magnification. | |
Example: | |
Given that the zoom limit of the device’s camera is X184, | |
calculate the value needed to set the zoom position to X5: | |
Position = (5 / 184) * 65535 = approximately 1781 | |
''' | |
def Set_Zoom_Position(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x4f, d1, d2) | |
__doc__ += ''' | |
Query Pan Position (0x51) | |
This command is used to query the current pan position of the device. | |
The response to this command uses opcode 0x59. | |
See the description of opcode 0x59 for more information. | |
''' | |
def Query_Pan_Position(addr): | |
'Extended Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x51, 0, 0) | |
__doc__ += ''' | |
Query Tilt Position (0x53) | |
This command is used to query the current tilt position of the device. | |
The response to this command uses opcode 0x5B. | |
See the description of opcode 0x5B for more information. | |
''' | |
def Query_Tilt_Position(addr): | |
'Extended Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x53, 0, 0) | |
__doc__ += ''' | |
Query Zoom Position (0x55) | |
This command is used to query the current zoom position of the device. | |
The response to this command uses opcode 0x5D. | |
See the description of opcode 0x5D for more information. | |
''' | |
def Query_Zoom_Position(addr): | |
'Extended Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x55, 0, 0) | |
def Reserved_Opcode_57(addr): return Reserved_Opcode(addr, 0x57) | |
__doc__ += ''' | |
Query Pan Position Response (0x59) | |
The position is given in hundredths of a degree and has a range from 0 to 35999 (decimal). | |
Example: a position value of 4500 indicates 45 degrees. | |
Note that the value returned is always the “absolute” pan position. | |
It does not take into account any adjustment to the screen display that may | |
have been made by using the “Set Zero Position”, opcode (0x49) command or | |
the “Set Azimuth Zero” menu item. | |
''' | |
def Query_Pan_Response(addr, value): | |
'Extended Response' # ??? XXX | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x59, d1, d2) | |
__doc__ += ''' | |
Query Tilt Position Response (0x5B) | |
The position is given in hundredths of a degree and has a range from 0 to 35999 (decimal). | |
Refer to examples listed in description of the “Set Tilt Position”, opcode 0x4D command. | |
''' | |
def Query_Tilt_Response(addr, value): | |
'Extended Response' # ??? XXX | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x5b, d1, d2) | |
__doc__ += ''' | |
Query Zoom Position Response (0x5D) | |
The position is given as a ratio based on the device’s Zoom Limit setting. | |
This value can be converted into units of magnification by using the following formula: | |
current_magnification = (position / 65535) * zoom_limit | |
Where current_zoom_position and zoom_limit are given in units of magnification. | |
Example: | |
Given that the zoom limit of the device’s camera is X184, | |
position value is 1781, calculate the current magnification: | |
Current magnification = (1781 / 65535) * 184 = approximately X5. | |
Note: This message is sent in response to the Query Zoom Position (0x55) command. | |
''' | |
def Query_Zoom_Response(addr, value): | |
'Extended Response' # ??? XXX | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x5d, d1, d2) | |
__doc__ += ''' | |
Set Magnification (0x5F) | |
This command is used to set the zoom position of the device. | |
The position is given in hundredths of units of magnification. | |
Example: a value of 500 means X5. | |
''' | |
def Set_Magnification(addr, value): | |
'General Response' | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x5f, d1, d2) | |
__doc__ += ''' | |
Query Magnification (0x61) | |
This command is used to query the current zoom position of the device. | |
The response to this command uses opcode 0x63. | |
See the description of opcode 0x63 for more information. | |
''' | |
def Query_Magnification(addr): | |
'Extended Response' | |
assert 0 <= addr <= 255, addr | |
return command(addr, 0, 0x61, 0, 0) | |
__doc__ += ''' | |
Query Magnification Response (0x63) | |
The value returned is given in hundredths of units of magnification. | |
Example: a value of 500 means X5. | |
''' | |
def Query_Magnification_Response(addr, value): | |
assert 0 <= addr <= 255, addr | |
assert 0 <= value <= 65535, value | |
d1, d2 = (value >> 8) & 255, value & 255 | |
return command(addr, 0, 0x63, d1, d2) | |
def Reserved_Opcode_65(addr): return Reserved_Opcode(addr, 0x65) | |
def Reserved_Opcode_67(addr): return Reserved_Opcode(addr, 0x67) | |
def Reserved_Opcode_69(addr): return Reserved_Opcode(addr, 0x69) | |
def Reserved_Opcode_6b(addr): return Reserved_Opcode(addr, 0x6b) | |
def Reserved_Opcode_6d(addr): return Reserved_Opcode(addr, 0x6d) | |
def Reserved_Opcode_6f(addr): return Reserved_Opcode(addr, 0x6f) | |
def Reserved_Opcode_71(addr): return Reserved_Opcode(addr, 0x71) | |
__doc__ += ''' | |
The General Response | |
The General Response has the following format. | |
Note that each block represents 1 byte. | |
Byte 1 Byte 2 Byte 3 Byte 4 | |
Sync Address Alarm Information Checksum | |
The alarm information is formatted as follows: | |
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 | |
None Alarm 7 Alarm 6 Alarm 5 Alarm 4 Alarm 3 Alarm 2 Alarm 1 | |
If the bit is on (1) then the alarm is active. If the bit is off (0) | |
then the alarm is inactive. | |
The checksum is the sum of the transmitted command’s checksum and the alarm | |
information. | |
''' | |
# 76543210 | |
ALARM1 = 0b00000001 | |
ALARM2 = 0b00000010 | |
ALARM3 = 0b00000100 | |
ALARM4 = 0b00001000 | |
ALARM5 = 0b00010000 | |
ALARM6 = 0b00100000 | |
ALARM7 = 0b01000000 | |
ALARMX = 0b10000000 | |
def Parse_General_Response(resp, sent_cs=None): | |
assert len(resp) == 4, resp | |
assert resp[0] == SYNC, resp | |
addr = resp[1] | |
info = resp[2] & ~ALARMX | |
assert 0 <= info <= 127, info | |
if sent_cs is not None: | |
assert 0 <= sent_cs <= 255, sent_cs | |
csum = resp[3] | |
assert csum == ((sent_cs + info) & 255), resp | |
res = set() | |
if info & ALARM1: res.add(ALARM1) | |
if info & ALARM2: res.add(ALARM2) | |
if info & ALARM3: res.add(ALARM3) | |
if info & ALARM4: res.add(ALARM4) | |
if info & ALARM5: res.add(ALARM5) | |
if info & ALARM6: res.add(ALARM6) | |
if info & ALARM7: res.add(ALARM7) | |
return addr, res | |
__doc__ += ''' | |
The Query (0x45) Response | |
The response to the Query command is: | |
Byte 1 Byte 2 Bytes 3 to 17 Byte 18 | |
Sync (1 byte) Address (1 byte) Part Number (15 bytes) Checksum (1 byte) | |
The address is the address of the device responding to the query. | |
The content of the part number field is dependent on the type and version of | |
the device being programmed, please refer to the table that follows. | |
The checksum is the 8 bit (modulo 256) sum of the transmitted query command’s | |
checksum, the address of the response, and the 15-byte part number. | |
''' | |
def Parse_Query45_Response(resp, sent_cs=None): | |
assert len(resp) == 18, resp | |
assert resp[0] == SYNC, resp | |
addr = resp[1] | |
pnum = resp[2:-1] | |
if sent_cs is not None: | |
assert 0 <= sent_cs <= 255, sent_cs | |
csum = resp[-1] | |
xsum = sum(pnum) & 255 | |
assert csum == ((sent_cs + addr + xsum) & 255), resp | |
return addr, str(pnum) | |
__doc__ += ''' | |
The Extended Response | |
The Extended Response has the following format. | |
Note that each block represents 1 byte | |
Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7 | |
Sync Address Future Use “opcode” Data1 Data2 Checksum | |
The address is the address of the device that is responding. | |
The Future Use byte should always be set to 0. | |
Opcode, Data1 and Data2 are dependent on the type of response. | |
See the opcode description section of this document for the details of a particular response. | |
The checksum is the 8 bit (modulo 256) sum of all the bytes excluding the Sync byte. | |
''' | |
def Parse_Extended_Response(resp, _=None, _op=None): | |
assert len(resp) == 7, resp | |
assert resp[0] == SYNC, resp | |
addr = resp[1] | |
rezerved = resp[2] | |
assert rezerved == 0, resp | |
opcode = resp[3] | |
if _op is not None: | |
assert opcode == _op, resp | |
data1 = resp[4] | |
data2 = resp[5] | |
csum = resp[-1] | |
xsum = sum(resp[1:-1]) & 255 | |
assert csum == xsum, resp | |
return addr, (opcode, data1, data2) | |
def get_parser_for(func): | |
docstr = func.__doc__.strip() | |
if 'Response' in docstr: | |
for l in docstr.splitlines(): | |
if l.endswith('Response'): | |
if ',' in l: | |
l, op = l.split(',', 1) # `op` is unused now | |
parser_name = 'Parse_' + '_'.join(l.split()) | |
if parser_name in globals(): | |
return globals()[parser_name] | |
# vim: ai et ts=4 sts=4 sw=4 ft=python |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment