first commit
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
"""
|
||||
This module contains experimental code for using the (extend) UHSDR API
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
__author__ = "DB4PLE"
|
||||
__copyright__ = "Copyright 2018, UHSDR project"
|
||||
__credits__ = ["DB4PLE"]
|
||||
__license__ = "GPLv3"
|
||||
__status__ = "Prototype"
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
class CatCmdFt817:
|
||||
"""
|
||||
List of used/supported FT817 CAT command codes
|
||||
This list includes the officially known FT817 codes (including the "undocumented" ones)
|
||||
"""
|
||||
READ_EEPROM = 0xBB
|
||||
WRITE_EEPROM = 0xBC
|
||||
PTT_STATE = 0xF7
|
||||
|
||||
class CatCmd(CatCmdFt817):
|
||||
"""
|
||||
This list extends the FT817 by the special UHSDR codes implemented only in the UHSDR dialect
|
||||
of FT817 CAT (which must be different from the FT817 ones)
|
||||
"""
|
||||
|
||||
UHSDR_ID = 0x42
|
||||
"""
|
||||
this will return the bytes ['U', 'H' , 'S', 'D', 'R' ] and is used to identify an UHSDR with high enough firmware level
|
||||
"""
|
||||
|
||||
class UhsdrConfigIndex:
|
||||
"""
|
||||
this is a completely incomplete list of config value indicies as used in the UHSDR firmware
|
||||
we list only the absolutely necessary ids here for the moment (those must never change in different firmware versions, would break this code)
|
||||
"""
|
||||
VER_MAJOR = 176
|
||||
VER_MINOR = 310
|
||||
VER_BUILD = 171
|
||||
NUMBER_OF_ENTRIES = 407
|
||||
|
||||
def eprint(*args, **kwargs):
|
||||
"""
|
||||
a small function to print to stderr, used for error and logging messages
|
||||
"""
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
class catSerial:
|
||||
"""
|
||||
Low Level FT817 CAT protocol handling on a serial communication
|
||||
"""
|
||||
def __init__(self, comObj):
|
||||
self.comObj = comObj
|
||||
def sendCommand(self, command):
|
||||
bytesWritten = self.comObj.write(command)
|
||||
return bytesWritten == 5
|
||||
|
||||
def readResponse(self,count):
|
||||
response = self.comObj.read(count)
|
||||
return (len(response) == count,response)
|
||||
|
||||
|
||||
class catCommands:
|
||||
"""
|
||||
CAT API: Here we have direct access to CAT API ations, each logical API function has its direct counterpart in this
|
||||
class
|
||||
We do not implement any extra control logic in here, just the call of the API function and returning the response
|
||||
This may be enriched to have all availabl CAT API functions, right now it is just what we need now
|
||||
"""
|
||||
|
||||
def __init__(self, catObj):
|
||||
self.catObj = catObj
|
||||
|
||||
def execute(self, cmd, count):
|
||||
if self.catObj.sendCommand(cmd):
|
||||
ok,res = self.catObj.readResponse(count)
|
||||
return ok,bytearray(res)
|
||||
else:
|
||||
return (False,bytearray([]))
|
||||
|
||||
def readEEPROM(self, addr):
|
||||
cmd = bytearray([ (addr & 0xff00)>>8,addr & 0xff, 0x00, 0x00, CatCmd.READ_EEPROM])
|
||||
ok,res = self.execute(cmd,2)
|
||||
if ok:
|
||||
return res[1] * 256 + res[0]
|
||||
else:
|
||||
return ok
|
||||
|
||||
def readUHSDR(self):
|
||||
cmd = bytearray([ 0x00, 0x00 , 0x00, 0x00, CatCmd.UHSDR_ID])
|
||||
ok,res = self.execute(cmd,5)
|
||||
|
||||
return res == bytearray("UHSDR", 'utf-8')
|
||||
|
||||
def readPttState(self):
|
||||
cmd = bytearray([ 0x00, 0x00 , 0x00, 0x00, CatCmd.PTT_STATE])
|
||||
ok,res = self.execute(cmd,5)
|
||||
|
||||
return res
|
||||
|
||||
def writeEEPROM(self, addr, value16bit):
|
||||
cmd = bytearray([ (addr & 0xff00)>>8,addr & 0xff, (value16bit & 0xff) >> 0, (value16bit & 0xff00) >> 8, CatCmd.WRITE_EEPROM])
|
||||
ok,res = self.execute(cmd,1)
|
||||
return ok
|
||||
|
||||
def readUHSDRConfig(self, index):
|
||||
return self.readEEPROM(index + 0x8000);
|
||||
|
||||
def writeUHSDRConfig(self, index, value):
|
||||
return self.writeEEPROM(index + 0x8000, value);
|
||||
|
||||
|
||||
class UhsdrConfig():
|
||||
"""
|
||||
CONFIG MANAGEMENT: Handling of reading / writing TRX configurations, detection of TRX presence etc.
|
||||
This class represents high-level actions, should involve proper parameter checking etc.
|
||||
"""
|
||||
|
||||
def __init__(self, catObj):
|
||||
self.catObj = catObj
|
||||
|
||||
def getVersion(self):
|
||||
"""
|
||||
return firmware version as integer tuple (major,minor,build)
|
||||
or (False,False,False) if something goes wrong
|
||||
"""
|
||||
return (self.catObj.readUHSDRConfig(UhsdrConfigIndex.VER_MAJOR),
|
||||
self.catObj.readUHSDRConfig(UhsdrConfigIndex.VER_MINOR),
|
||||
self.catObj.readUHSDRConfig(UhsdrConfigIndex.VER_BUILD))
|
||||
|
||||
def isUhsdrConnected(self):
|
||||
"""
|
||||
we test if an UHSDR with extended API is connected by using an identification API call not present
|
||||
on a FT817 or older UHSDR / mcHF firmwares
|
||||
returns: True if a suitable TRX is connected, False otherwise
|
||||
"""
|
||||
return self.catObj.readUHSDR()
|
||||
|
||||
#
|
||||
def getConfigValueCount(self):
|
||||
return self.catObj.readUHSDRConfig(UhsdrConfigIndex.NUMBER_OF_ENTRIES)
|
||||
|
||||
def getValue(self, index):
|
||||
# TODO: do some range checking here
|
||||
return self.catObj.readUHSDRConfig(index)
|
||||
|
||||
def setValue(self, index, value):
|
||||
# TODO: do some range checking here
|
||||
retval = False
|
||||
if self.catObj.writeUHSDRConfig(index, value):
|
||||
retval = value == self.getValue(index)
|
||||
|
||||
return retval
|
||||
|
||||
|
||||
def configToJson(self):
|
||||
"""
|
||||
read the configuration from TRX into a data dictionary
|
||||
|
||||
returns tuple with boolean success state and read data
|
||||
|
||||
right now the returned JSON structure is quite simple
|
||||
we store the version number as list of 3 integers under the 'version' key
|
||||
we store the date and time of backup as UTC under the 'when' key
|
||||
we store each configuration value as addr/value pair under the 'eeprom' key
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
retval = False
|
||||
valList = []
|
||||
self.data = {}
|
||||
self.data['version'] = self.getVersion()
|
||||
self.data['when'] = str(datetime.utcnow())
|
||||
self.data['eeprom'] = []
|
||||
numberOfValues = self.getConfigValueCount()
|
||||
for index in range(numberOfValues):
|
||||
val = self.getValue(index)
|
||||
valList.append(val)
|
||||
self.data['eeprom'].append({ 'addr' : index , 'value' : val })
|
||||
|
||||
retval = all(val is not False for val in valList) or len(valList) != 0
|
||||
return retval,self.data
|
||||
|
||||
def jsonToConfig(self,data):
|
||||
"""
|
||||
write the configuration in passed data dictionary to TRX
|
||||
|
||||
returns tuple with boolean success state and human readable return msg
|
||||
|
||||
data dictionary must conform to format generated by configToJson()
|
||||
"""
|
||||
|
||||
retval = True
|
||||
retmsg = "OK"
|
||||
if data['when'] != None and len(data['version']) == 3 and len(data['eeprom']) == data['eeprom'][UhsdrConfigIndex.NUMBER_OF_ENTRIES]['value']:
|
||||
numberOfValues = data['eeprom'][UhsdrConfigIndex.NUMBER_OF_ENTRIES]['value']
|
||||
# we do not restore index 0 as it contains EEPROM type. This is never changed during configuration backup, too dangerous
|
||||
for index in range(numberOfValues):
|
||||
addr = data['eeprom'][index]['addr']
|
||||
value = data['eeprom'][index]['value']
|
||||
if addr != 0:
|
||||
if self.setValue(addr,value) == False:
|
||||
retmsg = "Restoring value {} at addr {} failed".format(value,addr)
|
||||
retval = False
|
||||
break
|
||||
else:
|
||||
retmsg = "Configuration data failed consistency check"
|
||||
retval = False
|
||||
return retval,retmsg
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
"""
|
||||
This module contains experimental code for using the (extend) UHSDR API
|
||||
and contains a small commandline client for backup and restore of
|
||||
the UHSDR configuration data from/to a TRX
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
__author__ = "DB4PLE"
|
||||
__copyright__ = "Copyright 2018, UHSDR project"
|
||||
__credits__ = ["DB4PLE"]
|
||||
__license__ = "GPLv3"
|
||||
__status__ = "Prototype"
|
||||
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
import uhsdr
|
||||
|
||||
def listUHSDRSerial():
|
||||
"""
|
||||
list serial ports. For new pySerial version we return only the real UHSDR device, otherwise just a list of serial port objects
|
||||
|
||||
returns tupel (Boolean,List) True if UHSDR filter was active, False otherwise, second element is actual list of info for com ports.
|
||||
"""
|
||||
import serial
|
||||
import serial.tools.list_ports;
|
||||
from distutils.version import StrictVersion
|
||||
# UHSDR uses USB VID:PID=0483:5732
|
||||
retval = False,[]
|
||||
|
||||
if StrictVersion(serial.VERSION) >= StrictVersion("3.0"):
|
||||
retval = True,[ comport.device for comport in serial.tools.list_ports.comports() if comport.vid == 0x0483 and comport.pid == 0x5732 ]
|
||||
else:
|
||||
uhsdr.eprint("pySerial version ",serial.VERSION," is too old for detecting UHSDR device, using full serial port list")
|
||||
retval = False,[ vars(comport) for comport in serial.tools.list_ports.comports()]
|
||||
return retval
|
||||
|
||||
def backupRestoreApp():
|
||||
import serial
|
||||
"""
|
||||
Simple command line tool to backup/restore the configuration from/to TRX
|
||||
"""
|
||||
|
||||
if False and "idlelib" in sys.modules:
|
||||
uhsdr.eprint("Running in IDLE GUI, please set parameters directly in script, search for #change ")
|
||||
#change parameters here
|
||||
sys.argv = [sys.argv[0],'--port','15','-b']
|
||||
#no more changes below
|
||||
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-b","--backup", help="backup the UHSDR TRX configuration to file", action="store_true")
|
||||
parser.add_argument("-r","--restore", help="restore the UHSDR TRX configuration from file", action="store_true")
|
||||
parser.add_argument("-p","--port", help="UHSDR serial port either by number (COM<num> in Windows, Linux /dev/ttyACM<num>) or full device name ( e.g. '/dev/cu.modemABCD', all operating systems )", type=str, default="undefined serial port name")
|
||||
parser.add_argument("-f","--file", help="filename to backup to/restore from, if not defined 'uhsdr_config.json' is used", type=str, default="uhsdr_config.json")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("This program backups and restores the configuration of UHSDR TRX via USB")
|
||||
|
||||
if args.port == -1:
|
||||
print("Use -h to get more information")
|
||||
print("")
|
||||
isFiltered, uhsdrDeviceList = listUHSDRSerial()
|
||||
if uhsdrDeviceList == []:
|
||||
uhsdr.eprint("No suitable serial port detected, make sure to connect UHSDR TRX")
|
||||
else:
|
||||
if isFiltered:
|
||||
uhsdr.eprint("List of detected UHSDR TRX")
|
||||
else:
|
||||
uhsdr.eprint("List of detected serial ports:")
|
||||
uhsdr.eprint(uhsdrDeviceList)
|
||||
uhsdr.eprint("Please specify valid UHSDR TRX serial port number(!) with -p / --port")
|
||||
else:
|
||||
|
||||
try:
|
||||
int(args.port);
|
||||
comPort= ("COM" if os.name == "nt" else "/dev/ttyACM") + str(args.port)
|
||||
except:
|
||||
comPort = args.port
|
||||
|
||||
uhsdr.eprint("Opening serial port ",comPort,":")
|
||||
try:
|
||||
mySer = serial.Serial(comPort, 38400, timeout=0.500, parity=serial.PARITY_NONE)
|
||||
except:
|
||||
uhsdr.eprint("... failed")
|
||||
raise
|
||||
uhsdr.eprint("... okay")
|
||||
myCom = uhsdr.catSerial(mySer)
|
||||
myCAT = uhsdr.catCommands(myCom)
|
||||
myUHSDR = uhsdr.UhsdrConfig(myCAT)
|
||||
|
||||
uhsdr.eprint("Detecting if UHSDR is connected: ")
|
||||
if myUHSDR.isUhsdrConnected():
|
||||
uhsdr.eprint("... yes")
|
||||
version = myUHSDR.getVersion()
|
||||
print("Detected UHSDR Firmware Version", version)
|
||||
if args.backup:
|
||||
uhsdr.eprint("Reading configuration from UHSDR TRX:")
|
||||
ok,data = myUHSDR.configToJson()
|
||||
if ok:
|
||||
uhsdr.eprint("... success. Now saving in file:")
|
||||
with open(args.file, 'w') as outfile:
|
||||
try:
|
||||
json.dump(data, outfile, indent=4)
|
||||
uhsdr.eprint("... saved to " + args.file + " file")
|
||||
except:
|
||||
uhsdr.eprint("... failed to save to " + args.file + " file")
|
||||
raise
|
||||
outfile.close()
|
||||
else:
|
||||
uhsdr.eprint("... could not read data sucessfully")
|
||||
elif args.restore:
|
||||
with open(args.file, 'r') as infile:
|
||||
data = json.load(infile)
|
||||
uhsdr.eprint("Sending configuration to UHSDR TRX:")
|
||||
ok,msg = myUHSDR.jsonToConfig(data)
|
||||
if ok:
|
||||
uhsdr.eprint("... restored all data from " + args.file + " file")
|
||||
print("ACTIVATING RESTORED CONFIGURATION: Switch off TRX WITHOUT saving configuration -> Press Band+ and Power buttons")
|
||||
print("")
|
||||
print("ATTENTION: In case of an accidential restore:")
|
||||
print("Press Power button to save running configuration, overwriting the restored values in configuration memory.")
|
||||
else:
|
||||
uhsdr.eprint("... problem occured: ",msg)
|
||||
|
||||
infile.close()
|
||||
else:
|
||||
uhsdr.eprint("... could not find a connected UHSDR with extended CAT commands (required)")
|
||||
|
||||
mySer.close()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
backupRestoreApp()
|
||||
Reference in New Issue
Block a user