Skip to content

Commit

Permalink
Merge pull request #124 from hmatuschek/d578uv
Browse files Browse the repository at this point in the history
Imeplemnted AnyTone AT-D578UV support
  • Loading branch information
hmatuschek committed Jul 12, 2021
2 parents 069a61e + db0fdfd commit 05076e1
Show file tree
Hide file tree
Showing 25 changed files with 2,048 additions and 233 deletions.
54 changes: 53 additions & 1 deletion cli/decodecodeplug.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
#include "rd5r_codeplug.hh"
#include "gd77_codeplug.hh"
#include "opengd77_codeplug.hh"
#include "d868uv_codeplug.hh"
#include "d878uv_codeplug.hh"

#include "d878uv2_codeplug.hh"
#include "d578uv_codeplug.hh"

int decodeCodeplug(QCommandLineParser &parser, QCoreApplication &app) {
Q_UNUSED(app);
Expand Down Expand Up @@ -146,6 +148,31 @@ int decodeCodeplug(QCommandLineParser &parser, QCoreApplication &app) {
return -1;
}

if (3 <= parser.positionalArguments().size()) {
QFile outfile(parser.positionalArguments().at(2));
if (! outfile.open(QIODevice::WriteOnly)) {
logError() << "Cannot write CSV codeplug file '" << outfile.fileName() << "':" << outfile.errorString();
return -1;
}
QTextStream stream(&outfile);
config.writeCSV(stream, errorMessage);
outfile.close();
} else {
QTextStream stream(stdout);
config.writeCSV(stream, errorMessage);
}
} else if ("d878uv2"==parser.value("radio").toLower()) {
D878UV2Codeplug codeplug;
if (! codeplug.read(filename)) {
logError() << "Cannot decode binary codeplug file '" << filename << "':" << codeplug.errorMessage();
return -1;
}
Config config;
if (! codeplug.decode(&config)) {
logError() << "Cannot decode binary codeplug file '" << filename << "':" << codeplug.errorMessage();
return -1;
}

if (3 <= parser.positionalArguments().size()) {
QFile outfile(parser.positionalArguments().at(2));
if (! outfile.open(QIODevice::WriteOnly)) {
Expand All @@ -171,6 +198,31 @@ int decodeCodeplug(QCommandLineParser &parser, QCoreApplication &app) {
return -1;
}

if (3 <= parser.positionalArguments().size()) {
QFile outfile(parser.positionalArguments().at(2));
if (! outfile.open(QIODevice::WriteOnly)) {
logError() << "Cannot write CSV codeplug file '" << outfile.fileName() << "':" << outfile.errorString();
return -1;
}
QTextStream stream(&outfile);
config.writeCSV(stream, errorMessage);
outfile.close();
} else {
QTextStream stream(stdout);
config.writeCSV(stream, errorMessage);
}
} else if ("d578uv"==parser.value("radio").toLower()) {
D578UVCodeplug codeplug;
if (! codeplug.read(filename)) {
logError() << "Cannot decode binary codeplug file '" << filename << "':" << codeplug.errorMessage();
return -1;
}
Config config;
if (! codeplug.decode(&config)) {
logError() << "Cannot decode binary codeplug file '" << filename << "':" << codeplug.errorMessage();
return -1;
}

if (3 <= parser.positionalArguments().size()) {
QFile outfile(parser.positionalArguments().at(2));
if (! outfile.open(QIODevice::WriteOnly)) {
Expand Down
9 changes: 9 additions & 0 deletions cli/encodecallsigndb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "uv390_callsigndb.hh"
#include "opengd77_callsigndb.hh"
#include "d868uv_callsigndb.hh"
#include "d878uv2_callsigndb.hh"
#include "crc32.hh"


Expand Down Expand Up @@ -89,6 +90,14 @@ int encodeCallsignDB(QCommandLineParser &parser, QCoreApplication &app) {
<< "': " << db.errorMessage();
return -1;
}
} else if (("d878uv2"==parser.value("radio").toLower()) || ("d578uv"==parser.value("radio").toLower()) ){
D878UV2CallsignDB db;
db.encode(&userdb, selection);
if (! db.write(parser.positionalArguments().at(1))) {
logError() << "Cannot write output call-sign DB file '" << parser.positionalArguments().at(1)
<< "': " << db.errorMessage();
return -1;
}
} else {
logError() << "Cannot encode calls-sign DB: Unknown radio '" << parser.value("radio") << "'.";
return -1;
Expand Down
41 changes: 41 additions & 0 deletions cli/encodecodeplug.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
#include "rd5r_codeplug.hh"
#include "gd77_codeplug.hh"
#include "opengd77_codeplug.hh"
#include "d868uv_codeplug.hh"
#include "d878uv_codeplug.hh"
#include "d878uv2_codeplug.hh"
#include "d578uv_codeplug.hh"
#include "crc32.hh"


Expand Down Expand Up @@ -119,6 +122,25 @@ int encodeCodeplug(QCommandLineParser &parser, QCoreApplication &app) {
<< "': " << codeplug.errorMessage();
return -1;
}
} else if ("d878uv2"==parser.value("radio").toLower()) {
Config config;
QString errorMessage;
QTextStream stream(&infile);
if (! config.readCSV(stream, errorMessage)) {
logError() << "Cannot parse CSV codeplug '" << infile.fileName() << "': " << errorMessage;
return -1;
}
D878UV2Codeplug codeplug;
codeplug.setBitmaps(&config);
codeplug.allocateUpdated();
codeplug.allocateForEncoding();
codeplug.encode(&config, flags);
codeplug.image(0).sort();
if (! codeplug.write(parser.positionalArguments().at(2))) {
logError() << "Cannot write output codeplug file '" << parser.positionalArguments().at(1)
<< "': " << codeplug.errorMessage();
return -1;
}
} else if ("d868uv"==parser.value("radio").toLower()) {
Config config;
QString errorMessage;
Expand All @@ -138,6 +160,25 @@ int encodeCodeplug(QCommandLineParser &parser, QCoreApplication &app) {
<< "': " << codeplug.errorMessage();
return -1;
}
} else if ("d578uv"==parser.value("radio").toLower()) {
Config config;
QString errorMessage;
QTextStream stream(&infile);
if (! config.readCSV(stream, errorMessage)) {
logError() << "Cannot parse CSV codeplug '" << infile.fileName() << "': " << errorMessage;
return -1;
}
D578UVCodeplug codeplug;
codeplug.setBitmaps(&config);
codeplug.allocateUpdated();
codeplug.allocateForEncoding();
codeplug.encode(&config, flags);
codeplug.image(0).sort();
if (! codeplug.write(parser.positionalArguments().at(2))) {
logError() << "Cannot write output codeplug file '" << parser.positionalArguments().at(1)
<< "': " << codeplug.errorMessage();
return -1;
}
} else {
logError() << "Cannot encode codeplug: Unknown radio '" << parser.value("radio") << "'.";
return -1;
Expand Down
Binary file added doc/fig/d578uv.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
186 changes: 186 additions & 0 deletions doc/reveng/anytone/d578uv/at_d578uv_emulator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
#!/usr/bin/env python3
#
# Emulate anytone d878uv radio to customer programming software.
# Send intercepted data stream over network to server script for further investigation.
#
# This script connects to a virtual com port COM26 which is connected via a virtual
# null modem cable to the virtual com port COM18 which is used by the programming software.
# This virtual ports and cable can be provided by the COM0COM tool.
#
# Linux users can use
# socat -d -d pty,raw,echo=0,b4000000 pty,raw,echo=0,b4000000
# for emulating a virtual null modem cable.

import serial
import time
import sys
import struct


# config
filebase = 'codeplug'
filecount = 0
comport = 'COM6' # connected to COM18 with com0com. use COM18 in CPS


def hexDump(s):
h = " ".join(map("{:02x}".format, s))
t = ""
for i in range(len(s)):
c = s[i]
if c>=0x20 and c<0x7f:
t += chr(c)
else:
t += "."
return( h + " | " + t)

# parameters?
if len(sys.argv) == 3:
filebase = sys.argv[1]
comport = sys.argv[2]
elif len(sys.argv) == 2:
filebase = sys.argv[1]
elif len(sys.argv) >3:
print("Usage: " + sys.argv[0] + ' filebase [comport]')
exit()



# open serial port
serialPort = None

try:
print("Trying comport " + comport)
serialPort = serial.Serial(port = comport, baudrate=4000000, bytesize=8, timeout=1, stopbits=serial.STOPBITS_ONE) # 115200 921600 4000000
except (err):
print('ERR: Could not open port ' + comport)
print("Usage: " + sys.argv[0] + ' servername [comport]: ' + err)
exit()


out = None
nextaddr = None

# wait for data

try:

while 1:

command = ''

command = serialPort.read()
while serialPort.in_waiting > 0:
command += serialPort.read()

# respond to command on com port
if ( len(command) == 0 ):
pass

elif ( command == b'PROGRAM'):
print("Program session requested.")
resp = b'QX\x06'
serialPort.write(resp)
filename = "{}_{:04}.hex".format(filebase, filecount)
out = open(filename, "w")
nextaddr = None

elif ( command == b'\x02' ):
print("Device info requested.")
resp = b'ID578UV\x00\x00V110\x00\x00\x06'
serialPort.write(resp)

elif ( command == b'R\x02\xfa\x00\x20\x10' ):
print("Read special memory request.")
resp = b'W\x02\xfa\x00\x20\x10\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x24\x06'
serialPort.write(resp)

elif ( command[0:4] == b'R\x02\xfa\x00' and command[5] == 16 ):
# 0x02fa00..
print("Read local information.")

resp = b'W\x02\xfa\x00' + bytes([command[4]]) + b'\x10'

if ( command[4] == 0x00 ):
resp += b'\x00\x00\x00\x03\x01\x01\x01\x00\x00\x01\x01\x20\x20\x20\x20\xff'

elif ( command[4] == 0x10 ): # Radio Type
resp += b'\x44\x38\x37\x38\x55\x56\x00\x01\x00\xff\xff\xff\xff\xff\xff\xff'

elif ( command[4] == 0x30 ): # Serial Number
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
elif ( command[4] == 0x40 ): # Production Date
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
elif ( command[4] == 0x50 ): # Manucfacture Code
resp += b'\x31\x32\x33\x34\x35\x36\x37\x38\xff\xff\xff\xff\xff\xff\xff\xff'
elif ( command[4] == 0x60 ): # Maintained Date
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
elif ( command[4] == 0x70 ): # Dealer Code
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
elif ( command[4] == 0x80 ): # Stock Date
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
elif ( command[4] == 0x90 ): # Sell Date
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
elif ( command[4] == 0xa0 ): # Seller
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

elif ( command[4] == 0xb0 ): # Maintained Description
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
elif ( command[4] == 0xc0 ):
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
elif ( command[4] == 0xd0 ):
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
elif ( command[4] == 0xe0 ):
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
elif ( command[4] == 0xf0 ):
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

else:
resp += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

resp = resp + bytes( [sum(resp[1:]) & 0xff] ) + b'\x06'
#print(resp.hex())
serialPort.write(resp)

elif ( command[0] == ord('W') ) :
resp = b'\x06' # just ack
serialPort.write(resp)
res = struct.unpack(">cIB16sBB", command)
addr = res[1]
if nextaddr != addr:
out.write((8+3+16*3+16+2)*"-" + "\n")
out.write("{:08X} : {}\n".format(addr, hexDump(res[3])))
nextaddr = addr+16

elif ( command == b'END' ):
print("End session.")
resp = b'\x06' # just ack
serialPort.write(resp)
out.close()
filecount += 1

elif ( command == b'UPDATE' ):
# for firmware update the device has to be switched on while pressing PF3 (blue button on top) and PTT keys
print("Start Firmware Update. Only useful if device is in update receiving mode. (Switch on while pressing PF3 (blue button on top) and PTT keys)")
resp = b'\x06' # just ack
serialPort.write(resp)

elif ( command == b'\x18' ):
print("Firmware Update Send Complete. Switch device on while pressing PF2 (top left side) and PTT keys to start installer.")
resp = b'\x06' # just ack
serialPort.write(resp)

elif ( command[0] == 0x01 ):
print("Firmware data.")
resp = b'\x06' # just ack
serialPort.write(resp)
else:
#print("> " + str(command))
pass



finally:
print('QRT')
serialPort.close()

12 changes: 9 additions & 3 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ SET(libdmrconf_SOURCES
csvreader.cc dfufile.cc repeaterdatabase.cc userdatabase.cc logger.cc
config.cc contact.cc rxgrouplist.cc channel.cc zone.cc scanlist.cc gpssystem.cc codeplug.cc
roaming.cc callsigndb.cc talkgroupdatabase.cc radioid.cc
rd5r.cc rd5r_codeplug.cc uv390.cc uv390_codeplug.cc uv390_callsigndb.cc gd77.cc gd77_codeplug.cc
rd5r.cc rd5r_codeplug.cc
uv390.cc uv390_codeplug.cc uv390_callsigndb.cc
gd77.cc gd77_codeplug.cc
opengd77.cc opengd77_interface.cc opengd77_codeplug.cc opengd77_callsigndb.cc
anytone_interface.cc anytone_radio.cc anytone_codeplug.cc
d868uv.cc d868uv_codeplug.cc d868uv_callsigndb.cc d878uv.cc d878uv_codeplug.cc
d868uv.cc d868uv_codeplug.cc d868uv_callsigndb.cc
d878uv.cc d878uv_codeplug.cc
d578uv.cc d578uv_codeplug.cc
d878uv2.cc d878uv2_codeplug.cc d878uv2_callsigndb.cc)
SET(libdmrconf_MOC_HEADERS
radio.hh ${hid_HEADERS} hid_interface.hh dfu_libusb.hh usbserial.hh
Expand All @@ -25,7 +29,9 @@ SET(libdmrconf_MOC_HEADERS
rd5r.hh rd5r_codeplug.hh uv390.hh uv390_codeplug.hh uv390_callsigndb.hh gd77.hh gd77_codeplug.hh
opengd77.hh opengd77_interface.hh opengd77_codeplug.hh opengd77_callsigndb.hh
anytone_interface.hh anytone_radio.hh anytone_codeplug.hh
d868uv.hh d868uv_codeplug.hh d868uv_callsigndb.hh d878uv.hh d878uv_codeplug.hh
d868uv.hh d868uv_codeplug.hh d868uv_callsigndb.hh
d878uv.hh d878uv_codeplug.hh
d578uv.hh d578uv_codeplug.hh
d878uv2.hh d878uv2_codeplug.hh d878uv2_callsigndb.hh)
SET(libdmrconf_HEADERS libdmrconf.hh radiointerface.hh
utils.hh crc32.hh csvwriter.hh signaling.hh codeplugcontext.hh addressmap.hh)
Expand Down
Loading

0 comments on commit 05076e1

Please sign in to comment.