kopia lustrzana https://github.com/Hamlib/Hamlib
250 wiersze
8.4 KiB
Python
Executable File
250 wiersze
8.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# This file is part of Hamlib
|
|
# (C) 2021 Stephane Fillod
|
|
#
|
|
# SPDX-License-Identifier: GPL-3
|
|
|
|
"""
|
|
Protocol simulator of PRM80xx running firmware V5.
|
|
This script needs a POSIX system to run.
|
|
|
|
On one terminal, start this script:
|
|
$ ./pysimulprm80.py -l /tmp/prm80simul
|
|
Slave name /dev/pts/5
|
|
Name symlinked /tmp/prm80simul
|
|
...
|
|
On another terminal, test the prm80 backend:
|
|
$ rigctl -vvvvvv -r /tmp/prm80simul -m 28001
|
|
"""
|
|
# Implementation from
|
|
# - https://github.com/f4fez/prm80/blob/master/doc/Computer_commands_V4.md
|
|
# - https://github.com/f4fez/prm80/blob/master/doc/Computer_control.md
|
|
# - https://sourceforge.net/p/hamlib/discussion/25919/thread/93afa09f52/
|
|
# - https://github.com/f4fez/prm80/blob/master/src/inc_ser.a51
|
|
|
|
import argparse
|
|
import time
|
|
import os, pty, tty, termios
|
|
|
|
class Prm80Simul:
|
|
|
|
def __init__(self, pty_r, pty_w):
|
|
self.pty_r = pty_r
|
|
self.pty_w = pty_w
|
|
self.SetupPseudoSerial(self.pty_r)
|
|
self.SetupPseudoSerial(self.pty_w)
|
|
|
|
self.msg_version = "PRM8060 V5.0 430"
|
|
|
|
self.mdict = {
|
|
'V' : self.tch_Version,
|
|
'N' : self.tch_Nmem,
|
|
'K' : self.tch_Klock,
|
|
'F' : self.tch_Fsquelch,
|
|
'O' : self.tch_Ovolume,
|
|
'D' : self.tch_Dmode,
|
|
'T' : self.tch_Tstate,
|
|
'U' : self.tch_UprintRAM,
|
|
'P' : self.tch_Peditchannel,
|
|
'Q' : self.tch_Qmaxchan,
|
|
'C' : self.tch_Chanlist,
|
|
'R' : self.tch_Rfreq,
|
|
'E' : self.tch_Estate,
|
|
'#' : self.tch_diese,
|
|
'*' : self.tch_autre,
|
|
}
|
|
# Some live data simulated inside the rig
|
|
self.ChanNum = 0
|
|
self.LockByte = 0
|
|
self.Squelch = 0
|
|
self.Volume = 0x10
|
|
self.ModeByte = 0x16
|
|
self.ChanState = 0x0c
|
|
self.MaxChan = 80
|
|
self.RxPLL = 0x7970 # 410 MHz (w/ IF offset)
|
|
self.TxPLL = 0x8020 # 410 MHz
|
|
|
|
def SetupPseudoSerial(self, fd):
|
|
""" make it raw, at 4800 bauds (defaults to 8N1) """
|
|
tty.setraw(fd)
|
|
term_settings = termios.tcgetattr(fd)
|
|
term_settings[4] = termios.B4800 # ispeed
|
|
term_settings[5] = termios.B4800 # ospeed
|
|
termios.tcsetattr(fd, termios.TCSANOW, term_settings)
|
|
|
|
def pty_write(self, a):
|
|
""" helper write+flush """
|
|
self.pty_w.write(a)
|
|
self.flush_w()
|
|
|
|
|
|
def tch_autre(self):
|
|
""" Unknown command """
|
|
self.pty_write(b'* ?')
|
|
|
|
def tch_Version(self):
|
|
""" Print firmware version """
|
|
self.pty_write(self.msg_version.encode())
|
|
|
|
def tch_Nmem(self):
|
|
""" Set current channel """
|
|
self.pty_write(b'Channel : ')
|
|
self.ChanNum = int(pty_r.read(2).decode('ascii'))
|
|
|
|
def tch_Klock(self):
|
|
""" Set lock byte """
|
|
self.pty_write(b'Lock : ')
|
|
self.LockByte = int(pty_r.read(2).decode('ascii'), 16)
|
|
|
|
def tch_Fsquelch(self):
|
|
""" Set squelch. """
|
|
self.pty_write(b'Squelch : ')
|
|
self.Squelch = int(pty_r.read(2).decode('ascii'))
|
|
|
|
def tch_Ovolume(self):
|
|
""" Set volume. """
|
|
self.pty_write(b'Volume : ')
|
|
self.Volume = int(pty_r.read(2).decode('ascii'))
|
|
|
|
def tch_Dmode(self):
|
|
""" Set "Mode" byte. """
|
|
self.pty_write(b'Mode : ')
|
|
self.ModeByte = int(pty_r.read(2).decode('ascii'), 16)
|
|
|
|
def tch_Tstate(self):
|
|
""" Set Channel State. """
|
|
self.pty_write(b'Channel state : $')
|
|
self.ChanState = int(pty_r.read(2).decode('ascii'))
|
|
|
|
def tch_UprintRAM(self):
|
|
""" Print 80c552 internal RAM. """
|
|
self.pty_w.write(b'Display the 256 bytes from internal RAM : \r\n')
|
|
# 16 lines of 16 bytes each. The command needs about 3 sec.
|
|
# In V4, rssi_hold is at 0x53
|
|
self.pty_w.write(b'$00 : 00 00 00 D5 09 00 00 0F 7F 99 D2 7D B2 93 8F C5\r\n')
|
|
self.pty_w.write(b'$10 : 60 8B 43 CA 31 43 BB BA F9 27 87 47 06 52 BE 55\r\n')
|
|
self.pty_w.write(b'$20 : 81 2F FD 17 01 12 A3 00 13 02 34 96 0B 04 54 7A\r\n')
|
|
self.pty_w.write(b'$30 : 13 03 00 30 B4 00 03 00 FC 58 71 00 0F 08 08 82\r\n')
|
|
self.pty_w.write(b'$40 : 9E 86 EE 60 1F 1F 00 FB FF FF 44 55 FF 73 5A F5\r\n')
|
|
self.pty_w.write(b'$50 : 7E 02 06 72 B7 FF 8D B1 92 79 93 8D 3E 4E 8E 06\r\n')
|
|
self.pty_w.write(b'$60 : DB C7 3D 11 AE 5A 4F 3D 80 82 D1 8A 88 6C 7B E5\r\n')
|
|
self.pty_w.write(b'$70 : 2F 98 4C 72 5B A4 78 5C 7D 45 46 8C 25 23 BB 82\r\n')
|
|
self.pty_w.write(b'$80 : 60 1E 2F 42 28 38 E3 7D 94 BC CA B4 B1 43 AE 84\r\n')
|
|
self.pty_w.write(b'$90 : 81 88 75 1A 2D 93 7E E8 CF 79 B6 E0 0F 31 AD AA\r\n')
|
|
self.pty_w.write(b'$A0 : 4F 1C 0F 12 55 00 24 13 08 D2 0E C0 00 20 AF 0E\r\n')
|
|
self.pty_w.write(b'$B0 : 40 FF 40 00 B8 00 0F 0F B8 0F 10 B8 0F 14 10 FC\r\n')
|
|
self.pty_w.write(b'$C0 : 08 E8 D8 3F 33 20 D9 50 8F 1A A2 FF CC D4 2E 3A\r\n')
|
|
self.pty_w.write(b'$D0 : 12 22 B7 23 2F 8F 64 47 99 21 6C D8 B4 B0 C3 51\r\n')
|
|
self.pty_w.write(b'$E0 : 90 E8 67 5C 18 5E AB 2D BD 71 52 0F 96 6F DA 56\r\n')
|
|
self.pty_w.write(b'$F0 : 1D E1 08 42 C2 2D 3B C7 87 23 88 E3 1A 95 F1 EE\r\n')
|
|
self.flush_w()
|
|
|
|
def tch_Qmaxchan(self):
|
|
""" Set channels number. """
|
|
self.pty_write(b'Channels number (00 to 99) : ')
|
|
self.MaxChan = int(pty_r.read(2).decode('ascii'))
|
|
# This command gets an extra CRLF ??!
|
|
self.pty_write(b'\r\n')
|
|
|
|
def tch_Peditchannel(self):
|
|
""" Edit/Add channel """
|
|
# TODO keep edited channel content in the class
|
|
self.pty_write(b'Channel to set : ')
|
|
chan_num = int(pty_r.read(2).decode('ascii'))
|
|
self.pty_write(b'\r\n')
|
|
|
|
# NB: here prompts end with '$'
|
|
self.pty_write(b'PLL value to load : $')
|
|
pll_freq_value = int(pty_r.read(4).decode('ascii'), 16)
|
|
self.pty_write(b'\r\n')
|
|
|
|
self.pty_write(b'Channel state : $')
|
|
chan_state = int(pty_r.read(2).decode('ascii'), 16)
|
|
self.pty_write(b'\r\n')
|
|
|
|
# for higher channels, allow to test this scenario
|
|
if chan_num > self.MaxChan and chan_num != 99:
|
|
self.pty_write(b'This channel number doesn\'t exist. Add new channel (Y/N) ? ')
|
|
# response "Y/N"
|
|
yesno = pty_r.read(1).decode('ascii')
|
|
self.pty_write(b'\r\n')
|
|
|
|
def tch_Estate(self):
|
|
""" Show system state (Mode-Chan-Chanstate-Sql-Vol-Lock-RX freq-TX freq-RSSI) (FW V5)"""
|
|
#self.pty_write(b'16000C0010007970802018')
|
|
rssi=24
|
|
self.pty_write('{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:04X}{:04X}{:02X}'.format(
|
|
self.ModeByte,
|
|
self.ChanNum,
|
|
self.ChanState,
|
|
self.Squelch, # low nibble
|
|
self.Volume,
|
|
self.LockByte,
|
|
self.RxPLL,
|
|
self.TxPLL,
|
|
rssi
|
|
).encode())
|
|
|
|
def tch_Chanlist(self):
|
|
""" Print channels list """
|
|
self.pty_write(b'Channels list :\r\n00 : 8464 84\r\n01 : 81B0 00\r\n02 : 8464 0C')
|
|
|
|
def tch_Rfreq(self):
|
|
""" Set synthetiser frequencies """
|
|
self.pty_write(b'RX frequency : ')
|
|
self.RxPLL = int(pty_r.read(4).decode('ascii'), 16)
|
|
|
|
self.pty_write(b'\r\nTX frequency : ')
|
|
self.TxPLL = int(pty_r.read(4).decode('ascii'), 16)
|
|
|
|
def tch_diese(self):
|
|
""" Ping """
|
|
self.pty_write(b'!')
|
|
|
|
def exec_cmd(self,cmd):
|
|
default='*'
|
|
self.mdict.get(cmd.upper(),default)() # get() method returns the function matching the argument
|
|
|
|
self.pty_write(b'\r\n>')
|
|
|
|
def flush_w(self):
|
|
self.pty_w.flush()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
master, slave = pty.openpty()
|
|
s_name = os.ttyname(slave)
|
|
m_name = os.ttyname(master)
|
|
pty_r = os.fdopen(master, "rb")
|
|
pty_w = os.fdopen(master, "wb")
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("-l", "--symlink",
|
|
help="make a symlink with the opened pty",
|
|
type=str,
|
|
dest='symlink')
|
|
options = parser.parse_args()
|
|
|
|
print ("Slave name ", s_name)
|
|
|
|
# Create a symlink
|
|
if options.symlink:
|
|
if os.access(options.symlink, os.F_OK, follow_symlinks=False):
|
|
os.remove(options.symlink)
|
|
os.symlink(s_name, options.symlink)
|
|
print ("Name symlinked ", options.symlink)
|
|
|
|
|
|
prm80 = Prm80Simul(pty_r, pty_w)
|
|
|
|
while True:
|
|
cmd = pty_r.read(1).decode('ascii')
|
|
print ("Received command ["+cmd+"]")
|
|
|
|
prm80.exec_cmd(cmd)
|
|
|
|
print ("Restart mainloop")
|
|
|