Sync edgetx to Gitea

This commit is contained in:
2026-08-03 16:37:20 +08:00
commit 14eb5a3fb9
5364 changed files with 2835009 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env python
from __future__ import print_function
import sys
import glob
def addLine(filename, newline, after):
print(filename, newline)
with open(filename, 'r') as f:
lines = f.readlines()
for i, line in enumerate(lines):
if after in line:
lines.insert(i + 1, newline + '\n')
break
with open(filename, 'w') as f:
f.writelines(lines)
def modifyTranslations(constant, translation, after):
for filename in glob.glob('translations/*.h'):
newline = "#define " + constant + " " * max(1, 31 - len(constant)) + '"' + translation + '"'
addLine(filename, newline, after + " ")
def modifyDeclaration(constant, after):
newline = "extern const char S" + constant + "[];"
filename = "translations.h"
addLine(filename, newline, after + "[];")
def modifyDefinition(constant, after):
newline = "const char S" + constant + "[] = " + constant + ";"
filename = "translations.cpp"
addLine(filename, newline, after + "[] ")
after = sys.argv[-1]
for arg in sys.argv[1:-1]:
constant, translation = arg.split("=")
modifyTranslations(constant, translation, after)
modifyDeclaration(constant, after)
modifyDefinition(constant, after)
after = constant
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env python
from __future__ import division
import sys
filename = sys.argv[1]
fileout = sys.argv[2]
# Read entire file
with open(filename, "rb") as fr:
sts = fr.read()
# Parse into chunks of 16 bytes
sts = [sts[i:i+16] for i in range(0, len(sts), 16)]
with open(fileout, "w") as fw:
for st in sts:
for b in st:
fw.write("0x%02x," % (ord(b) if isinstance(b, str) else b))
fw.write("\n")
fw.write("\n")
+291
View File
@@ -0,0 +1,291 @@
#!/usr/bin/env python
from __future__ import print_function
import os
import sys
import subprocess
import shutil
from fwoptions import *
# Error codes
FIRMWARE_SIZE_TOO_BIG = 1
COMPILATION_ERROR = 4
INVALID_FIRMWARE = 5
INVALID_BOARD = 6
INVALID_LANGUAGE = 7
def build_target(target, path, cmake_options):
srcdir = os.path.dirname(os.path.realpath(__file__)) + "/../.."
outpath = path + ".out"
errpath = path + ".err"
# Launch CMake
cmd = ["cmake"]
for option, value in cmake_options.items():
cmd.append("-D%s=%s" % (option, value))
if "EDGETX_VERSION_SUFFIX" in os.environ:
suffix = os.environ["EDGETX_VERSION_SUFFIX"]
cmd.append('-DVERSION_SUFFIX="%s"' % suffix)
# if not suffix.startswith("rel"):
# cmd.append('-DTEST_BUILD_WARNING=YES')
cmd.append(srcdir)
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, error = proc.communicate()
if proc.returncode == 0:
open(outpath, "a").write("\n".join(cmd) + output.decode("utf-8") + error.decode("utf-8"))
else:
open(errpath, "w").write(output.decode("utf-8") + error.decode("utf-8"))
return COMPILATION_ERROR
# Launch make
cmd = ["make", "-j3", target]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, error = proc.communicate()
if proc.returncode == 0:
open(outpath, "a").write(output.decode("utf-8") + error.decode("utf-8"))
else:
open(errpath, "w").write(output.decode("utf-8") + error.decode("utf-8"))
return COMPILATION_ERROR
return 0
def main():
if len(sys.argv) != 3:
return INVALID_FIRMWARE
target = sys.argv[1]
directory, filename = os.path.split(sys.argv[2])
root, ext = os.path.splitext(filename)
options = root.split("-")
if len(options) < 2 or options[0] != "opentx":
return INVALID_FIRMWARE
optcount = 1
cmake_options = {}
board_name = options[optcount]
if board_name == "x9lite":
cmake_options["PCB"] = "X9LITE"
firmware_options = options_taranis_x9lite
maxsize = 65536 * 8
elif board_name == "x9lites":
cmake_options["PCB"] = "X9LITES"
firmware_options = options_taranis_x9lite
maxsize = 65536 * 8
elif options[optcount] == "x7":
cmake_options["PCB"] = "X7"
firmware_options = options_taranis_x7
maxsize = 65536 * 8
elif options[optcount] == "x7access":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "ACCESS"
firmware_options = options_taranis_x7
maxsize = 65536 * 8
elif board_name == "xlite":
cmake_options["PCB"] = "XLITE"
firmware_options = options_taranis_xlite
maxsize = 65536 * 8
elif board_name == "xlites":
cmake_options["PCB"] = "XLITES"
firmware_options = options_taranis_xlites
maxsize = 65536 * 8
elif board_name == "x9d":
cmake_options["PCB"] = "X9D"
firmware_options = options_taranis_x9d
maxsize = 65536 * 8
elif board_name == "x9d+":
cmake_options["PCB"] = "X9D+"
firmware_options = options_taranis_x9dp
maxsize = 65536 * 8
elif board_name == "x9d+2019":
cmake_options["PCB"] = "X9D+"
cmake_options["PCBREV"] = "2019"
firmware_options = options_taranis_x9dp
maxsize = 65536 * 8
elif board_name == "x9e":
cmake_options["PCB"] = "X9E"
firmware_options = options_taranis_x9e
maxsize = 65536 * 8
elif board_name == "x10":
cmake_options["PCB"] = "X10"
firmware_options = options_horus_x10
maxsize = 2 * 1024 * 1024
elif board_name == "x10express":
cmake_options["PCB"] = "X10"
cmake_options["PCBREV"] = "EXPRESS"
firmware_options = options_horus_x10
maxsize = 2 * 1024 * 1024
elif board_name == "x12s":
cmake_options["PCB"] = "X12S"
firmware_options = options_horus_x12s
maxsize = 2 * 1024 * 1024
elif board_name == "lr3pro":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "LR3PRO"
firmware_options = options_betafpv_lr3pro
maxsize = 65536 * 8
elif board_name == "tlite":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "TLITE"
firmware_options = options_jumper_tlite
maxsize = 65536 * 8
elif board_name == "t12":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "T12"
firmware_options = options_jumper_t12
maxsize = 65536 * 8
elif board_name == "tx12":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "TX12"
firmware_options = options_radiomaster_tx12
maxsize = 65536 * 8
elif board_name == "zorro":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "ZORRO"
firmware_options = options_radiomaster_zorro
maxsize = 65536 * 8
elif board_name == "boxer":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "BOXER"
firmware_options = options_radiomaster_boxer
maxsize = 65536 * 8 * 2
elif board_name == "pocket":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "POCKET"
firmware_options = options_radiomaster_pocket
maxsize = 65536 * 8
elif board_name == "mt12":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "MT12"
firmware_options = options_radiomaster_mt12
maxsize = 65536 * 8 * 2
elif board_name == "gx12":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "GX12"
firmware_options = options_radiomaster_gx12
maxsize = 65536 * 8 * 2
elif board_name == "t8":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "T8"
firmware_options = options_radiomaster_t8
maxsize = 65536 * 8
elif board_name == "t15":
cmake_options["PCB"] = "X10"
cmake_options["PCBREV"] = "T15"
firmware_options = options_jumper_t16
maxsize = 2 * 1024 * 1024
elif board_name == "t16":
cmake_options["PCB"] = "X10"
cmake_options["PCBREV"] = "T16"
firmware_options = options_jumper_t16
maxsize = 2 * 1024 * 1024
elif board_name == "t18":
cmake_options["PCB"] = "X10"
cmake_options["PCBREV"] = "T18"
firmware_options = options_jumper_t18
maxsize = 2 * 1024 * 1024
elif board_name == "tx16s":
cmake_options["PCB"] = "X10"
cmake_options["PCBREV"] = "TX16S"
firmware_options = options_radiomaster_tx16s
maxsize = 2 * 1024 * 1024
elif board_name == "f16":
cmake_options["PCB"] = "X10"
cmake_options["PCBREV"] = "F16"
firmware_options = options_fatfish_f16
maxsize = 2 * 1024 * 1024
elif board_name == "v12":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "V12"
firmware_options = options_helloradiosky_v12
maxsize = 65536 * 8 * 2
elif board_name == "v14":
cmake_options["PCB"] = "X7"
cmake_options["PCBREV"] = "V14"
firmware_options = options_helloradiosky_v14
maxsize = 65536 * 8 * 2
elif board_name == "v16":
cmake_options["PCB"] = "X10"
cmake_options["PCBREV"] = "V16"
firmware_options = options_helloradiosky_v16
maxsize = 2 * 1024 * 1024
else:
return INVALID_BOARD
if target == "firmware":
binary = "firmware.bin"
ext = ".bin"
filename = "opentx"
else:
return INVALID_BOARD
filename += "-" + board_name
optcount += 1
# The firmware options
for opt, values in firmware_options.items():
found = False
for i in range(optcount, len(options)):
if options[i] == opt:
found = True
break
if not isinstance(values, list):
values = [values]
for name, value1, value2 in values:
if found:
value = value1
filename += "-" + opt
else:
value = value2
if value is not None:
cmake_options[name] = value
# The firmware display language
language = ""
for key in languages:
if key == options[-1]:
language = key
if not language:
return INVALID_LANGUAGE
cmake_options["TRANSLATIONS"] = language.upper()
filename += "-" + language + ext
path = os.path.join(directory, filename)
errpath = path + ".err"
if os.path.isfile(errpath):
print(filename)
return COMPILATION_ERROR
if os.path.isfile(path):
print(filename)
return 0
result = build_target(target, path, cmake_options)
if result != 0:
print(filename)
return result
if target == "firmware":
# Check binary size
size = os.stat(binary).st_size
if size > maxsize:
return FIRMWARE_SIZE_TOO_BIG
# Copy binary to the binaries directory
shutil.move(binary, path)
print(filename)
return 0
if __name__ == "__main__":
exit(main())
+9
View File
@@ -0,0 +1,9 @@
#!/usr/bin/env python
import glob
import os
for f in glob.glob("*.bmp") + glob.glob("*.BMP"):
t = f.title()
if f != t:
os.rename(f, t)
+58
View File
@@ -0,0 +1,58 @@
#!/bin/env python
from __future__ import print_function
SIGN_BIT = (0x80) # Sign bit for a A-law byte.
QUANT_MASK = (0xf) # Quantization field mask.
SEG_SHIFT = (4) # Left shift for segment number.
SEG_MASK = (0x70) # Segment field mask.
BIAS = (0x84) # Bias for linear code.
def alaw2linear(a_val):
a_val ^= 0x55
t = a_val & QUANT_MASK
seg = (a_val & SEG_MASK) >> SEG_SHIFT
if (seg):
t = (t + t + 1 + 32) << (seg + 2)
else:
t = (t + t + 1) << 3
if a_val & SIGN_BIT:
return t
else:
return -t
def ulaw2linear(u_val):
# Complement to obtain normal u-law value.
u_val = ~u_val
# Extract and bias the quantization bits. Then
# shift up by the segment number and subtract out the bias.
t = ((u_val & QUANT_MASK) << 3) + BIAS
t <<= (u_val & SEG_MASK) >> SEG_SHIFT
if u_val & SIGN_BIT:
return (BIAS - t)
else:
return (t - BIAS)
def pcmTable(fn):
result = []
for i in range(256):
result.append(fn(i))
return result
def tableToString(name, table):
result = 'const int16_t ' + name + '[256] = { '
result += ', '.join(str(i) for i in table)
result += ' };'
return result
print(tableToString('alawTable', pcmTable(alaw2linear)))
print(tableToString('ulawTable', pcmTable(ulaw2linear)))
+403
View File
@@ -0,0 +1,403 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This program parses CRSF capture data
from __future__ import division, print_function
import sys, struct
import math
import argparse
lineNumber = 0
timeData = 0
prevTimeData = 0
crossfire_types = [
"UINT8",
"INT8",
"UINT16",
"INT16",
"UINT32",
"INT32",
"UINT64",
"INT64",
"FLOAT",
"TEXT_SELECTION",
"STRING",
"FOLDER",
"INFO",
"COMMAND",
"VTX",
]
cmd_status = [
"READY",
"START",
"PROGRESS",
"CONFIRMATION_NEEDED",
"CONFIRM",
"CANCEL",
"POLL",
]
def dump(data, maxLen=None):
if maxLen and len(data) > maxLen:
data = data[:maxLen]
return " ".join("{:02x}".format(c) for c in data)
# CRC8 implementation with polynom = x^8+x^7+x^6+x^4+x^2+1 (0xD5)
crc8tab = [
0x00, 0xD5, 0x7F, 0xAA, 0xFE, 0x2B, 0x81, 0x54,
0x29, 0xFC, 0x56, 0x83, 0xD7, 0x02, 0xA8, 0x7D,
0x52, 0x87, 0x2D, 0xF8, 0xAC, 0x79, 0xD3, 0x06,
0x7B, 0xAE, 0x04, 0xD1, 0x85, 0x50, 0xFA, 0x2F,
0xA4, 0x71, 0xDB, 0x0E, 0x5A, 0x8F, 0x25, 0xF0,
0x8D, 0x58, 0xF2, 0x27, 0x73, 0xA6, 0x0C, 0xD9,
0xF6, 0x23, 0x89, 0x5C, 0x08, 0xDD, 0x77, 0xA2,
0xDF, 0x0A, 0xA0, 0x75, 0x21, 0xF4, 0x5E, 0x8B,
0x9D, 0x48, 0xE2, 0x37, 0x63, 0xB6, 0x1C, 0xC9,
0xB4, 0x61, 0xCB, 0x1E, 0x4A, 0x9F, 0x35, 0xE0,
0xCF, 0x1A, 0xB0, 0x65, 0x31, 0xE4, 0x4E, 0x9B,
0xE6, 0x33, 0x99, 0x4C, 0x18, 0xCD, 0x67, 0xB2,
0x39, 0xEC, 0x46, 0x93, 0xC7, 0x12, 0xB8, 0x6D,
0x10, 0xC5, 0x6F, 0xBA, 0xEE, 0x3B, 0x91, 0x44,
0x6B, 0xBE, 0x14, 0xC1, 0x95, 0x40, 0xEA, 0x3F,
0x42, 0x97, 0x3D, 0xE8, 0xBC, 0x69, 0xC3, 0x16,
0xEF, 0x3A, 0x90, 0x45, 0x11, 0xC4, 0x6E, 0xBB,
0xC6, 0x13, 0xB9, 0x6C, 0x38, 0xED, 0x47, 0x92,
0xBD, 0x68, 0xC2, 0x17, 0x43, 0x96, 0x3C, 0xE9,
0x94, 0x41, 0xEB, 0x3E, 0x6A, 0xBF, 0x15, 0xC0,
0x4B, 0x9E, 0x34, 0xE1, 0xB5, 0x60, 0xCA, 0x1F,
0x62, 0xB7, 0x1D, 0xC8, 0x9C, 0x49, 0xE3, 0x36,
0x19, 0xCC, 0x66, 0xB3, 0xE7, 0x32, 0x98, 0x4D,
0x30, 0xE5, 0x4F, 0x9A, 0xCE, 0x1B, 0xB1, 0x64,
0x72, 0xA7, 0x0D, 0xD8, 0x8C, 0x59, 0xF3, 0x26,
0x5B, 0x8E, 0x24, 0xF1, 0xA5, 0x70, 0xDA, 0x0F,
0x20, 0xF5, 0x5F, 0x8A, 0xDE, 0x0B, 0xA1, 0x74,
0x09, 0xDC, 0x76, 0xA3, 0xF7, 0x22, 0x88, 0x5D,
0xD6, 0x03, 0xA9, 0x7C, 0x28, 0xFD, 0x57, 0x82,
0xFF, 0x2A, 0x80, 0x55, 0x01, 0xD4, 0x7E, 0xAB,
0x84, 0x51, 0xFB, 0x2E, 0x7A, 0xAF, 0x05, 0xD0,
0xAD, 0x78, 0xD2, 0x07, 0x53, 0x86, 0x2C, 0xF9
]
def crc8(buffer):
crc = 0
for c in buffer:
crc = crc8tab[crc ^ c]
return crc
def ParseGPS(payload):
lat, long, speed, head, alt, numsat = struct.unpack('>iiHHHB', bytes(bytearray(payload))) # bytes(bytearray) casting is required for python 2.7.3 compatibility
return "[GPS] lat:%f long:%f speed:%d heading:%d alt:%d numsat:%d" % (lat / 1e7, long / 1e7, speed / 100, head / 100, alt - 1000, numsat)
def ParseBattery(payload):
voltage = float((payload[0] << 8) + payload[1]) / 10
current = float((payload[2] << 8) + payload[3]) / 10
consumption = (payload[4] << 16) + (payload[5] << 8) + payload[6]
return "[Battery] %.1fV %.1fA %dmAh" % (voltage, current, consumption)
def ParseBaroAltVario(payload):
alt_packed = (payload[0] << 8) + payload[1]
altitude = float(alt_packed & 0x7fff) if (alt_packed & 0x8000) else (float(alt_packed - 10000) / 10)
if len(payload) == 3:
# per CRSF spec
vario_packed = float(payload[2])
vario = (math.exp(abs(vario_packed) * 0.026) - 1) * 100 * math.copysign(1, vario_packed)
else:
# ELRS and Rotorflight
vario = float(struct.unpack('>h', bytes(payload[2:4]))[0] / 100)
return "[Baro Altitude Vario] Altitude %.1f m, Vertical Speed %.2f m/s" % (altitude, vario)
def ParseVtxTelem(payload):
return "[VTX Telemetry] freq=%d, power=%d, pitmode=%d" % (int.from_bytes(payload[1:3], 'big'), int(payload[3]), int(payload[4]))
def ParseLinkStatistics(payload):
return "[Link Statistics] "
def ParseChannels(payload):
return "[Channel Data] "
def ParseAttitude(payload):
pitch = float((payload[0] << 8) + payload[1]) / 1000
roll = float((payload[2] << 8) + payload[3]) / 1000
yaw = float((payload[4] << 8) + payload[5]) / 1000
return "[Attitude] pitch=%.3f roll=%.3f yaw=%.3f" % (pitch, roll, yaw)
def ParseFlightMode(payload):
return '[Flight Mode] "%s"' % "".join([chr(c) for c in payload[:-1]])
def ParsePingDevices(_):
return '[Ping Devices]'
def ParseDevice(payload):
return '[Device] 0x%02x "%s" %d parameters' % (payload[1], "".join([chr(c) for c in payload[2:-14]]), payload[-2])
def ParseRadioId(payload):
return '[RadioId] subcmd 0x%02x, interval %d, correction %d' % \
(payload[2], int.from_bytes(payload[3:7], 'big')/10, int.from_bytes(payload[7:11], 'big', signed=True)/10)
def ParseLinkStatisticsRX(payload):
return "[RX Link Stats] rssi=%d dBm rssi=%d%% lqi=%d snr=%d dB rf_power=%d dBm" % (payload[0], payload[1], payload[2], payload[3], payload[4])
def ParseCommand(payload):
if payload[2]==0x10 and payload[3]==5:
return '[Command] subcmd Crossfire, set ModelId, data %d' % (payload[4])
if payload[2]==0x0a and payload[3]==0x70:
return '[Command] subcmd General, Speed Proposal, port_id %d, bitrate %d' % (payload[4], int.from_bytes(payload[5:9], 'big'))
if payload[2]==0x0a and payload[3]==0x71:
return '[Command] subcmd General, Speed Response, port_id %d, %s' % (payload[4], 'accepted' if payload[5] else 'rejected')
return '[Command] subcmd 0x%02x, datatype 0x%02x, data %d' % \
(payload[2], payload[3], payload[4])
def ParseFieldsRequest(payload):
return '[Fields request]'
lastChunkRequested = 0
chunkOutOfOrder = False
def ParseFieldRequest(payload):
global lastChunkRequested, chunkOutOfOrder
s = '[Field request] device=0x%02x field=%d chunk=%d' % (payload[1], payload[2], payload[3])
if lastChunkRequested > 0 and payload[3] != lastChunkRequested+1:
s += ' (OUT OF ORDER REQUEST!)'
chunkOutOfOrder = True
lastChunkRequested = payload[3]
return s
def ParseFieldUpdate(payload):
return '[Field update] device=0x%02x field=%d value=%s' % (payload[1], payload[2], ' '.join(f'{byte:02x}' for byte in payload[3:]))
def ParseELRSInfo(payload):
return '[ELRS info] device=0x%02x bad=%d, good=%d, flags=0x%02x, flag_str=%s' % (payload[1], payload[2],
int.from_bytes(payload[3:5], 'big'), payload[5], ''.join(map(chr, payload[6:-2])))
fieldBuff = []
lastChunk = 0
def ParseField(payload):
global fieldBuff, lastChunk, lastChunkRequested, chunkOutOfOrder
fieldBuff += payload[4:]
if lastChunk > 0 and payload[3] != lastChunk-1:
s = '[Field] device=0x%02x field=%d chunk=%d (OUT OF ORDER CHUNK!)' % (payload[1], payload[2], payload[3])
lastChunk = 0
fieldBuff = []
return s
if payload[3] != 0:
lastChunk = payload[3]
return '[Field] device=0x%02x field=%d chunk=%d' % (payload[1], payload[2], payload[3])
print("fieldBuff: ", " ".join(f'{byte:02x}' for byte in fieldBuff))
name = ""
options = []
i = 2
try:
while fieldBuff[i] != 0:
name += chr(fieldBuff[i])
i += 1
i += 1
ftype = crossfire_types[fieldBuff[1] & 0x7f]
retstr = '[Field] %s device=0x%02x field=%d parent=%d type=%s' % (name, payload[1], payload[2], fieldBuff[0], ftype)
if ftype == "TEXT_SELECTION":
name = ""
while fieldBuff[i] != 0:
if fieldBuff[i] == 0x3b:
options.append(name)
name = ""
i += 1
continue
name += chr(fieldBuff[i])
i += 1
options.append(name)
i += 1
retstr += ' selection=(%d) %s' % (fieldBuff[i], options[fieldBuff[i]])
retstr += ' options %s' % (", ".join(options))
if ftype == "INFO":
value = ""
while fieldBuff[i] != 0:
value += chr(fieldBuff[i])
i += 1
retstr += ' name=%s value=%s' % (name, value)
if ftype == "COMMAND":
retstr += ' status=%s, timeout=%d ms, info=%s' % (cmd_status[fieldBuff[i]], fieldBuff[i+1], ''.join(map(chr, fieldBuff[i+2:])))
fieldBuff = []
lastChunk = 0
lastChunkRequested = 0
return retstr
except Exception as inst:
print(type(inst)) # the exception instance
print(inst.args) # arguments stored in .args
print(inst) # __str__ allows args to be printed directly,
#... # but may be overridden in exception subclasses
print("i: ", i)
print("len(payload): ", len(payload))
if i < len(payload):
print("payload[i-1]: ", payload[i-1])
fieldBuff = []
lastChunk = 0
return '[Exception]'
parsers = {
0x02: ParseGPS,
0x08: ParseBattery,
0x09: ParseBaroAltVario,
0x10: ParseVtxTelem,
0x14: ParseLinkStatistics,
0x16: ParseChannels,
0x1c: ParseLinkStatisticsRX,
0x1E: ParseAttitude,
0x21: ParseFlightMode,
0x28: ParsePingDevices,
0x29: ParseDevice,
0x2a: ParseFieldsRequest,
0x2b: ParseField,
0x2c: ParseFieldRequest,
0x2d: ParseFieldUpdate,
0x2e: ParseELRSInfo,
0x32: ParseCommand,
0x3a: ParseRadioId,
}
def ParsePacket(packet):
global timeData, prevTimeData, fieldBuff
length = packet[1]
command = packet[2]
payload = packet[3:-1]
crc = packet[-1]
diffTime = timeData - prevTimeData
prefix = '(%d)' % lineNumber if timeData == 0 else '%10.6f [%9.6f]' % (timeData, diffTime)
prevTimeData = timeData
timeData = 0
if crc != crc8(packet[2:-1]):
print(prefix, dump(packet), "[CRC error]")
fieldBuff = []
lastChunk = 0
return
if args.ignore and command == 0x16:
return
if args.omit and ((command == 0x2d and payload[2] == 0x00) or \
command == 0x2e):
return
if args.remove and command == 0x3a:
return
parser = parsers.get(command, None)
if parser != None:
print(prefix, dump(packet), parser(payload))
else:
print(prefix, dump(packet), '[Unknown Command 0x%0x]' % command)
crossfireDataBuff = []
chunkedBuffer = []
def ParseData(data):
global crossfireDataBuff
crossfireDataBuff += data
# build packet for parsing
# separate buffers for data sources so chunked data handled correctly
while len(crossfireDataBuff) > 1:
if crossfireDataBuff[0] != 0xc8 and crossfireDataBuff[0] != 0x00 and crossfireDataBuff[0] != 0xee and crossfireDataBuff[0] != 0xea:
print("Skipped 1 byte", dump(crossfireDataBuff[:1]))
crossfireDataBuff = crossfireDataBuff[1:]
continue
length = crossfireDataBuff[1]
if length < 2 or length > 0x40:
print("Skipped 1 bytex", dump(crossfireDataBuff[:1]))
crossfireDataBuff = crossfireDataBuff[1:]
continue
if len(crossfireDataBuff) < length+2:
break
ParsePacket(crossfireDataBuff[:length+2])
crossfireDataBuff = crossfireDataBuff[length+2:]
def readSport(inp):
global timeData
line = inp.readline()
lineNumber += 1
if len(line) == 0:
return
line = line.strip('\r\n')
if len(line) == 0:
return
parts = line.split(': ')
if len(parts) < 2:
print("weird data: \"%s\" at line %d" % (line, lineNumber))
return
timeData = float(parts[0].strip())
crossfireData = parts[1].strip()
# convert from hex
parts = crossfireData.split(' ')
binData = [int(hex, 16) for hex in parts]
return binData
def readCsv(inp):
global lineNumber
global timeData
crossfireData = []
line = inp.readline()
if ('Value' in line):
line = inp.readline()
lineNumber += 1
line = "".join(line.split())
parts = line.split(',')
if len(parts) < 2:
return []
crossfireData = [int(parts[1][2:4],16)]
if timeData == 0:
timeData = float(parts[0].strip())
return crossfireData
def readHex(inp):
global lineNumber
crossfireData = []
line = inp.readline()
lineNumber += 1
line = "".join(line.split())
crossfireData = [int(hex,16) for hex in [line[i:i+2] for i in range(0, len(line), 2)]]
return crossfireData
def readBinary(inp):
crossfireData = []
while (b := inp.read(1)):
crossfireData += b
return crossfireData
# execution starts here
parser = argparse.ArgumentParser()
parser.add_argument('inputFile', default='stdin', help='Input file with capture data (stdin works)')
parser.add_argument('-f', '--format', default='sport',
choices=['bin', 'hex', 'csv', 'sport'],
help='Type of input file. bin=binary, hex=ascii hex, csv=salae async serial export, sport=s.port log')
parser.add_argument('-i', '--ignore', action="store_true",
help='Ignore rc data (stick) packets')
parser.add_argument('-o', '--omit', action="store_true",
help='Ignore ELRS status request/response packets')
parser.add_argument('-r', '--remove', action="store_true",
help='Ignore RadioId CRSFshot messages')
args = parser.parse_args()
# open input
if args.inputFile == 'stdin':
if args.format == 'bin':
inp = sys.stdin.buffer
else:
inp = sys.stdin
else:
if args.format == 'bin':
inp = open(args.inputFile, 'rb')
else:
inp = open(args.inputFile, 'r')
if args.format == 'sport':
get_data = readSport
elif args.format == 'csv':
get_data = readCsv
elif args.format == 'bin':
get_data = readBinary
else:
get_data = readHex
while (crossfireData := get_data(inp)):
ParseData(crossfireData)
+37
View File
@@ -0,0 +1,37 @@
from __future__ import print_function
BITLEN_DSM2 = 16
def sendByteDsm2(b):
print("%02x:" % b, end=' ')
lev = 0
len = BITLEN_DSM2
for i in range(9):
nlev = b & 1
if lev == nlev:
len += BITLEN_DSM2
else:
print(len, end=' ')
# _send_1(nlev ? len-5 : len+3);
len = BITLEN_DSM2
lev = nlev
b = (b >> 1) | 0x80
# _send_1(len+BITLEN_DSM2+3); // 2 stop bits
print(len + BITLEN_DSM2)
sendByteDsm2(24)
sendByteDsm2(17)
sendByteDsm2(2)
sendByteDsm2(0x00)
sendByteDsm2(0x06)
sendByteDsm2(0)
sendByteDsm2(10)
sendByteDsm2(0)
sendByteDsm2(14)
sendByteDsm2(0)
sendByteDsm2(18)
sendByteDsm2(0)
sendByteDsm2(22)
sendByteDsm2(0)
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/python
# vim: set fileencoding=utf-8
import clang.cindex
import asciitree # must be version 0.2
import sys
def node_children(node):
return list(c for c in node.get_children() if c is not None and c.location is not None and c.location.file is not None and c.location.file.name == sys.argv[1])
def print_node(node):
text = node.spelling or node.displayname
kind = str(node.kind)[str(node.kind).index('.')+1:]
if clang.cindex.CursorKind.FIELD_DECL == node.kind:
size = node.type.get_size()
return '{} {} {}'.format(kind, text, size)
return '{} {}'.format(kind, text)
if len(sys.argv) < 2:
print("Usage: dump_ast.py [header file name] [additional compile args]")
sys.exit()
clang.cindex.Config.set_library_file('/usr/local/Cellar/llvm/6.0.0/lib/libclang.dylib')
index = clang.cindex.Index.create()
translation_unit = index.parse(sys.argv[1], ['-x', 'c++', '-std=c++11'] + sys.argv[2:])
print(asciitree.draw_tree(translation_unit.cursor, node_children, print_node))
+182
View File
@@ -0,0 +1,182 @@
#!/usr/bin/python
# vim: set fileencoding=utf-8
from clang.cindex import *
import asciitree # must be version 0.2
import sys
TOP_NODE_TYPES = [
CursorKind.ENUM_DECL,
CursorKind.STRUCT_DECL ]
CHILD_NODE_TYPES = [
CursorKind.FIELD_DECL,
CursorKind.UNION_DECL,
#CursorKind.STRUCT_DECL,
CursorKind.ENUM_CONSTANT_DECL ]
def filter_node(node):
if node is None:
return False
# # no node or no location
# if node is None or node.location is None or node.location.file is None:
# return False
# # not the main source file
# if node.location.file.name != sys.argv[1]:
# return False
if node.kind in CHILD_NODE_TYPES:
return True
return False
def get_struct_decl(node):
for c in node.get_children():
if c is not None and c.kind == CursorKind.STRUCT_DECL:
return c
return None
def node_children(node):
if node is None:
return []
if isinstance(node,list):
return node
if node.kind == CursorKind.FIELD_DECL:
if node.type.kind == TypeKind.ELABORATED:
return node_children(node.type.get_declaration())
elif node.type.kind == TypeKind.RECORD:
return list(c for c in node.type.get_fields())
elif node.type.kind == TypeKind.CONSTANTARRAY:
et = node.type.element_type
if et.kind == TypeKind.RECORD:
return list(c for c in et.get_fields())
elif node.type.kind == TypeKind.ENUM:
return node_children(node.type.get_declaration())
else:
print("{} {}".format(str(node.type.kind),node.spelling))
return list(c for c in node.get_children() if filter_node(c))
def get_array_size(f):
et = f.type.element_type
ec = f.type.element_count
if et.spelling == 'char':
return 'string({})'.format(ec)
else:
return '{} x {} bytes'.format(ec,et.get_size())
def get_field_size(f):
if f.is_bitfield():
return '{} bits'.format(f.get_bitfield_width())
elif f.type.kind == TypeKind.CONSTANTARRAY:
return get_array_size(f)
return '{} bytes'.format(f.type.get_size())
def print_field_decl(kind, text, f):
ft = ''
is_anon = f.is_anonymous()
if f.type.kind == TypeKind.RECORD:
decl = f.type.get_declaration()
if decl.kind == CursorKind.UNION_DECL:
if is_anon:
ft = 'anonymous union ({})'.format(decl.mangled_name)
else:
ft = 'union {}'.format(f.type.spelling)
elif decl.kind == CursorKind.STRUCT_DECL:
if is_anon:
ft = '{} anonymous struct'.format(f.kind)
else:
ft = 'struct {}'.format(f.type.spelling)
elif f.type.kind == TypeKind.TYPEDEF:
decl = f.type.get_canonical()
ft = decl.spelling
elif get_struct_decl(f) is not None:
ft = 'anon struct'
elif f.type.kind == TypeKind.CONSTANTARRAY:
et = f.type.element_type
ft = et.spelling
else:
ft = f.type.spelling
return '{} {} ({})'.format(ft, text, get_field_size(f))
def print_enum_decl(kind, text, e):
return '{} {} ({})'.format(kind, text, e.enum_value)
def print_union_decl(kind, text, f):
return '{} {} ({})'.format(kind, text, get_field_size(f))
def print_node(node):
if isinstance(node,list):
return 'ROOT'
text = node.spelling or node.displayname
kind = str(node.kind)[str(node.kind).index('.')+1:]
if CursorKind.FIELD_DECL == node.kind:
return print_field_decl(kind, text, node)
if CursorKind.ENUM_CONSTANT_DECL == node.kind:
return print_enum_decl(kind, text, node)
if CursorKind.UNION_DECL == node.kind:
return print_union_decl(kind, text, node)
return '{} {}'.format(kind, text)
def get_top_node(name, node):
for c in node.get_children():
if c.spelling == name:
# struct found
return c
print("get_top_node: '{}' not found!".format(name))
return None
def get_top_nodes(node):
l = list()
for c in node.get_children():
# no node or no location
if c is None or c.location is None or c.location.file is None:
continue
# not the main source file
if c.location.file.name != sys.argv[1]:
continue
if c.kind in TOP_NODE_TYPES:
# struct found
l.append(c)
return l
def print_top_node(name):
s = get_top_node(name, translation_unit.cursor)
if s is None:
return
print(asciitree.draw_tree(s, node_children, print_node))
def print_all():
s = get_top_nodes(translation_unit.cursor)
print(asciitree.draw_tree(s, node_children, print_node))
### Main ###
if len(sys.argv) < 3:
print("Usage: dump_ast_yaml.py [header file name] [top node] [additional compile args]")
sys.exit()
Config.set_library_file('/usr/local/Cellar/llvm/6.0.0/lib/libclang.dylib')
index = Index.create()
translation_unit = index.parse(sys.argv[1], ['-x', 'c++', '-std=c++11'] + sys.argv[3:])
print_top_node(sys.argv[2])
#print_top_node('CustomFunctionData')
#print_all()
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/awk -nf
#
# Parses the output of elf-size-report.awk and calculates segment memory usage deltas between the analyzed elf files.
#
# License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
# Copyright (c)2016 Maxim Paperno
# Copyright (c)2017 OpenTX
#
BEGIN {
in_ttls = 0;
elf_cnt = 0;
sec_cnt = 0;
}
{
if ($1 == "----" && NF > 5 && $5 ~ /\.elf$/) {
fname[elf_cnt] = $5;
}
else if (tolower($1) ~ /totals/) {
in_ttls = 1;
elf_cnt++;
}
else if (in_ttls && NF != 5) {
in_ttls = 0;
}
else if (in_ttls) {
if (elf_cnt == 1)
a[++sec_cnt] = $1;
d[elf_cnt][$1]["total"] = $2;
}
}
END {
printf("\n---- Comparing results of %d files ----\n\n", elf_cnt);
PROCINFO["sorted_in"] = "@ind_num_asc"
for (j in d) {
if (length(fname[j-1]))
printf("file %d: %s\n", j, fname[j-1])
}
printf("\n%-8s ", "Section");
for (j in d) {
printf("%8s ", "file " j);
if (j > 1)
printf("(%7s) ", "\xCE\x94 vs f1");
if (j > 2)
printf("(%7s) ", "\xCE\x94 vs f" j-1);
}
printf("\n");
for (i in a) {
n = a[i];
printf("%8s ", n);
for (j in d) {
printf("%8.2f ", d[j][n]["total"]);
if (j > 1)
printDelta(d[j][n]["total"] - d[1][n]["total"]);
if (j > 2)
printDelta(d[j][n]["total"] - d[j-1][n]["total"]);
}
printf("\n");
}
printf("\n");
}
function printDelta(dlta) {
if (dlta)
printf("(%+7.2f) ", dlta);
else
printf("(%7s) ", "--.--");
}
+105
View File
@@ -0,0 +1,105 @@
#!/usr/bin/awk -nf
#
# Process gcc binutils 'size -A' output to show more specific and summarized information about memory segment usage.
#
# This script expects some variables to be set on the command line with -v:
# names : list of segment names ("Flash", "RAM" etc.)
# addrs : list of segment start addresses in hex/oct/dec (in same order as names)
# sizes : list of segment sizes in KB (in same order as names)
# elf : name of elf file (optional, for display only)
# mcu : name of MCU (optional, for display only)
# skip_data : skip duplicate .data in Flash (0/1 default: 0)
#
# NOTE: The memory segments should be specified in increasing address order (low to high).
#
# Usage example:
# arm-none-eabi-size -A -x firmware.elf | ./elf-size-report.awk \
# -v elf="firmware.elf" \
# -v mcu="STM32F401VG" \
# -v names="Flash RAM" \
# -v addrs "0x08000000 0x100000000" \
# -v sizes "512 128"
#
# License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
# Copyright (c)2016 Maxim Paperno
# Copyright (c)2017 OpenTX
#
BEGIN {
err = 0;
len = split(names, a_names, " ");
alen = split(addrs, a_addrs, " ");
slen = split(sizes, a_sizes, " ");
if (!len || len != alen || len != slen) {
print "Error: No segment data provided or array sizes mismatch, exiting.";
exit err = 1;
}
flash_name = "Flash";
for (i in a_names) {
n = a_names[i];
a[i] = n; d[n]["addr"] = a_addrs[i]; d[n]["size"] = a_sizes[i]; d[n]["sum"] = 0;
if (tolower(n) ~ /flash/)
flash_name = n;
}
flmax = d[flash_name]["addr"];
fmtstr = "%-20s %7d 0x%08x %s\n";
printf("\n---- Size report");
if (length(elf)) printf(" for %s", elf);
if (length(mcu)) printf(" (MCU: %s)", mcu);
printf(" ----\n\n");
printf("Section Details:\n");
printf("%-20s %7s %10s %s\n", "section", "size(B)", "addr", "loc");
}
# Process each line of 'size' output listing the program's sections,
# skip line if sections size or address are zero.
NR > 2 && $2 != 0 && $3 != 0 {
if ($1 == ".data") {
# .data section is always in Flash but 'size' reports it only in RAM (sometimes twice)
d[flash_name]["sum"] += $2;
printf(fmtstr, $1 " (added)", $2, flmax, flash_name);
flmax += $3;
if (skip_data) { next; }
}
# Walk over each specified memory segment looking for a match
# note: (this assumes segments were passed in ascending memory order)
PROCINFO["sorted_in"] = "@ind_num_desc"
addr = d[a[len]]["addr"];
for (i in a) {
n = a[i];
if ($3 >= addr) {
# check for overflow into contigious region
t = d[n]["sum"] + $2 - (d[n]["size"] * 1024);
if (t > 0 && i < len && d[a[i+1]]["addr"] = d[n]["addr"] + d[n]["size"] + 1) {
d[n]["sum"] += $2 - t;
d[a[i+1]]["sum"] += t;
seg = n "+" a[i+1];
}
else {
d[n]["sum"] += $2;
seg = n;
}
printf(fmtstr, $1, $2, $3, seg);
# track maximum flash address used
if (n == flash_name)
flmax += $2;
break;
}
else if (i > 1)
addr = d[a[i-1]]["addr"];
}
}
END {
if (err) exit err;
fmtstr = "%7s: %8.2f %6.2f%% %6d\t%7.2f\n";
printf("\nTotals %8s usage of ttl\tKB free\n", "KB");
PROCINFO["sorted_in"] = "@ind_num_asc"
for (i in a) {
n = a[i];
if (d[n]["size"] > 0)
printf(fmtstr, n, d[n]["sum"]/1024, d[n]["sum"]/(d[n]["size"]*1024)*100, d[n]["size"], d[n]["size"] - d[n]["sum"]/1024 );
}
printf("\n");
}
+184
View File
@@ -0,0 +1,184 @@
#!/bin/bash
#
# This is a wrapper for the elf-size-report.awk script which prints a detailed report
# of memory segment allocations and usage in a given ELF file, based on gcc 'size' output.
# This script sets up memory segment information needed by the awk script.
# If more than one .elf file is given, then a comparison (delta memory use) report is
# also performed using elf-size-compare.awk.
# Currently supports some STMicro & Atmel ARM MCUs. May not work with all placement strategies.
#
# Usage:
# [VAR=value] ./elf-memory-report.sh [options] [file.elf] [file2.elf] [...]
#
# Where VAR is any of the CAPITALIZED variables below. More than one can be specified,
# and any VAR can also be set in the environment.
# SIZE : gcc binutils 'size' command to use, default is "arm-none-eabi-size".
# MCU :
# Specifies MCU model on which to base default segment location/size parameters. Also see "--mcu=" option.
# MEM_START_<segment> & MEM_SIZE_<segment> : (read code below for defaults)
# Allows specifying additional/custom segment information, eg. MEM_SIZE_FLASH=768
#
# Options can be one or more of the following:
# --mcu=<MCU>
# Set MCU type. Same as setting the MCU env. variable, and overrides it.
# --skip-data
# Some linker scripts duplicate .data in the output (eg: .data and .data_run).
# Set this option to skip .data from being counted in RAM a 2nd time
# (it is always shown as being in Flash).
# --compare-only
# When specifying multiple elf files, only show the final comparison report.
# --no-compare
# Opposite and exclusive of previous option, that is, do NOT show comparison report (only size reports).
#
# If an elf file is not specified on the command line, this script will prompt for one.
#
# Examples:
# MCU="STM32F439xI" MEM_SIZE_SDRAM2=8192 ./elf-memory-report.sh path/to/firmware.elf
# ./elf-memory-report.sh --mcu ATSAM3S4C --compare-only firmware_new.elf firmware_old.elf
# set -k && ./elf-memory-report.sh MCU=STM32F427VG --skip-data firmware.elf
# (the -k switch allows variable assignment anywhere on the command line)
# c:\cygwin\bin\bash -kc './elf-memory-report.sh --mcu ATSAM3S8C "c:\windows\path\with spaces\firmware.elf"'
#
# License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
# Copyright (c)2016 Maxim Paperno
# Copyright (c)2017 OpenTX
#
# -- gcc binutils 'size' command to use
: ${SIZE:="arm-none-eabi-size"}
# -- MCU type, if defined will set certain memory defaults
# e.g. STM32F205xE or ATSAM3S4C
: ${MCU:="unknown"}
# -- process options
compare=2
skip_data=0
for i in "$@" ; do
case $i in
--mcu=*) MCU="${i#*=}"; shift;;
--skip-data) skip_data=1; shift;;
--compare-only) compare=1; shift;;
--no-compare) compare=0; shift;;
esac
done
# -- make sure gawk is installed
: ${AWKCMD:=""}
if gawk --version >/dev/null 2>&1; then
[[ "$OSTYPE" == "darwin"* ]] && AWKCMD="gawk -nf"
else
echo "GAWK is required, exiting."
exit 1
fi
# -- defaults which may change based on MCU
default_flash_addr=0x08000000
default_flash_sz=1024 # KB
default_ccm_sz=0
default_ram1_sz=128
default_ram2_sz=0
default_ram3_sz=0
# -- Set defaults based on MCU type
if [[ $MCU != "unknown" ]] ; then
mcu_brand=${MCU:0:2} # ST or AT
if [[ $mcu_brand == "ST" && ${#MCU} -ge 7 ]] ; then
# e.g.: STM32F427VG
stm_series=${MCU:6:1} # 4
stm_ser_ex=${MCU:7:1} # 2
stm_flash=${MCU:10:1} # G
default_ram1_sz=112
default_ram2_sz=16
case $stm_flash in
"I") default_flash_sz=2048;;
"G") default_flash_sz=1024;;
"E") default_flash_sz=512;;
"C") default_flash_sz=256;;
esac
if (( $stm_series == 4 )) ; then
default_ccm_sz=64
if (( $stm_ser_ex >= 2 )) ; then
default_ram3_sz=64
fi
fi
elif [[ $mcu_brand == "AT" && ${#MCU} -ge 9 ]] ; then
# e.g.: ATSAM3S4C
atm_series=${MCU:5:4} # 3S4C
default_flash_addr=0x00400000
case $atm_series in
"3S8C") default_flash_sz=512; default_ram1_sz=64;;
"3S4C") default_flash_sz=256; default_ram1_sz=48;;
"3S2C") default_flash_sz=128; default_ram1_sz=32;;
esac
else
echo "MCU type unrecognized, exiting."
exit 1
fi
fi
# -- target memory locations in hex
: ${MEM_START_FLASH:=$default_flash_addr}
: ${MEM_START_CCRAM:=0x10000000}
: ${MEM_START_SRAM1:=0x20000000}
: ${MEM_START_SRAM2:=0x2001c000}
: ${MEM_START_SRAM3:=0x20020000}
: ${MEM_START_PSRAM1:=0x60000000}
: ${MEM_START_SDRAM1:=0xC0000000}
: ${MEM_START_SDRAM2:=0xD0000000}
# -- target memory sizes in KB
: ${MEM_SIZE_FLASH:=$default_flash_sz}
: ${MEM_SIZE_CCRAM:=$default_ccm_sz}
: ${MEM_SIZE_SRAM1:=$default_ram1_sz}
: ${MEM_SIZE_SRAM2:=$default_ram2_sz}
: ${MEM_SIZE_SRAM3:=$default_ram3_sz}
: ${MEM_SIZE_PSRAM1:=0}
: ${MEM_SIZE_SDRAM1:=0}
: ${MEM_SIZE_SDRAM2:=0}
# -- prompt for elf file if not on command line
if [[ -z "$@" ]] ; then
read -p "Elf file(s): " ELF_FILES
else
ELF_FILES=$@
fi
# -- Set arrays for awk script. The segments should be in increasing address order.
names=("Flash" "CCRAM" "SRAM1" "SRAM2" "SRAM3" "PSRAM1" "SDRAM1" "SDRAM2")
addrs=(${MEM_START_FLASH} ${MEM_START_CCRAM} ${MEM_START_SRAM1} ${MEM_START_SRAM2} ${MEM_START_SRAM3} \
${MEM_START_PSRAM1} ${MEM_START_SDRAM1} ${MEM_START_SDRAM2})
sizes=(${MEM_SIZE_FLASH} ${MEM_SIZE_CCRAM} ${MEM_SIZE_SRAM1} ${MEM_SIZE_SRAM2} ${MEM_SIZE_SRAM3} \
${MEM_SIZE_PSRAM1} ${MEM_SIZE_SDRAM1} ${MEM_SIZE_SDRAM2})
base_dir="$(dirname "$BASH_SOURCE")"
base_dir="${base_dir%%[[:space:]]}" # sometimes we get trailing \n
# -- run the ARM size command for each elf file and send output to awk script
all_output=""
file_cnt=0
for elf in "$@"; do
if [[ ! -f "$elf" ]] ; then
echo "ELF file \"$elf\" not found."
continue
fi
#${SIZE} -A -x ${elf}
escelf="${elf//\\//}"
output=`${SIZE} -A -x ${elf} | ${AWKCMD} "${base_dir}/elf-size-report.awk" \
-v mcu="${MCU}" \
-v elf="${escelf}" \
-v skip_data=${skip_data} \
-v names="${names[*]}" \
-v addrs="${addrs[*]}" \
-v sizes="${sizes[*]}"`
if (( compare != 1 )) ; then
echo "$output"
echo
fi
file_cnt+=1
all_output+="${output}\n"
done
# -- if more than one elf file was given, also run a comparison report
if (( file_cnt > 1 && compare )) ; then
echo "$all_output" | ${AWKCMD} "${base_dir}/elf-size-compare.awk"
fi
+332
View File
@@ -0,0 +1,332 @@
#!/usr/bin/python
from __future__ import annotations
import argparse
import struct
import sys
from typing import BinaryIO, Literal, Optional, Iterator
from elftools.elf.elffile import ELFFile
DATA_BLOCK_SIZE = 256
UF2_MAGIC_START = b"UF2\n\x57\x51\x5d\x9e"
UF2_MAGIC_END = b"\x30\x6f\xb1\n"
UF2_MAX_BLOCK_SIZE = 512
# extensions
UF2_VERSION_TAG = 0x9FC7BC
UF2_DEVICE_TAG = 0x650D9D
UF2_REBOOT_TAG = 0xE60835
def pad32(b: bytes) -> bytes:
rem = len(b) % 4
if rem == 0:
return b
return b + (b"\x00" * (4 - rem))
class UF2Extension:
def __init__(self, tag: int, payload: bytes):
self.tag = tag
self.payload = payload
def __str__(self):
return f"0x{self.tag:06X}: {self.payload}"
def _len(self):
return 4 + len(self.payload)
def to_bytes(self):
return (
self._len().to_bytes(1, "little")
+ self.tag.to_bytes(3, "little")
+ pad32(self.payload)
)
class UF2Writer:
def __init__(
self,
fstream: BinaryIO,
*,
block_size=DATA_BLOCK_SIZE,
family=0,
device="",
version="",
):
self.out = fstream
self.block_size = block_size
self.family = family
self.blockno = 0
self.extensions = []
if device is not None and len(device):
self.extensions.append(UF2Extension(UF2_DEVICE_TAG, device.encode("utf-8")))
if version is not None and len(version):
self.extensions.append(
UF2Extension(UF2_VERSION_TAG, version.encode("utf-8"))
)
def _write_block(
self,
addr: int,
payload: bytes | bytearray,
nblocks: int,
/,
flags=0,
extensions: list[UF2Extension] = [],
):
if self.family:
flags |= 0x2000
merged_extensions = extensions + self.extensions
if len(merged_extensions) > 0:
flags |= 0x8000
hdr = UF2_MAGIC_START + struct.pack(
b"<IIIIII", flags, addr, len(payload), self.blockno, nblocks, self.family
)
size = len(hdr) + len(payload)
self.out.write(hdr)
self.out.write(payload)
for ext in merged_extensions:
ext_bytes = ext.to_bytes()
self.out.write(ext_bytes)
size += len(ext_bytes)
fill = UF2_MAX_BLOCK_SIZE - size - 4
self.out.write(b"\00" * fill)
self.out.write(UF2_MAGIC_END)
self.blockno += 1
class ELFReader:
def __init__(self, fstream):
self.elf = ELFFile(fstream)
self.segments = self._get_merged_elf_segments()
def _get_merged_elf_segments(self):
def iter_segments():
for i in range(self.elf.num_segments()):
segment = self.elf.get_segment(i)
type = segment["p_type"]
addr = segment["p_paddr"]
filesz = segment["p_filesz"]
if type == "PT_LOAD" and filesz > 0:
yield (i, addr, filesz)
def is_mergeable(last_addr, addr):
if last_addr is None:
return False
diff = addr - last_addr
return diff >= 0 and diff < 256
segments = []
last_addr = None
for i, addr, filesz in iter_segments():
if is_mergeable(last_addr, addr):
saddr, size, chunks = segments[-1]
fill = addr - last_addr
if fill > 0:
chunks.append((-1, fill))
size += fill
chunks.append((i, filesz))
size += filesz
segments[-1] = (saddr, size, chunks)
last_addr = saddr + size
else:
segments.append((addr, filesz, [(i, filesz)]))
last_addr = addr + filesz
return segments
def iter_blocks(self, chunks, *, block_size=DATA_BLOCK_SIZE) -> Iterator[bytearray]:
buffer = bytearray()
for i, size in chunks:
# fill buffer first
if i >= 0:
buffer += self.elf.get_segment(i).data()
else:
buffer += b"\x00" * size
# then drain it
while len(buffer) >= block_size:
yield buffer[:block_size]
buffer = buffer[block_size:]
# and finally drain it completely
while len(buffer) > 0:
yield buffer[:block_size]
buffer = buffer[block_size:]
def get_total_blocks(self, /, block_size=DATA_BLOCK_SIZE) -> int:
blocks = 0
for _, size, _ in self.segments:
blocks += (size + block_size - 1) // block_size
return blocks
def get_total_size(self) -> int:
total_size = 0
for _, size, _ in self.segments:
total_size += size
return total_size
def get_section(self, name: str) -> tuple[int, int]:
section = self.elf.get_section_by_name(name)
if section is not None:
return (section["sh_addr"], section["sh_size"])
return (0, 0)
class UF2InsertBlock:
def __init__(
self,
insert_addr: int,
addr: int,
payload: bytes,
*,
extensions: list[UF2Extension] = [],
flags=0,
):
self.insert_addr = insert_addr
self.addr = addr
self.payload = payload
self.extensions = extensions
self.flags = flags
def write_uf2(
reader: ELFReader, uf2: UF2Writer, insert_block: Optional[UF2InsertBlock] = None
):
nblocks = reader.get_total_blocks() + (1 if insert_block else 0)
for addr, _, chunks in reader.segments:
block_addr = addr
for block in reader.iter_blocks(chunks):
if insert_block and insert_block.insert_addr <= block_addr:
uf2._write_block(
insert_block.addr,
insert_block.payload,
nblocks,
flags=insert_block.flags,
extensions=insert_block.extensions,
)
insert_block = None
if len(block) < DATA_BLOCK_SIZE:
block = block + b"\xff" * (DATA_BLOCK_SIZE - len(block))
uf2._write_block(block_addr, block, nblocks)
block_addr += len(block)
def elf_to_uf2(args: argparse.Namespace) -> None:
reader = ELFReader(args.input)
total_blocks = reader.get_total_blocks()
print(f"Total blocks: {total_blocks}")
total_size = reader.get_total_size()
print(f"Total size: {total_size}")
insert_block = None
if args.reboot is not None:
insert = None
addr = None
start = None
if args.reboot == "auto":
if len(reader.segments) > 1:
addr, _ = reader.get_section(".reboot_buffer")
start, bl_size = reader.get_section(".bootloader")
insert = start + bl_size
else:
insert, addr, start = args.reboot
if insert and addr and start:
print(f"Insert reboot block @{insert:08X} ({addr:08X}/{start:08X})")
insert_block = UF2InsertBlock(
insert,
addr,
b"BDFU",
extensions=[UF2Extension(UF2_REBOOT_TAG, start.to_bytes(4, "little"))],
flags=1,
)
uf2 = UF2Writer(
args.output, family=args.family, device=args.device, version=args.version
)
write_uf2(reader, uf2, insert_block)
print(f"Blocks written: {uf2.blockno}")
def parse_reboot_arg(arg: str) -> tuple[int, int, int] | Literal["auto"]:
if arg == "auto":
return arg
addresses = arg.split(":")
if len(addresses) != 2:
raise argparse.ArgumentTypeError("invalid addresses")
try:
insert = int(addresses[0], 0)
addr = int(addresses[1], 0)
start = int(addresses[2], 0)
return (insert, addr, start)
except ValueError:
raise argparse.ArgumentTypeError("invalid addresses")
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument(
"--family", type=lambda x: int(x, 0), default=0, help="UF2 family ID"
)
parser.add_argument("--device", default="", help="UF2 device description")
parser.add_argument("--version", default="", help="UF2 firmware version")
parser.add_argument(
"--reboot",
type=parse_reboot_arg,
metavar="{INSERT:ADDRESS:START | auto}",
help="add reboot block",
)
parser.add_argument(
"-i",
"--input",
type=argparse.FileType("rb"),
required=True,
help="input ELF file",
)
parser.add_argument(
"output",
nargs="?",
help="output UF2 file",
type=argparse.FileType("wb"),
default="output.uf2",
)
return parser.parse_args()
def main() -> None:
try:
args = parse_args()
except Exception as e:
print(e)
sys.exit(-1)
try:
elf_to_uf2(args)
except Exception as e:
print(e)
sys.exit(-1)
finally:
args.input.close()
args.output.close()
if __name__ == "__main__":
main()
+213
View File
@@ -0,0 +1,213 @@
#!/usr/bin/env python
import argparse
from PIL import Image
# LZ4 compression
import lz4.block
class RawMixin:
def encode_byte(self, byte):
self.write(byte)
def encode_end(self):
pass
class RleMixin:
RLE_BYTE = 0
RLE_SEQ = 1
def __init__(self):
self.state = self.RLE_BYTE
self.count = 0
self.prev_byte = None
def eq_prev_byte(self, byte):
if self.prev_byte is None:
return False
return byte == self.prev_byte
def encode_byte(self, byte):
if self.state == self.RLE_BYTE:
self.write(byte)
if self.eq_prev_byte(byte):
self.state = self.RLE_SEQ
self.count = 0
else:
self.prev_byte = byte
elif self.state == self.RLE_SEQ:
if self.eq_prev_byte(byte):
self.count += 1
if self.count == 255:
self.write(self.count)
self.prev_byte = None
self.state = self.RLE_BYTE
else:
self.write(self.count)
self.write(byte)
self.prev_byte = byte
self.state = self.RLE_BYTE
def encode_end(self):
if self.state == self.RLE_SEQ:
self.write(self.count)
class Lz4Mixin:
def __init__(self):
self.buffer = []
def encode_byte(self,byte):
self.buffer.append(byte)
def encode_end(self):
compressed_data = lz4.block.compress(bytes(self.buffer), compression=12, mode='high_compression', store_size=False)
data_len = len(compressed_data).to_bytes(4, byteorder='little')
for b in data_len:
self.write(b)
for b in compressed_data:
self.write(b)
class ImageEncoder:
def __init__(self, filename, size_format, reverse=False):
self.f = open(filename, "w")
self.size_format = size_format
self.reverse = reverse
def write(self, value):
if isinstance(value, bytes):
value = int.from_bytes(value, 'little')
self.f.write("0x%02x," % value)
def write_size(self, width, height):
if self.size_format == 2:
self.f.write("%d,%d,%d,%d,\n" % (width % 256, width // 256, height % 256, height // 256))
else:
self.f.write("%d,%d,\n" % (width, height))
def encode_1bit(self, image, rows):
image = image.convert(mode='1')
width, height = image.size
self.write_size(width, height // rows)
for y in range(0, height, 8):
for x in range(width):
value = 0
for z in range(8):
if y + z < height:
if image.format == "XBM":
if image.getpixel((x, y + z)) > 0:
value += 1 << z
else:
if image.getpixel((x, y + z)) == 0:
value += 1 << z
self.encode_byte(value)
self.f.write("\n")
self.encode_end()
def encode_4bits(self, image):
image = image.convert(mode='L')
width, height = image.size
self.write_size(width, height)
for y in range(0, height, 2):
for x in range(width):
value = 0xFF
gray1 = self.get_pixel(image, x, y)
if y + 1 < height:
gray2 = self.get_pixel(image, x, y + 1)
else:
gray2 = 255
for i in range(4):
if gray1 & (1 << (4 + i)):
value -= 1 << i
if gray2 & (1 << (4 + i)):
value -= 1 << (4 + i)
self.encode_byte(value)
self.f.write("\n")
self.encode_end()
def encode_8bits(self, image):
image = image.convert(mode='L')
width, height = image.size
self.write_size(width, height)
for y in range(height):
for x in range(width):
value = self.get_pixel(image, x, y)
value = 0xFF - value
self.encode_byte(value)
self.encode_end()
def encode_5_6_5(self, image):
width, height = image.size
self.write_size(width, height)
for y in range(height):
for x in range(width):
pixel = self.get_pixel(image, x, y)
val = ((pixel[0] >> 3) << 11) + ((pixel[1] >> 2) << 5) + ((pixel[2] >> 3) << 0)
self.encode_byte(val & 255)
self.encode_byte(val >> 8)
self.encode_end()
def encode_4_4_4_4(self, image):
width, height = image.size
self.write_size(width, height)
for y in range(height):
for x in range(width):
pixel = self.get_pixel(image, x, y)
val = ((pixel[3] // 16) << 12) + ((pixel[0] // 16) << 8) + ((pixel[1] // 16) << 4) + ((pixel[2] // 16) << 0)
self.encode_byte(val & 255)
self.encode_byte(val >> 8)
self.encode_end()
def get_pixel(self, image, x, y):
if self.reverse:
return image.getpixel((image.width - x - 1, image.height - y - 1))
else:
return image.getpixel((x, y))
@staticmethod
def create(filename, size_format=1, reverse=False, encode_mixin=RawMixin):
class ResultClass(ImageEncoder, encode_mixin):
def __init__(self, *args, **kwargs):
ImageEncoder.__init__(self, *args, **kwargs)
encode_mixin.__init__(self)
return ResultClass(filename, size_format, reverse)
def main():
parser = argparse.ArgumentParser(description='Bitmaps encoder')
parser.add_argument('input', action="store", help="Input file name")
parser.add_argument('output', action="store", help="Output file name")
parser.add_argument('--format', action="store", help="Output format")
parser.add_argument("--reverse", help="Invert pixels order", action="store_true")
parser.add_argument("--rle", help="Enable RLE compression", action="store_true")
parser.add_argument("--lz4", help="Enable RLE compression", action="store_true")
parser.add_argument("--rows", help="Image rows count (for 1bit format)", type=int, default=1)
parser.add_argument("--size-format", help="Header image size format (1 or 2 bytes)", type=int, default=1)
args = parser.parse_args()
image = Image.open(args.input)
output = args.output
byte_encoder = RawMixin
if args.rle:
byte_encoder = RleMixin
if args.lz4:
byte_encoder = Lz4Mixin
encoder = ImageEncoder.create(output, args.size_format, args.reverse, byte_encoder)
if args.format == "1bit":
encoder.encode_1bit(image, args.rows)
elif args.format == "4bits":
encoder.encode_4bits(image)
elif args.format == "8bits":
encoder.encode_8bits(image)
elif args.format == "4/4/4/4":
encoder.encode_4_4_4_4(image)
elif args.format == "5/6/5":
encoder.encode_5_6_5(image)
if __name__ == "__main__":
main()
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env python
from __future__ import division, print_function
curr = 0
idx = 0
byte = 0
def push4bits(val):
global curr, idx, byte
val = val & 0x0f
curr += val << idx
idx += 4
if idx == 8:
print("0x%02X," % curr, end=' ')
idx = 0
curr = 0
byte += 1
if byte % 16 == 0:
print()
cluster = 0
def pushCluster(val):
global cluster
push4bits(val)
push4bits(val >> 4)
push4bits(val >> 8)
cluster += 1
def pushFile(size):
sectors = size // 512
count = sectors // 8
for i in range(count - 1):
pushCluster(cluster + 1)
pushCluster(0xFFF)
def pushDisk(eeprom, flash):
global curr, idx, byte, cluster
curr = idx = byte = cluster = 0
print("Disk with %dk EEPROM and %dk FLASH:" % (eeprom, flash))
pushCluster(0xFF8)
pushCluster(0xFFF)
pushFile(flash * 1024)
if eeprom > 0:
pushFile(eeprom * 1024)
while byte < 512:
push4bits(0)
print()
#pushDisk(32, 512)
pushDisk(32, 512)
pushDisk(0, 2048)
+141
View File
@@ -0,0 +1,141 @@
#
# Most of this code is from clang_complete:
# https://github.com/xavierd/clang_complete/blob/master/plugin/libclang.py
#
import os
import sys
import re
from clang.cindex import *
# Check if libclang is able to find the builtin include files.
#
# libclang sometimes fails to correctly locate its builtin include files. This
# happens especially if libclang is not installed at a standard location. This
# function checks if the builtin includes are available.
def canFindBuiltinHeaders(index, args = []):
flags = 0
currentFile = ("test.c", '#include "stddef.h"')
try:
tu = index.parse("test.c", args, [currentFile], flags)
except TranslationUnitLoadError as e:
return 0
return len(tu.diagnostics) == 0
# Derive path to clang builtin headers.
#
# This function tries to derive a path to clang's builtin header files. We are
# just guessing, but the guess is very educated. In fact, we should be right
# for all manual installations (the ones where the builtin header path problem
# is very common) as well as a set of very common distributions.
def getBuiltinHeaderPath(library_path):
if not library_path:
return None
if os.path.isfile(library_path):
library_path = os.path.dirname(library_path)
knownPaths = [
library_path + "/../lib/clang", # default value
library_path + "/../clang", # gentoo
library_path + "/clang", # opensuse
library_path + "/", # Google
"/usr/lib64/clang", # x86_64 (openSUSE, Fedora)
"/usr/lib/clang"
]
for path in knownPaths:
try:
subDirs = [f for f in os.listdir(path) if os.path.isdir(path + "/" + f)]
subDirs = sorted(subDirs) or ['.']
path = path + "/" + subDirs[-1] + "/include"
#print("searching builtins in " + path)
if canFindBuiltinHeaders(index, ["-I" + path]):
return path
except:
pass
return None
def natural_sort_key(s):
return [int(text) if text.isdigit() else text.lower()
for text in re.split(r'(\d+)', s)]
def findLibClang():
if sys.platform == "darwin":
knownPaths = [
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
"/Library/Developer/CommandLineTools/usr/lib"
]
if Config.library_path:
knownPaths.insert(0, Config.library_path)
libSuffix = ".dylib"
elif sys.platform.startswith("linux"):
base_dirs = ["/usr/lib", "/usr/local/lib"]
version_paths = []
for base_dir in base_dirs:
if os.path.exists(base_dir):
for item in os.listdir(base_dir):
if item.startswith("llvm-") and os.path.isdir(os.path.join(base_dir, item, "lib")):
llvm_lib_path = os.path.join(base_dir, item, "lib")
version = item[5:]
version_paths.append((version, llvm_lib_path))
version_paths.sort(key=lambda x: natural_sort_key(x[0]), reverse=True)
knownPaths = [path for _, path in version_paths]
knownPaths.extend(["/usr/local/lib", "/usr/lib", "/usr/lib64"])
libSuffix = ".so"
elif sys.platform == "win32" or sys.platform == "msys":
knownPaths = os.environ.get("PATH").split(os.pathsep)
libSuffix = ".dll"
else:
# Unsupported platform
return None
for path in knownPaths:
# print("trying " + path, file=sys.stderr)
if os.path.exists(path + "/libclang" + libSuffix):
return path
elif (sys.platform == "win32" or sys.platform == "msys"):
# Check for versioned and non-versioned libclang.dll if on msys
pattern = re.compile(r'^libclang(-\d+(\.\d+)?)?\.dll$')
if os.path.exists(path):
for filename in os.listdir(path):
if pattern.match(filename):
return os.path.join(path, filename)
# If no known path is found
return None
def initLibClang():
global index
library_path = findLibClang()
if library_path:
print("libclang found: " + library_path, file=sys.stderr)
if os.path.isdir(library_path):
Config.set_library_path(library_path)
else:
Config.set_library_file(library_path)
else:
print("WARN (find_clang): libclang path not found", file=sys.stderr)
Config.set_compatibility_check(False)
try:
index = Index.create()
except Exception as e:
print("ERROR (find_clang): could not load libclang from '%s'." % library_path, file=sys.stderr)
print(" : detected platform '%s'" % sys.platform, file=sys.stderr)
return False
global builtin_hdr_path
builtin_hdr_path = getBuiltinHeaderPath(library_path)
if builtin_hdr_path:
print("builtin header path found: " + builtin_hdr_path, file=sys.stderr)
# Everything is OK, libclang can be used
return True
+105
View File
@@ -0,0 +1,105 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys, os
import glob
from PIL import Image, ImageDraw, ImageFont
chars_en = u""" !"#$%&'()*+,-./0123456789:;<=>?°ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz~|≥"""
chars_fr = u"""éèàîç"""
chars_da = u"""åæøÅÆØ"""
chars_de = u"""ÄäÖöÜüß"""
chars_cz = u"""ěščřžýáíéňóůúďťĚŠČŘŽÝÁÍÉŇÓÚŮĎŤ"""
chars_es = u"""ÑñÁáÉéÍíÓóÚú"""
chars_fi = u"""åäöÅÄÖ"""
chars_it = u"""àù"""
chars_pl = u"""ąćęłńóśżźĄĆĘŁŃÓŚŻŹ"""
chars_pt = u"""ÁáÂâÃãÀàÇçÉéÊêÍíÓóÔôÕõÚú"""
chars_ru = u"""АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя"""
chars_ua = u"""БГҐДЄЖЗИЇЙЛПУФЦЧШЩЬЮЯбвгґдєжзиїйклмнптфцчшщьюя"""
COUNT_EXTRA_CHARS = 21
chars_extra = u"".join([chr(1+i) for i in range(COUNT_EXTRA_CHARS)])
chars = chars_en + chars_extra + chars_fr + chars_da + chars_de + chars_cz + chars_es + chars_fi + chars_it + chars_pl + chars_pt + chars_ru + chars_ua
def createFontBitmap(filename, fontname, fontsize, fontoffset, foreground, background, coordsfile=True):
coords = []
font_filename = 'fonts/' + fontname + '.ttf'
print("Font filename: %s" % font_filename)
font = ImageFont.truetype(font_filename, fontsize)
extraImage = None
extraFilename = 'fonts/extra_%dpx.png' % fontsize
try:
extraImage = Image.open(extraFilename)
extraImage = extraImage.convert('RGB');
except IOError:
print("Missing extra symbol filename %s" % extraFilename)
def getCharWidth(c):
if c in chars_extra:
if extraImage:
# Extra characters 16px
#if fontsize == 16:
if ord(c) <= 9:
return 13
else:
return 15
else:
return 0
elif c == " ":
return 4
else:
w, h = font.getsize(c)
return w + 1
def getFontWidth():
width = 0
for c in chars:
width += getCharWidth(c)
return width
width = getFontWidth()
image = Image.new('RGB', (width, fontsize + 4), background)
draw = ImageDraw.Draw(image)
width = 0
for c in chars:
coords.append(width)
if c in chars_extra:
if extraImage and c == chars_extra[0]:
image.paste(extraImage.copy(),(width,1))
elif c == " ":
pass
elif c == "j" and fontsize == 32:
w, h = font.getsize(c)
draw.text((width + 2, fontoffset), c, fill=foreground, font=font)
elif c == "j" and fontsize == 64:
aw, h = font.getsize(c)
draw.text((width + 3, fontoffset), c, fill=foreground, font=font)
else:
w, h = font.getsize(c)
draw.text((width + 1, fontoffset), c, fill=foreground, font=font)
width += getCharWidth(c)
coords.append(width)
image.save(filename + ".png")
if coordsfile:
with open(filename + ".specs", "w") as f:
f.write(",".join(str(tmp) for tmp in coords))
# Main
if __name__ == "__main__":
createFontBitmap(sys.argv[4], sys.argv[1], int(sys.argv[2]), int(sys.argv[3]), (0,0,0), (255,255,255))
+378
View File
@@ -0,0 +1,378 @@
#!/usr/bin/env python
languages = (
"en",
"fr",
"se",
"it",
"cz",
"de",
"pt",
"es",
"pl",
"nl"
)
tts_languages = {
"en",
"fr",
"it",
"cz",
"de",
"pt"
}
options_taranis_x9d = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"haptic": ("HAPTIC", "YES", "NO"),
"nogvars": ("GVARS", "NO", "YES"),
"sqt5font": ("FONT", "SQT5", None),
"noras": ("RAS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"eu": ("MODULE_PROTOCOL_D8", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO")
}
options_taranis_x9dp = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"sqt5font": ("FONT", "SQT5", None),
"noras": ("RAS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"eu": ("MODULE_PROTOCOL_D8", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO")
}
options_taranis_x7 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"sqt5font": ("FONT", "SQT5", None),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"eu": ("MODULE_PROTOCOL_D8", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
}
options_taranis_x9lite = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"sqt5font": ("FONT", "SQT5", None),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"eu": ("MODULE_PROTOCOL_D8", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO")
}
options_taranis_xlite = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"sqt5font": ("FONT", "SQT5", None),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"eu": ("MODULE_PROTOCOL_D8", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO")
}
options_taranis_xlites = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"sqt5font": ("FONT", "SQT5", None),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"eu": ("MODULE_PROTOCOL_D8", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO")
}
options_taranis_x9e = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"sqt5font": ("FONT", "SQT5", None),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"shutdownconfirm": ("SHUTDOWN_CONFIRMATION", "YES", "NO"),
"eu": ("MODULE_PROTOCOL_D8", "NO", "YES"),
"horussticks": ("STICKS", "HORUS", "STANDARD"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO")
}
options_horus_x12s = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"eu": ("MODULE_PROTOCOL_D8", "NO", "YES"),
"pcbdev": ("PCBREV", "10", None),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"internalaccess": [("INTERNAL_MODULE_PXX1", "NO", None), ("INTERNAL_MODULE_PXX2", "YES", None)],
"externalaccessmod": ("HARDWARE_EXTERNAL_ACCESS_MOD", "YES", "NO"),
}
options_horus_x10 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"eu": ("MODULE_PROTOCOL_D8", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"internalaccess": [("INTERNAL_MODULE_PXX1", "NO", None), ("INTERNAL_MODULE_PXX2", "YES", None)],
"externalaccessmod": ("HARDWARE_EXTERNAL_ACCESS_MOD", "YES", "NO"),
}
options_horus_x10express = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"luac": ("LUA_COMPILER", "YES", "NO"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"eu": ("MODULE_PROTOCOL_D8", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
}
options_betafpv_lr3pro = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
}
options_jumper_tlite = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
}
options_jumper_t12 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"internalmulti": ("INTERNAL_MODULE_MULTI", "YES", "NO"),
"afhds3": ("AFHDS3", "YES", "NO")
}
options_jumper_t16 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"internalmulti": ("INTERNAL_MODULE_MULTI", "YES", "NO"),
"bluetooth": ("BLUETOOTH", "YES", "NO"),
"externalaccessmod": ("HARDWARE_EXTERNAL_ACCESS_MOD", "YES", "NO"),
}
options_jumper_t18 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"bluetooth": ("BLUETOOTH", "YES", "NO"),
"externalaccessmod": ("HARDWARE_EXTERNAL_ACCESS_MOD", "YES", "NO"),
}
options_radiomaster_tx12 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO")
}
options_radiomaster_tx12mk2 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO"),
"internalelrs": ("INTERNAL_MODULE_ELRS", "YES", "NO"),
}
options_radiomaster_zorro = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO"),
"internalelrs": ("INTERNAL_MODULE_ELRS", "YES", "NO"),
}
options_radiomaster_boxer = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO"),
"internalelrs": ("INTERNAL_MODULE_ELRS", "YES", "NO"),
}
options_radiomaster_gx12 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO"),
"internalelrs": ("INTERNAL_MODULE_ELRS", "YES", "NO"),
}
options_radiomaster_pocket = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO"),
"internalelrs": ("INTERNAL_MODULE_ELRS", "YES", "NO"),
}
options_radiomaster_mt12 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO"),
"internalelrs": ("INTERNAL_MODULE_ELRS", "YES", "NO"),
}
options_radiomaster_t8 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"bindkey": ("BIND_KEY", "NO", "YES"),
}
options_radiomaster_tx16s = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"bluetooth": ("BLUETOOTH", "YES", "NO"),
"internalgps": ("INTERNAL_GPS", "YES", "NO"),
"externalaccessmod": ("HARDWARE_EXTERNAL_ACCESS_MOD", "YES", "NO"),
}
options_fatfish_f16 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"bluetooth": ("BLUETOOTH", "YES", "NO"),
"internalgps": ("INTERNAL_GPS", "YES", "NO"),
"externalaccessmod": ("HARDWARE_EXTERNAL_ACCESS_MOD", "YES", "NO"),
}
options_helloradiosky_v12 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO"),
"internalelrs": ("INTERNAL_MODULE_ELRS", "YES", "NO"),
}
options_helloradiosky_v14 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"afhds3": ("AFHDS3", "YES", "NO"),
"internalelrs": ("INTERNAL_MODULE_ELRS", "YES", "NO"),
}
options_helloradiosky_v16 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
"flexr9m": ("MODULE_PROTOCOL_FLEX", "YES", None),
"bluetooth": ("BLUETOOTH", "YES", "NO"),
"internalgps": ("INTERNAL_GPS", "YES", "NO"),
"externalaccessmod": ("HARDWARE_EXTERNAL_ACCESS_MOD", "YES", "NO"),
}
options_commando8_t8 = {
"noheli": ("HELI", "NO", "YES"),
"lua": ("LUA", "YES", "NO_MODEL_SCRIPTS"),
"nogvars": ("GVARS", "NO", "YES"),
"faimode": ("FAI", "YES", None),
"faichoice": ("FAI", "CHOICE", None),
"nooverridech": ("OVERRIDE_CHANNEL_FUNCTION", "NO", "YES"),
}
+135
View File
@@ -0,0 +1,135 @@
#!/usr/bin/env python3
# Copyright (C) 2020 Inria
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
"""Generate the vectors table for a given STM32 CPU line."""
import os
import argparse
import re
VECTORS_FORMAT = """
#include "{cpu_line}.h"
#include "cortex_m_isr.h"
void Default_Handler() {{ default_isr_handler(); }}
/* {cpu_line} specific interrupt vectors */
{isr_lines}
/* CPU specific interrupt vector table */
const isr_t _isr_vector[] ISR_VECTOR = {{
[0] = (isr_t)&_estack,
{irq_lines}
}};
"""
def parse_cmsis(cmsis_dir, cpu_line):
"""Parse the CMSIS to get the list IRQs."""
cpu_line_cmsis = os.path.join("{}/{}.h".format(cmsis_dir, cpu_line.lower()))
with open(cpu_line_cmsis, "rb") as cmsis:
cmsis_content = cmsis.readlines()
irq_lines = []
use_line = False
for line in cmsis_content:
try:
line = line.decode()
except UnicodeDecodeError:
# skip line that contains non unicode characters
continue
# start filling lines after interrupt Doxygen comment
if "typedef enum" in line:
irq_lines = [] # Cleanup any previous content
use_line = True
# enum not yet detected: skip
if not use_line:
continue
# Stop at the end of the IRQn_Type enum definition
if "IRQn_Type" in line:
break
# use a regexp to get the available IRQs
match = re.match(r"[ ]+([a-zA-Z0-9_]+_IRQn)[ ]+= \d+", line)
# Skip lines that don't match
if match:
irq_lines.append(match.group(1).strip())
isrs = [
{"irq": irq + " + 16", "func": irq.rsplit("_", 1)[0] + "_IRQHandler"}
for irq in irq_lines
]
return {"isrs": isrs, "cpu_line": cpu_line}
def generate_vectors(context):
"""Use vector template string to generate the vectors C file."""
isr_line_format = "WEAK_DEFAULT void {func}();"
irq_line_format = " [{irq:<35}] = {func},"
isr_lines = []
irq_lines = []
cortex_m_irqs = [
"Reset_Handler",
"NMI_Handler",
"HardFault_Handler",
"MemManage_Handler",
"BusFault_Handler",
"UsageFault_Handler",
"0",
"0",
"0",
"0",
"SVC_Handler",
"DebugMon_Handler",
"0",
"PendSV_Handler",
"SysTick_Handler",
]
irqn = 1
for isr in cortex_m_irqs:
if isr != "0":
isr_lines.append(isr_line_format.format(func=isr))
irq_lines.append(irq_line_format.format(irq=irqn, func=isr))
irqn += 1
for isr in context["isrs"]:
isr_line = isr_line_format.format(**isr)
if isr_line not in isr_lines:
isr_lines.append(isr_line)
irq_lines.append(irq_line_format.format(**isr))
vectors_content = VECTORS_FORMAT.format(
cpu_line=context["cpu_line"],
isr_lines="\n".join(isr_lines),
irq_lines="\n".join(irq_lines),
)
print(vectors_content)
def main(args):
"""Main function."""
context = parse_cmsis(args.cmsis_dir, args.cpu_line)
generate_vectors(context)
PARSER = argparse.ArgumentParser()
PARSER.add_argument("cmsis_dir", help="CMSIS directory")
PARSER.add_argument("cpu_line", help="STM32 CPU line")
if __name__ == "__main__":
main(PARSER.parse_args())
+116
View File
@@ -0,0 +1,116 @@
#!/usr/bin/env python3
import find_clang
import sys
import clang.cindex
import time
import os
structs = []
extrastructs = []
def valid_spelling(spelling):
return spelling and not spelling.startswith("(")
def build_struct(cursor, anonymousUnion=False):
if not anonymousUnion:
structs.append(cursor.spelling)
print("template <class A, class B>\nvoid copy%s(A * dest, B * src)\n{" % cursor.spelling)
for c in cursor.get_children():
if c.kind == clang.cindex.CursorKind.UNION_DECL:
if valid_spelling(c.spelling):
raise Exception("Cannot handle non anonymous unions")
copied_union_member = False
for uc in c.get_children():
if not valid_spelling(uc.spelling) or uc.kind == clang.cindex.CursorKind.PACKED_ATTR:
# Ignore
pass
else:
# per default we copy only the first member of a union and warn if there are more
# members (declare the other members NOBACKUP)
if copied_union_member:
print("Warning more than one union member (%s) in anynomous union inside struct %s, consider NOBACKUP statements" % (uc.spelling, cursor.spelling), file=sys.stderr)
else:
copy_decl(uc, uc.spelling)
copied_union_member = True
elif c.kind == clang.cindex.CursorKind.FIELD_DECL:
copy_decl(c, c.spelling)
if not anonymousUnion:
print("}\n")
def build(cursor):
result = []
for c in cursor.get_children():
if c.location.file.name == sys.argv[1]:
if c.kind == clang.cindex.CursorKind.STRUCT_DECL:
build_struct(c)
for c, spelling in extrastructs:
print("template <class A, class B>\nvoid copy%s(A * dest, B * src)\n{" % spelling)
build_struct(c, True)
print("}\n")
return result
def copy_decl(c, spelling):
children = [ch for ch in c.get_children()]
if c.type.get_array_size() > 0:
if c.type.get_array_element_type().spelling in structs:
print(" for (int i=0; i<%d; i++) {" % c.type.get_array_size())
print(" copy%s(&dest->%s[i], &src->%s[i]);" % (c.type.get_array_element_type().spelling, spelling, spelling))
print(" }")
else:
print(" memcpy(dest->%s, src->%s, sizeof(dest->%s));" % (spelling, spelling, spelling))
elif len(children) == 1 and children[0].kind == clang.cindex.CursorKind.STRUCT_DECL and not valid_spelling(children[0].spelling):
# inline declared structs
if valid_spelling(c.semantic_parent.spelling):
spelling_func = c.semantic_parent.spelling + "_" + spelling
else:
spelling_func = c.semantic_parent.semantic_parent.spelling + "_" + spelling
extrastructs.append((children[0], spelling_func))
print(" copy%s(&dest->%s, &src->%s);" % (spelling_func, spelling, spelling))
elif c.type.get_declaration().spelling in structs:
print(" copy%s(&dest->%s, &src->%s);" % (c.type.get_declaration().spelling, spelling, spelling))
else:
print(" dest->%s = src->%s;" % (spelling, spelling))
def header():
print("// This file was auto-generated by %s script on %s. Do not edit this file!\n\n\n" % (os.path.basename(sys.argv[0]), time.asctime()))
def print_translation_unit_diags(diags, prefix=''):
for diag in diags:
print(prefix + str(diag), file=sys.stderr)
print_translation_unit_diags(diag.children, ' ' + prefix)
def main():
if not find_clang.initLibClang():
sys.exit(-1)
index = find_clang.index
args = ['-x', 'c++', '-std=c++11'] + sys.argv[2:]
if find_clang.builtin_hdr_path:
args.append("-I" + find_clang.builtin_hdr_path)
translation_unit = index.parse(sys.argv[1], args)
if translation_unit.diagnostics:
print_translation_unit_diags(translation_unit.diagnostics)
sys.exit(-1)
header()
build(translation_unit.cursor)
if __name__ == "__main__":
main()
+575
View File
@@ -0,0 +1,575 @@
#!/usr/bin/python
import os
import sys
import find_clang
from clang.cindex import *
# debug
import asciitree # must be version 0.2
# output
import jinja2
DEBUG_ATTRS = ['layoutData','topbarData','widgetData']
#USE_FAKE_STRUCT = False
USE_FAKE_STRUCT = True
def node_children(node):
l = list(c for c in node.get_children() if c is not None)
if len(l) > 0:
return l
return []
#decl = node.type.get_declaration()
#return list(c for c in decl.get_children() if c is not None)
def print_node(node):
text = node.spelling or node.displayname
kind = str(node.kind)[str(node.kind).index('.')+1:]
if CursorKind.FIELD_DECL == node.kind:
size = node.type.get_size()
return '{} {} {}'.format(kind, text, size)
return '{} {}'.format(kind, text)
def dump_node(node):
print(asciitree.draw_tree(node, node_children, print_node))
has_errors = False
def print_error(*args):
print("ERROR:",*args,file=sys.stderr)
has_errors = True
def print_debug(*args):
print("DBG:",*args,file=sys.stderr)
def bail_out(*args):
print_error(*args)
sys.exit(-1)
_anon_cnt = 0
def get_next_anon():
global _anon_cnt
label = str(_anon_cnt)
_anon_cnt = _anon_cnt + 1
return label
SIGNED_TYPES = ['bool', 'char', 'short', 'int', 'long' ]
def map_type(type_name):
if 'unsigned' in type_name:
return 'unsigned'
elif ('signed' in type_name) or (type_name in SIGNED_TYPES):
return 'signed'
return type_name
def mangle_type(type_name):
return type_name.replace(':','_')
# Cursor or Type
def get_type(obj):
if isinstance(obj,Cursor):
return obj.type
else:
return obj
def is_string(type):
if type.kind == TypeKind.CONSTANTARRAY:
if type.element_type.spelling == 'char':
return True
return False
class AST_Element:
def __init__(self, name, cursor):
self.name = name
self.cursor = cursor
self.elmts = []
def get_children(self):
return self.cursor.get_children()
def get_elmts(self):
return self.elmts
def append(self,elmt):
if isinstance(elmt, list):
self.elmts.extend(elmt)
else:
self.elmts.append(elmt)
def str(self):
if hasattr(self,'value'):
if hasattr(self,'ann'):
return '{}:\t{} ({})'.format(self.name,self.value,self.ann)
return '{}:\t{}'.format(self.name,self.value)
elif hasattr(self,'type'):
if hasattr(self,'var_type'):
return '{} {}\t{}'.format(self.type, self.var_type, self.name)
else:
return '{}\t{}'.format(self.type, self.name)
return self.name
class FieldAST(AST_Element):
def __init__(self, name, cursor):
super(FieldAST, self).__init__(name, cursor)
self.is_array = False
self.func = 'NULL'
if self.name in DEBUG_ATTRS:
#print("# field name={} type={} canon={} decl={}".format(self.name,cursor.type.spelling,cursor.type.get_canonical().spelling,str(cursor.type.kind)))
#dump_node(cursor)
pass
if isinstance(cursor,Cursor):
t = cursor.type
if cursor.is_bitfield():
self.bits = cursor.get_bitfield_width()
else:
# cursor is a type...
t = cursor
if not hasattr(self,'bits'):
self.bits = t.get_size() * 8
if t.kind == TypeKind.TYPEDEF:
self.type = map_type(t.get_canonical().spelling)
elif t.kind == TypeKind.CONSTANTARRAY:
self.type = t.element_type.spelling
self.is_array = True
self.length = t.element_count
if self.type == 'char':
self.type = 'string'
else:
self.type = map_type(t.get_canonical().spelling)
class StructAST(AST_Element):
type = 'struct'
def __init__(self, name, cursor, alt_name=''):
self.var_name = name
if len(alt_name) > 0:
name = alt_name
if name == '' or (isinstance(cursor,Cursor) and cursor.is_anonymous()):
name = self.name_prefix() + 'anonymous_' + get_next_anon()
else:
name = self.name_prefix() + name
self.func = 'NULL'
super(StructAST, self).__init__(name, cursor)
def name_prefix(self):
return self.type + '_'
class UnionAST(StructAST):
type = 'union'
class EnumAST(AST_Element):
type = 'enum'
def __init__(self, name, cursor):
self.var_name = name
super(EnumAST, self).__init__('enum_' + name, cursor)
class AST:
name = 'ROOT'
def __init__(self):
self.structs = []
self.enums = []
def has_enum(self,name):
for e in self.enums:
if e.name == name:
return True
return False
def get_enums(self):
return self.enums
def get_structs(self):
return self.structs
def has_struct(self,name):
for s in self.structs:
if s.name == name:
return True
return False
def get_struct(self,name):
for s in self.structs:
if s.name == name:
return s
return None
def get_elmts(self):
return self.enums + self.structs
def append(self, elmt):
if isinstance(elmt, StructAST):
self.structs.append(elmt)
elif isinstance(elmt, EnumAST):
self.enums.append(elmt)
def str(self):
return self.name
def parse_struct(ast, node, alt_name):
st = None
if ast is not RootAST:
ast.var_type = ast.type
ast.type = 'struct'
st = StructAST(node.spelling, node, alt_name)
if (ast is RootAST):
old_st = RootAST.get_struct(st.name)
if old_st is not None:
return old_st
st.use_idx = False
ann = get_annotations(node)
if len(ann) > 0:
#print(ann)
for a in ann:
if a['type'] == 'idx' and a['val'] == 'true':
st.use_idx = True
break
for c in node.get_children():
parse_field(st,c)
if st is not ast:
ast.append(st)
#print_debug("IDX ",st.name, " ", st.use_idx)
return st
def parse_union(ast, node):
st = None
if ast is not RootAST:
ast.var_type = ast.type
ast.type = 'union'
st = UnionAST(node.spelling, node)
if (ast is RootAST):
old_st = RootAST.get_struct(st.name)
if old_st is not None:
return old_st
for c in node.get_children():
if c.kind == CursorKind.FIELD_DECL:
parse_field(st,c)
ann = get_annotations(node)
if len(ann) > 0:
for a in ann:
if a['type'] == 'func':
st.func = a['val']
if st is not ast:
ast.append(st)
return st
def get_annotations(node):
l = list()
for c in node.get_children():
if c.kind == CursorKind.ANNOTATE_ATTR:
ann = c.displayname.split(':')
l.append({'type':ann[0], 'val':ann[1]})
return l
def parse_field_record(f, node):
alt_name = ''
decl = node.type.get_declaration()
if not decl.spelling == '':
alt_name = mangle_type(node.type.spelling)
#print("alt_name = " + alt_name)
st = parse_node(RootAST,decl,alt_name)
if st is not None:
f.var_type = st.name
f.var_name = alt_name
f.type = st.type
def make_fake_array_struct(f, node_type, use_idx):
# if not USE_FAKE_STRUCT:
# f.var_type = node_type.spelling
# # if is_string(get_type(node_type)):
# # f.type = 'array'
# # f.length = get_type(node_type).element_count
# return
field = FieldAST('val', node_type)
#field.is_fake = True
type_name = field.type + '_' + str(field.bits)
struct_name = 'struct_' + type_name
if USE_FAKE_STRUCT and not RootAST.has_struct(struct_name):
#print("# field created " + struct_name)
st = StructAST(type_name, node_type)
st.append(field)
st.used_in_arrays = True
st.use_idx = use_idx
RootAST.append(st)
f.var_name = f.type
f.type = 'array'
f.var_type = struct_name
#f.var_name = field.type
def parse_field_array(f, node):
et = node.type.element_type
if f.type != 'string':
elmt_decl = et.get_declaration()
use_idx = True
ann = get_annotations(node)
if len(ann) > 0:
for a in ann:
if a['type'] == 'idx' and a['val'] == 'false':
use_idx = False
elif a['type'] == 'raw':
f.raw = a['val']
# let's see first if it's some kind of struct/union/enum
elmt_st = parse_node(RootAST, elmt_decl)
if elmt_st is not None:
f.type = 'array'
f.var_type = elmt_st.name
f.var_name = elmt_st.var_name
# mark array usage for unions
elmt_st.used_in_arrays = True
elmt_st.use_idx = use_idx
elif elmt_decl.kind == CursorKind.TYPEDEF_DECL:
# it's some typedef
#print_error("TYPEDEF {} {}".format(f.name, elmt_decl.spelling));
make_fake_array_struct(f, elmt_decl, use_idx)
elif et.kind == TypeKind.CONSTANTARRAY:
# it's an array:
# let's create a fake struct with the element type
#print_error("ARRAY {} {}".format(f.name, elmt_decl.spelling));
make_fake_array_struct(f, et, use_idx)
else:
pass
#print("# unknown array attr: {} {}".format(str(elmt_decl.kind), f.name))
def parse_field(ast,node):
if (not node.is_anonymous()) and (node.kind != CursorKind.FIELD_DECL):
return
f = FieldAST(node.spelling, node)
if node.type.kind in [TypeKind.ELABORATED, TypeKind.RECORD, TypeKind.ENUM]:
parse_field_record(f, node)
elif node.type.kind == TypeKind.CONSTANTARRAY:
parse_field_array(f, node)
ann = get_annotations(node)
if len(ann) > 0:
is_cust = False
is_enum = False
for a in ann:
if a['type'] == 'enum':
is_enum = True
enum_name = 'enum_' + a['val']
f.var_type = enum_name
if not RootAST.has_enum(enum_name):
parse_node(RootAST,get_top_node(a['val']))
elif a['type'] == 'func':
f.func = a['val']
elif a['type'] == 'name':
f.name = a['val']
elif a['type'] == 'read':
f.f_read = a['val']
is_cust = True
elif a['type'] == 'write':
f.f_write = a['val']
is_cust = True
elif a['type'] == 'array':
array_attrs = a['val'].split('|')
elmt_size = int(array_attrs[0])
f.var_type = array_attrs[1]
f.type = 'array'
f.length = int(f.bits / elmt_size)
f.func = array_attrs[2]
elif a['type'] == 'skip':
f.skip = True
if is_enum and not is_cust:
f.type = 'enum'
if len(f.name) == 0:
print_error("in '{}', field of type '{}' does not have a name"
.format(ast.name,f.var_type))
return
ast.append(f)
def parse_enum_field(ast,node):
ann = get_annotations(node)
if len(ann) > 0:
for a in ann:
if a['type'] == 'skip':
return
enum_value = node.spelling
if '_' in enum_value:
enum_value = enum_value[enum_value.index('_')+1:]
st = AST_Element(enum_value, node)
st.value = node.spelling #node.enum_value
ast.append(st)
# debug
if len(ann) > 0:
#print(ann)
st.ann = ann
def parse_enum(ast,node):
st = EnumAST(node.spelling or node.displayname, node)
for c in node.get_children():
parse_enum_field(st,c)
ast.append(st)
return st
def parse_node(ast,node,alt_name=''):
st = None
if node.kind in [CursorKind.STRUCT_DECL,CursorKind.CLASS_DECL]:
st = parse_struct(ast,node,alt_name)
elif node.kind == CursorKind.UNION_DECL:
st = parse_union(ast,node)
elif node.kind == CursorKind.FIELD_DECL:
parse_field(ast,node)
elif node.kind == CursorKind.ENUM_DECL:
st = parse_enum(ast,node)
else:
pass
return st
### main ###
def get_top_node(name):
node = translation_unit.cursor
for c in node.get_children():
if c.spelling == name:
# struct found
return c.get_definition()
bail_out(f"'{name}' not found")
def ast_children(ast_node):
if ast_node is not None:
return ast_node.get_elmts()
return []
def print_ast_node(ast_node):
#if isinstance(ast_node,dict):
# return str(ast_node)
return ast_node.str()
if len(sys.argv) < 2:
bail_out(f"usage: {sys.argv[0]} [header file name] [template] [node name] [additional compile args]")
if not find_clang.initLibClang():
sys.exit(-1)
index = find_clang.index
args = ['-x', 'c++', '-std=c++11', '-Wno-deprecated-register'] + sys.argv[4:]
if find_clang.builtin_hdr_path:
args.append("-I" + find_clang.builtin_hdr_path)
translation_unit = index.parse(sys.argv[1], args)
def show_tu_diags(diags, prefix=''):
tu_errors = 0
for diag in diags:
tu_errors = tu_errors + 1
print_error(prefix + str(diag))
show_tu_diags(diag.children, ' ' + prefix)
return tu_errors
tu_errors = show_tu_diags(translation_unit.diagnostics)
if tu_errors > 0:
bail_out("{} error(s) while compiling '{}'".format(tu_errors, sys.argv[1]))
RootAST = AST()
root_nodes_name = ''
top_node_names = sys.argv[3].split(',')
# cycle on top nodes:
for tn in top_node_names:
top_node = get_top_node(tn)
#dump_node(top_node)
parse_node(RootAST, top_node)
if len(root_nodes_name) > 0:
root_nodes_name = root_nodes_name + '_' + tn
else:
root_nodes_name = tn
# Do not generate anything we had some errors
if has_errors:
sys.exit(-1)
#print("Enums:", RootAST.get_enums())
#print("Structs:", RootAST.get_structs())
#print(asciitree.draw_tree(RootAST, ast_children, print_ast_node))
#
# Template rendering
#
__max_str_len = 0
def max_len(str):
global __max_str_len
if len(str) > __max_str_len:
__max_str_len = len(str)
return str
def get_max_len():
global __max_str_len
return __max_str_len
def max_bits(struct):
bits = 0
for s in struct.get_elmts():
if s.bits > bits:
bits = s.bits
return bits
def padding_bits(array):
array_bits = (array.bits // array.length) * array.length
return array.bits - array_bits
template = jinja2.Template(open(sys.argv[2]).read(), lstrip_blocks=True, trim_blocks=True)
template.globals['max_len'] = max_len
template.globals['get_max_len'] = get_max_len
template.globals['max_bits'] = max_bits
template.globals['padding_bits'] = padding_bits
## fixme: root_node_name needs to be mangled (contains ',')
print(template.render(root=RootAST,root_nodes=top_node_names,root_node_name=root_nodes_name))
+18
View File
@@ -0,0 +1,18 @@
# Hardware Definitions
The Python scripts in this directory are responsible for generating different
include files used to describe the hardware to the software.
## Data model
The data model supported is described `models.py`. A script
(`json_validator.py`) is available that validates the JSON files based on this
model.
## Code generator
The various include files are generated based on Jinja2 templates and `generator.py`
provided with a JSON target definition.
`legacy_names.py` contains some hard-coded definitions providing additional
information related to analog inputs (by target; mostly names and labels).
+28
View File
@@ -0,0 +1,28 @@
#from os import path
import argparse
from hal_json import parse_defines
from generator import generate_from_template
# def eprint(*args, **kwargs):
# print(*args, file=sys.stderr, **kwargs)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Process hardware definitions')
parser.add_argument('filename', metavar='filename', nargs='+')
parser.add_argument('-i', metavar='input', choices=['json','defines'], default='json')
parser.add_argument('-t', metavar='template')
parser.add_argument('-T', metavar='target')
args = parser.parse_args()
if args.i == 'defines':
for filename in args.filename:
parse_defines(filename, args.T)
elif args.i == 'json':
for filename in args.filename:
generate_from_template(filename, args.t, args.T)
+102
View File
@@ -0,0 +1,102 @@
import json
import os
import sys
import jinja2
import pydantic
import legacy_names
import json_index
from models import HardwareDefinition, KeyEnum
MAIN_CONTROL_LUT = {
# 2 Gimbal radios
"LH": {"str": "Rud", "local": "STR_STICK_NAMES0"},
"LV": {"str": "Ele", "local": "STR_STICK_NAMES1"},
"RV": {"str": "Thr", "local": "STR_STICK_NAMES2"},
"RH": {"str": "Ail", "local": "STR_STICK_NAMES3"},
# Surface radios
"ST": {"str": "ST", "local": "STR_SURFACE_NAMES0"},
"TH": {"str": "TH", "local": "STR_SURFACE_NAMES1"},
}
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def is_ext_input(input):
if str(getattr(input, "type", "")) != "FLEX":
return False
name = str(getattr(input, "name", ""))
return name.startswith("EXT")
def generate_from_template(json_filename, template_filename, target):
with open(json_filename) as json_file:
raw_data = json_file.read()
# Validate using Pydantic model
try:
hw_def = HardwareDefinition.from_json(raw_data)
except pydantic.ValidationError as e:
eprint(f"ERROR validating '{json_filename}' (target={target}):")
for err in e.errors():
loc = " -> ".join(str(l) for l in err["loc"])
eprint(f" {loc}: {err['msg']}")
sys.exit(1)
# Load remaining fields not yet in the Pydantic model
root_obj = json.loads(raw_data)
adc_index = json_index.build_adc_index(hw_def.adc_inputs)
adc_gpios = json_index.build_adc_gpio_port_index(hw_def.adc_inputs)
switch_gpios = json_index.build_switch_gpio_port_index(hw_def.switches)
key_gpios = json_index.build_key_gpio_port_index(hw_def.keys)
trims = root_obj.get("trims")
trim_gpios = json_index.build_trim_gpio_port_index(trims)
legacy_inputs = legacy_names.inputs_by_target(target)
template_dir = os.path.dirname(os.path.abspath(template_filename))
template_name = os.path.basename(template_filename)
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(template_dir),
lstrip_blocks=True,
trim_blocks=True,
)
env.tests["ext_input"] = is_ext_input
template = env.get_template(template_name)
key_index = list([str(i) for i in KeyEnum])
context = dict(
adc_inputs=hw_def.adc_inputs,
switches=hw_def.switches,
keys=hw_def.keys,
trims=trims,
adc_index=adc_index,
adc_gpios=adc_gpios,
switch_gpios=switch_gpios,
key_gpios=key_gpios,
trim_gpios=trim_gpios,
legacy_inputs=legacy_inputs,
main_labels=MAIN_CONTROL_LUT,
key_index=key_index,
)
try:
print(template.render(context))
except jinja2.UndefinedError as e:
eprint(f"ERROR rendering template '{template_name}'"
f" with json='{json_filename}', target='{target}':")
eprint(f" {e}")
none_keys = [k for k, v in context.items() if v is None]
if none_keys:
eprint(f" None-valued context keys: {none_keys}")
sys.exit(1)
+76
View File
@@ -0,0 +1,76 @@
//
// WARNING: DO NOT EDIT THIS FILE
// This file has been generated from the target's JSON hardware description
//
{% set main_inputs = adc_inputs.inputs | selectattr('type', '==', 'STICK') | list %}
{% set n_mains = main_inputs | count %}
static const etx_hal_adc_input_t _main_inputs[] = {
{% for input in main_inputs %}
{% set label = main_labels[input.name] %}
{ "{{ input.name }}", "{{ label.str }}", {{ label.local }} },
{% endfor %}
};
{% set flex_offset = n_mains %}
{% set flex_inputs = adc_inputs.inputs | selectattr('type', '==', 'FLEX') | list %}
{% set n_flex = flex_inputs | count %}
static const etx_hal_adc_input_t _flex_inputs[] = {
{% for input in flex_inputs %}
{ "{{ input.name }}", "{{ input.label }}", "{{ input.short_label }}" },
{% endfor %}
};
{% set raw_offset = flex_offset + n_flex %}
{% set raw_inputs = adc_inputs.inputs | selectattr('type', '==', 'RAW') | list %}
{% set n_raw = raw_inputs | count %}
static const etx_hal_adc_input_t _raw_inputs[] = {
{% for input in raw_inputs %}
{ "{{ input.name }}", "{{ input.label }}", "{{ input.short_label }}" },
{% endfor %}
};
{% set vbat_offset = raw_offset + n_raw %}
{% set vbat_inputs = adc_inputs.inputs | selectattr('type', '==', 'VBAT') | list %}
{% set n_vbat = vbat_inputs | count %}
static const etx_hal_adc_input_t _vbat_inputs[] = {
{% for input in vbat_inputs %}
{ "{{ input.name }}", nullptr, nullptr },
{% endfor %}
};
{% set rtc_bat_offset = vbat_offset + n_vbat %}
{% set rtc_bat_inputs = adc_inputs.inputs | selectattr('type', '==', 'RTC_BAT') | list %}
{% set n_rtc_bat = rtc_bat_inputs | count %}
static const etx_hal_adc_input_t _rtc_bat_inputs[] = {
{% for input in rtc_bat_inputs %}
{ "{{ input.name }}", nullptr, nullptr },
{% endfor %}
};
{% set lux_offset = rtc_bat_offset + n_rtc_bat %}
{% set lux_inputs = adc_inputs.inputs | selectattr('type', '==', 'LUX') | list %}
{% set n_lux = lux_inputs | count %}
static const etx_hal_adc_input_t _lux_inputs[] = {
{% for input in lux_inputs %}
{ "{{ input.name }}", nullptr, nullptr },
{% endfor %}
};
{% set n_inputs = lux_offset + n_lux %}
static const etx_hal_adc_inputs_t _hal_inputs[] = {
{ {{ n_mains }}, 0, _main_inputs },
{ {{ n_flex }}, {{ flex_offset }}, _flex_inputs },
{ {{ n_vbat }}, {{ vbat_offset }}, _vbat_inputs },
{ {{ n_rtc_bat }}, {{ rtc_bat_offset }}, _rtc_bat_inputs },
{ {{ n_lux }}, {{ lux_offset }}, _lux_inputs },
{ {{ n_inputs }}, 0, nullptr },
};
constexpr potconfig_t _pot_default_config = (0)
{% for flex in flex_inputs %}
{% if flex.default %}
| ((potconfig_t)FLEX_{{ flex.default }} << ({{ loop.index0 }} * POT_CFG_BITS))
{% endif %}
{% endfor %}
;
+26
View File
@@ -0,0 +1,26 @@
class CFS:
def __init__(self, cfs_rgb_led, cfs_groups):
self.rgb_led = cfs_rgb_led
self.groups = cfs_groups
def parse_cfs(hw_defs):
cfs_rgb_led = 0
cfs_groups = 0
fcfs = f'FUNCTION_SWITCHES'
frgb = f'FUNCTION_SWITCHES_RGB_LEDS'
if fcfs in hw_defs:
if frgb in hw_defs:
cfs_rgb_led = 1
if f'RADIO_GX12' in hw_defs:
cfs_groups = 4
elif f'RADIO_PA01' in hw_defs:
cfs_groups = 2
else:
cfs_groups = 3
return CFS(cfs_rgb_led, cfs_groups)
+78
View File
@@ -0,0 +1,78 @@
import re
import sys
#
# Return a file handle or STDIN
#
def open_file(filename):
if filename and not filename == "-":
return open(filename)
else:
return sys.stdin
#
# Read lines of defines into a dictionary
#
def parse_hw_defs(filename):
hw_defs = {}
with open_file(filename) as file:
for line in file.readlines():
m = re.match(r"#define ([^\s]*)\s*(.*)", line.rstrip())
if m:
name = m.group(1)
value = m.group(2)
if value.isnumeric():
value = int(value)
elif not value:
value = None
hw_defs[name] = value
return hw_defs
def prune_dict(d):
# ret = {}
# for k, v in d.items():
# if v is not None:
# ret[k] = v
# return ret
return d
# class DictEncoder(json.JSONEncoder):
# def default(self, o):
# if isinstance(o, Switch):
# return prune_dict(o.__dict__)
# if isinstance(o, ADCInput):
# return prune_dict(o.__dict__)
# if isinstance(o, SPI_ADCInput):
# return prune_dict(o.__dict__)
# if isinstance(o, ADC):
# return prune_dict(o.__dict__)
# if isinstance(o, Trim):
# return prune_dict(o.__dict__)
# if isinstance(o, Key):
# return prune_dict(o.__dict__)
# if isinstance(o, Display):
# return prune_dict(o.__dict__)
# if isinstance(o, CFS):
# return prune_dict(o.__dict__)
# if isinstance(o, Misc):
# return prune_dict(o.__dict__)
# # Let the base class default method raise the TypeError
# return json.JSONEncoder.default(self, o)
#
# Parse HAL defines into JSON
#
def parse_defines(filename, target):
# hw_defs = LoggingDict(parse_hw_defs(filename))
# out_defs = {}
#
# print(json.dumps(out_defs, cls=DictEncoder, indent=2))
pass
+29
View File
@@ -0,0 +1,29 @@
//
// WARNING: DO NOT EDIT THIS FILE
// This file has been generated from the target's JSON hardware description
//
const char* const _key_labels[] = {
{% for k in key_index %}
{% set key = keys | selectattr("key","==",k) | first %}
{% if key %}
"{{ key.label }}",
{% else %}
nullptr,
{% endif %}
{% endfor %}
};
constexpr uint32_t _defined_keys = 0
{% for key in keys %}
| (1 << {{ key.key }})
{% endfor %}
;
constexpr uint8_t _n_keys = {{ keys | length }};
constexpr uint8_t _n_trims = {{ trims | length }};
static_assert(DIM(_key_labels) == MAX_KEYS, "wrong size _key_labels[]");
static_assert(_n_keys <= MAX_KEYS, "too many keys defined");
static_assert(_n_trims <= MAX_TRIMS, "too many trims defined");
+60
View File
@@ -0,0 +1,60 @@
class Display:
def __init__(self, w, h, phys_w, phys_h, depth, color, oled, bl_color):
self.w = w
self.h = h
self.phys_w = phys_w
self.phys_h = phys_h
self.depth = depth
self.color = color
self.oled = oled
self.backlight_color = bl_color
def parse_lcd(hw_defs):
fw = f'LCD_W'
fh = f'LCD_H'
fphys_w = f'LCD_PHYS_W'
fphys_h = f'LCD_PHYS_H'
fdepth = f'LCD_DEPTH'
foled = f'OLED_SCREEN'
fbl_color = f'HAS_BACKLIGHT_COLOR'
w = hw_defs[fw]
h = hw_defs[fh]
if fphys_w in hw_defs:
phys_w = hw_defs[fphys_w]
else:
phys_w = w
if fphys_h in hw_defs:
phys_h = hw_defs[fphys_h]
else:
phys_h = h
depth = hw_defs[fdepth]
if phys_w == fw:
phys_w = w
if phys_w == fh:
phys_w = h
if phys_h == fh:
phys_h = h
if phys_h == fw:
phys_h = w
if foled in hw_defs:
oled = 1
else:
oled = 0
if depth < 16:
color = 0
else:
color = 1
if fbl_color in hw_defs:
bl_color = 1
else:
bl_color = 0
return Display(w, h, phys_w, phys_h, depth, color, oled, bl_color)
+56
View File
@@ -0,0 +1,56 @@
class Misc:
def __init__(self, has_audio_mute, has_bling_leds, has_ext_module_support, has_int_module_support, sport_max_baudrate, surface, cpu, cpu_type):
self.has_audio_mute = has_audio_mute
self.has_bling_leds = has_bling_leds
self.has_ext_module_support = has_ext_module_support
self.has_int_module_support = has_int_module_support
self.sport_max_baudrate = sport_max_baudrate
self.surface = surface
self.cpu = cpu
self.cpu_type = cpu_type
def parse_misc(hw_defs):
if f'RADIO_MT12' in hw_defs:
surface = 1
else:
surface = 0
if f'SPORT_MAX_BAUDRATE' in hw_defs:
sport_max_baudrate = hw_defs[f'SPORT_MAX_BAUDRATE']
else:
sport_max_baudrate = 400000
if f'AUDIO_MUTE_GPIO' in hw_defs:
has_audio_mute = 1
else:
has_audio_mute = 0
if f'BLING_LED_STRIP_LENGTH' in hw_defs:
has_bling_leds = hw_defs[f'BLING_LED_STRIP_LENGTH']
else:
has_bling_leds = 0
if f'RADIO_T8' in hw_defs:
has_ext_module_support = 0
else:
has_ext_module_support = 1
if f'PCBX9D' in hw_defs or f'PCBX9DP' in hw_defs or f'PCBX9E' in hw_defs:
has_int_module_support = 0
else:
has_int_module_support = 1
if f'CPU_TYPE_FULL' in hw_defs:
cpu = hw_defs[f'CPU_TYPE_FULL']
else:
cpu = f'Unknown'
if f'CPU_TYPE' in hw_defs:
cpu_type = hw_defs[f'CPU_TYPE']
else:
cpu_type = f'Unknown'
return Misc(has_audio_mute, has_bling_leds, has_ext_module_support, has_int_module_support, sport_max_baudrate, surface, cpu, cpu_type)
+92
View File
@@ -0,0 +1,92 @@
#
# These methods are used to build helper indexes on data structures
#
def build_adc_index(adc_inputs):
i = 0
index = {}
for adc_input in adc_inputs.inputs:
index[str(adc_input.name)] = i
i = i + 1
return index
def append_to_index(d, key, val):
if key not in d:
d[key] = []
d[key].append(val)
def build_adc_gpio_port_index(adc_inputs):
i = 0
gpios = {}
for adc_input in adc_inputs.inputs:
if str(adc_input.adc) == "SPI":
continue
gpio = getattr(adc_input, "gpio", None)
if gpio is None:
i = i + 1
continue
if gpio not in gpios:
gpios[gpio] = []
pin = {"pin": adc_input.pin, "idx": i}
gpios[gpio].append(pin)
i = i + 1
return gpios
def build_switch_gpio_port_index(switches):
gpios = {}
for switch in switches:
sw_type = str(switch.type)
if sw_type == "2POS" or sw_type == "FSWITCH":
gpio = switch.gpio
pin = switch.pin
if gpio is not None and pin is not None:
append_to_index(gpios, gpio, pin)
elif sw_type == "3POS":
gpio_high = switch.gpio_high
pin_high = switch.pin_high
if gpio_high is not None and pin_high is not None:
append_to_index(gpios, gpio_high, pin_high)
gpio_low = switch.gpio_low
pin_low = switch.pin_low
if gpio_low is not None and pin_low is not None:
append_to_index(gpios, gpio_low, pin_low)
return gpios
def build_trim_gpio_port_index(trims):
def index_contact(gpios, contact):
gpio = contact["gpio"]
pin = contact["pin"]
if gpio and pin:
append_to_index(gpios, contact["gpio"], contact["pin"])
gpios = {}
for trim in trims:
dec = trim.get("dec")
inc = trim.get("inc")
if dec and inc:
index_contact(gpios, dec)
index_contact(gpios, inc)
return gpios
def build_key_gpio_port_index(keys):
gpios = {}
for key in keys:
append_to_index(gpios, key.gpio, key.pin)
return gpios
+116
View File
@@ -0,0 +1,116 @@
from json import JSONDecodeError
from pathlib import Path
from typing import List, Tuple
from typing_extensions import Type
from pydantic import BaseModel, ValidationError
def validate_json_files(
directory: str, model_class: Type[BaseModel]
) -> Tuple[List[str], List[Tuple[str, str]]]:
"""
Validate all JSON files in a directory against a Pydantic model.
Args:
directory: Path to the directory containing JSON files
model_class: Pydantic model class to validate against
Returns:
Tuple of (valid_files, invalid_files_with_errors)
"""
directory_path = Path(directory)
if not directory_path.exists():
raise FileNotFoundError(f"Directory {directory} does not exist")
valid_files = []
invalid_files = []
# Find all JSON files in the directory
json_files = list(directory_path.glob("*.json"))
if not json_files:
print(f"No JSON files found in {directory}")
return valid_files, invalid_files
print(f"Found {len(json_files)} JSON files to validate")
for json_file in json_files:
try:
# Read the JSON file
with open(json_file, "r", encoding="utf-8") as f:
json_data = f.read()
# Validate against given model
model_class.model_validate_json(json_data)
valid_files.append(str(json_file))
print(f"{json_file.name} - Valid")
except ValidationError as e:
error_msg = str(e)
invalid_files.append((str(json_file), error_msg))
print(f"{json_file.name} - Validation Error:")
print(f" {error_msg}")
except JSONDecodeError as e:
error_msg = f"Invalid JSON syntax: {e}"
invalid_files.append((str(json_file), error_msg))
print(f"{json_file.name} - JSON Decode Error:")
print(f" {error_msg}")
except Exception as e:
error_msg = f"Unexpected error: {e}"
invalid_files.append((str(json_file), error_msg))
print(f"{json_file.name} - Unexpected Error:")
print(f" {error_msg}")
return valid_files, invalid_files
def main():
"""Main function to run the validation"""
import argparse
parser = argparse.ArgumentParser(
description="Validate JSON files against HardwareDefinition model"
)
parser.add_argument(
"-v", "--verbose", action="store_true", help="Show detailed error messages"
)
parser.add_argument(
"directory", help="Path to directory containing JSON files to validate"
)
args = parser.parse_args()
try:
from models import HardwareDefinition
valid_files, invalid_files = validate_json_files(
args.directory, HardwareDefinition
)
print("\n=== Validation Summary ===")
print(f"Total files processed: {len(valid_files) + len(invalid_files)}")
print(f"Valid files: {len(valid_files)}")
print(f"Invalid files: {len(invalid_files)}")
if invalid_files:
print("\nInvalid files:")
for file_path, error in invalid_files:
if args.verbose:
print(f" - {Path(file_path).name}: {error}")
else:
print(f" - {Path(file_path).name}")
return 0 if not invalid_files else 1
except Exception as e:
print(f"Error during validation: {e}")
return 1
if __name__ == "__main__":
exit(main())
File diff suppressed because it is too large Load Diff
+98
View File
@@ -0,0 +1,98 @@
import sys
from typing import Any, Dict, Iterator, KeysView, Optional, ValuesView, ItemsView
class LoggingDict:
"""A dictionary wrapper that logs all lookups to stderr."""
def __init__(self, data: Optional[Dict[Any, Any]] = None):
self._data = data if data is not None else {}
def __getitem__(self, key: Any) -> Any:
"""Log the lookup and return the value."""
print(f"{str(key)}", file=sys.stderr)
return self._data[key]
def __setitem__(self, key: Any, value: Any) -> None:
"""Set an item in the dictionary."""
self._data[key] = value
def __delitem__(self, key: Any) -> None:
"""Delete an item from the dictionary."""
del self._data[key]
def __contains__(self, key: Any) -> bool:
"""Check if key exists (this is also a lookup, so log it)."""
print(f"{str(key)}", file=sys.stderr)
return key in self._data
def __len__(self) -> int:
"""Return the length of the dictionary."""
return len(self._data)
def __iter__(self) -> Iterator:
"""Return an iterator over the keys."""
return iter(self._data)
def __repr__(self) -> str:
"""Return a string representation."""
return f"LoggingDict({self._data})"
def get(self, key: Any, default: Any = None) -> Any:
"""Get a value with optional default (logs the lookup)."""
print(f"{str(key)}", file=sys.stderr)
return self._data.get(key, default)
def keys(self) -> KeysView:
"""Return dictionary keys."""
return self._data.keys()
def values(self) -> ValuesView:
"""Return dictionary values."""
return self._data.values()
def items(self) -> ItemsView:
"""Return dictionary items."""
return self._data.items()
def update(self, other: Dict[Any, Any]) -> None:
"""Update the dictionary with another dictionary."""
self._data.update(other)
def clear(self) -> None:
"""Clear all items from the dictionary."""
self._data.clear()
def pop(self, key: Any, default: Any = None) -> Any:
"""Remove and return a value (logs the lookup)."""
print(f"{str(key)}", file=sys.stderr)
if default is None:
return self._data.pop(key)
return self._data.pop(key, default)
def setdefault(self, key: Any, default: Any = None) -> Any:
"""Get a value or set/return default (logs the lookup)."""
print(f"{str(key)}", file=sys.stderr)
return self._data.setdefault(key, default)
# Example usage
if __name__ == "__main__":
# Create a logging dictionary
d = LoggingDict({"a": 1, "b": 2, "c": 3})
# These operations will be logged to stderr
print("Value of 'a':", d["a"])
print("Value of 'b':", d.get("b"))
print("Does 'c' exist?", "c" in d)
print("Does 'd' exist?", "d" in d)
# Add new items
d["d"] = 4
d["e"] = 5
# More logged lookups
print("Value of 'd':", d["d"])
print("Popped 'e':", d.pop("e"))
print("Final dictionary:", d)
+42
View File
@@ -0,0 +1,42 @@
//
// WARNING: DO NOT EDIT THIS FILE
// This file has been generated from the target's JSON hardware description
//
{% macro lua_input(first, offset, input) %}
{% set li = legacy_inputs[input.name] %}
{ {{ first }} + {{ offset }}, "{{ li.lua }}", "{{ li.description }}" },
{%- endmacro %}
{% macro lua_inputs(first, inputs) %}
{% for input in inputs %}
{{ lua_input(first, loop.index0, input) }}
{% endfor %}
{%- endmacro %}
static const LuaSingleField _lua_inputs[] = {
// main inputs
{% set main_inputs = adc_inputs.inputs | selectattr('type', '==', 'STICK') | list %}
{{ lua_inputs('MIXSRC_FIRST_STICK', main_inputs) }}
// flex inputs
{% set inputs = adc_inputs.inputs | selectattr('type', '==', 'FLEX') | list %}
{{ lua_inputs('MIXSRC_FIRST_POT', inputs) }}
// switches
{% set inputs = switches | selectattr('type','!=','FSWITCH') | list %}
{% set sw_count = inputs | count %}
{% for n in range(sw_count) %}
{% set sw = switches[loop.index0] %}
{ MIXSRC_FIRST_SWITCH + {{ loop.index0 }}, "{{ sw.name }}", "{{ sw.name }}" },
{% endfor %}
// trims
{% set trims_count = trims | count %}
{% for n in range(trims_count) %}
{% if n < (main_inputs | length) %}
{% set input = main_inputs[loop.index0] %}
{% set li = legacy_inputs[input.name] %}
{ MIXSRC_FIRST_TRIM + {{ loop.index0 }}, "trim-{{ li.lua }}", "{{ li.description}} trim" },
{% else %}
{ MIXSRC_FIRST_TRIM + {{ loop.index0 }}, "trim-t{{ loop.index }}", "Aux trim T{{ loop.index }}" },
{% endif %}
{% endfor %}
};
+136
View File
@@ -0,0 +1,136 @@
//
// WARNING: DO NOT EDIT THIS FILE
// This file has been generated from the target's JSON hardware description
//
{% set key_plus = keys | selectattr("key","==","KEY_PLUS") | first %}
{% set key_up = keys | selectattr("key","==","KEY_UP") | first %}
{% set key_shift = keys | selectattr("key","==","KEY_SHIFT") | first %}
{% set key_pageup = keys | selectattr("key","==","KEY_PAGEUP") | first %}
{% set key_pagedn = keys | selectattr("key","==","KEY_PAGEDN") | first %}
{% set key_model = keys | selectattr("key","==","KEY_MODEL") | first %}
{% set key_menu = keys | selectattr("key","==","KEY_MENU") | first %}
{% set key_tele = keys | selectattr("key","==","KEY_TELE") | first %}
{% set key_sys = keys | selectattr("key","==","KEY_SYS") | first %}
{% set key_hats_as_keys = not key_pagedn and not key_menu and not key_up %}
{% if key_plus %}
LROT_NUMENTRY( EVT_VIRTUAL_PREV, EVT_KEY_FIRST(KEY_PLUS) )
LROT_NUMENTRY( EVT_VIRTUAL_PREV_REPT, EVT_KEY_REPT(KEY_PLUS) )
LROT_NUMENTRY( EVT_VIRTUAL_NEXT, EVT_KEY_FIRST(KEY_MINUS) )
LROT_NUMENTRY( EVT_VIRTUAL_NEXT_REPT, EVT_KEY_REPT(KEY_MINUS) )
LROT_NUMENTRY( EVT_VIRTUAL_DEC, EVT_KEY_FIRST(KEY_MINUS) )
LROT_NUMENTRY( EVT_VIRTUAL_DEC_REPT, EVT_KEY_REPT(KEY_MINUS) )
LROT_NUMENTRY( EVT_VIRTUAL_INC, EVT_KEY_FIRST(KEY_PLUS) )
LROT_NUMENTRY( EVT_VIRTUAL_INC_REPT, EVT_KEY_REPT(KEY_PLUS) )
{% elif key_up %}
LROT_NUMENTRY( EVT_VIRTUAL_PREV, EVT_KEY_FIRST(KEY_UP) )
LROT_NUMENTRY( EVT_VIRTUAL_PREV_REPT, EVT_KEY_REPT(KEY_UP) )
LROT_NUMENTRY( EVT_VIRTUAL_NEXT, EVT_KEY_FIRST(KEY_DOWN) )
LROT_NUMENTRY( EVT_VIRTUAL_NEXT_REPT, EVT_KEY_REPT(KEY_DOWN) )
LROT_NUMENTRY( EVT_VIRTUAL_DEC, EVT_KEY_FIRST(KEY_DOWN) )
LROT_NUMENTRY( EVT_VIRTUAL_DEC_REPT, EVT_KEY_REPT(KEY_DOWN) )
LROT_NUMENTRY( EVT_VIRTUAL_INC, EVT_KEY_FIRST(KEY_UP) )
LROT_NUMENTRY( EVT_VIRTUAL_INC_REPT, EVT_KEY_REPT(KEY_UP) )
{% else %}
LROT_NUMENTRY( EVT_VIRTUAL_PREV, EVT_ROTARY_LEFT )
LROT_NUMENTRY( EVT_VIRTUAL_NEXT, EVT_ROTARY_RIGHT )
LROT_NUMENTRY( EVT_VIRTUAL_DEC, EVT_ROTARY_LEFT )
LROT_NUMENTRY( EVT_VIRTUAL_INC, EVT_ROTARY_RIGHT )
LROT_NUMENTRY( ROTENC_LOWSPEED, ROTENC_LOWSPEED )
LROT_NUMENTRY( ROTENC_MIDSPEED, ROTENC_MIDSPEED )
LROT_NUMENTRY( ROTENC_HIGHSPEED, ROTENC_HIGHSPEED )
{% endif %}
{% if key_shift %}
LROT_NUMENTRY( EVT_VIRTUAL_PREV_PAGE, EVT_KEY_LONG(KEY_LEFT) )
LROT_NUMENTRY( EVT_VIRTUAL_NEXT_PAGE, EVT_KEY_LONG(KEY_RIGHT) )
LROT_NUMENTRY( EVT_VIRTUAL_MENU, EVT_KEY_BREAK(KEY_SHIFT) )
LROT_NUMENTRY( EVT_VIRTUAL_MENU_LONG, EVT_KEY_LONG(KEY_SHIFT) )
{% elif key_up %}
LROT_NUMENTRY( EVT_VIRTUAL_PREV_PAGE, EVT_KEY_LONG(KEY_LEFT) )
LROT_NUMENTRY( EVT_VIRTUAL_NEXT_PAGE, EVT_KEY_BREAK(KEY_LEFT) )
LROT_NUMENTRY( EVT_VIRTUAL_MENU, EVT_KEY_BREAK(KEY_RIGHT) )
LROT_NUMENTRY( EVT_VIRTUAL_MENU_LONG, EVT_KEY_LONG(KEY_RIGHT) )
{% else %}
LROT_NUMENTRY( EVT_VIRTUAL_PREV_PAGE, EVT_KEY_BREAK(KEY_PAGEUP) )
LROT_NUMENTRY( EVT_VIRTUAL_NEXT_PAGE, EVT_KEY_BREAK(KEY_PAGEDN) )
{% if key_model or key_hats_as_keys %}
LROT_NUMENTRY( EVT_VIRTUAL_MENU, EVT_KEY_BREAK(KEY_MODEL) )
LROT_NUMENTRY( EVT_VIRTUAL_MENU_LONG, EVT_KEY_LONG(KEY_MODEL) )
{% else %}
LROT_NUMENTRY( EVT_VIRTUAL_MENU, EVT_KEY_BREAK(KEY_MENU) )
LROT_NUMENTRY( EVT_VIRTUAL_MENU_LONG, EVT_KEY_LONG(KEY_MENU) )
{% endif %}
{% endif %}
LROT_NUMENTRY( EVT_VIRTUAL_ENTER, EVT_KEY_BREAK(KEY_ENTER) )
LROT_NUMENTRY( EVT_VIRTUAL_ENTER_LONG, EVT_KEY_LONG(KEY_ENTER) )
LROT_NUMENTRY( EVT_VIRTUAL_EXIT, EVT_KEY_BREAK(KEY_EXIT) )
LROT_NUMENTRY( EVT_EXIT_BREAK, EVT_KEY_BREAK(KEY_EXIT) )
{% macro key_event(name, key) %}
LROT_NUMENTRY( EVT_{{ name }}_FIRST, EVT_KEY_FIRST({{ key }}) )
LROT_NUMENTRY( EVT_{{ name }}_BREAK, EVT_KEY_BREAK({{ key }}) )
LROT_NUMENTRY( EVT_{{ name }}_LONG, EVT_KEY_LONG({{ key }}) )
LROT_NUMENTRY( EVT_{{ name }}_REPT, EVT_KEY_REPT({{ key }}) )
{% endmacro %}
{{ key_event('ENTER','KEY_ENTER') }}
{% if key_menu %}
{{ key_event('MENU','KEY_MENU') }}
{% endif %}
{% if key_tele or key_hats_as_keys %}
{{ key_event('TELEM','KEY_TELE') }}
{% endif %}
{% if key_model or key_hats_as_keys %}
{{ key_event('MODEL','KEY_MODEL') }}
{% endif %}
{% if key_sys or key_hats_as_keys %}
{{ key_event('SYS','KEY_SYS') }}
{% endif %}
{% if key_up %}
{{ key_event('UP', 'KEY_UP') }}
{{ key_event('DOWN', 'KEY_DOWN') }}
{{ key_event('LEFT', 'KEY_LEFT') }}
{{ key_event('RIGHT', 'KEY_RIGHT') }}
{% endif %}
{% if key_pageup or key_hats_as_keys %}
{{ key_event('PAGEUP', 'KEY_PAGEUP') }}
{{ key_event('PAGEDN', 'KEY_PAGEDN') }}
{% elif key_pagedn %}
{% if key_menu %}
LROT_NUMENTRY( EVT_PAGE_FIRST, EVT_KEY_FIRST(KEY_PAGEDN) )
LROT_NUMENTRY( EVT_PAGE_BREAK, EVT_KEY_BREAK(KEY_PAGEDN) )
LROT_NUMENTRY( EVT_PAGE_LONG, EVT_KEY_BREAK(KEY_PAGEUP) )
LROT_NUMENTRY( EVT_PAGE_REPT, EVT_KEY_REPT(KEY_PAGEDN) )
{% else %}
LROT_NUMENTRY( EVT_PAGEDN_FIRST, EVT_KEY_FIRST(KEY_PAGEDN) )
LROT_NUMENTRY( EVT_PAGEDN_BREAK, EVT_KEY_BREAK(KEY_PAGEDN) )
LROT_NUMENTRY( EVT_PAGEDN_LONG, EVT_KEY_BREAK(KEY_PAGEUP) )
LROT_NUMENTRY( EVT_PAGEDN_REPT, EVT_KEY_REPT(KEY_PAGEDN) )
{% endif %}
{% endif %}
{% if key_plus %}
{{ key_event('PLUS', 'KEY_PLUS') }}
{{ key_event('MINUS', 'KEY_MINUS') }}
{% endif %}
{% if key_shift %}
{{ key_event('SHIFT', 'KEY_SHIFT') }}
{% endif %}
{% if (not key_plus) and (not key_up) %}
{{ key_event('ROT', 'KEY_ENTER') }}
LROT_NUMENTRY( EVT_ROT_LEFT, EVT_ROTARY_LEFT )
LROT_NUMENTRY( EVT_ROT_RIGHT, EVT_ROTARY_RIGHT )
{% endif %}
+15
View File
@@ -0,0 +1,15 @@
//
// WARNING: DO NOT EDIT THIS FILE
// This file has been generated from the target's JSON hardware description
//
{% set main_inputs = adc_inputs.inputs | selectattr('type', '==', 'STICK') | list %}
{% for input in main_inputs %}
{% set label = main_labels[input.name] %}
LROT_NUMENTRY( MIXSRC_{{ label.str }}, MIXSRC_FIRST_STICK + {{ loop.index0 }} )
{% endfor %}
{% set regular_switches = switches | selectattr('type','!=','FSWITCH') | list %}
{% for switch in regular_switches %}
LROT_NUMENTRY( MIXSRC_{{ switch.name }}, MIXSRC_FIRST_SWITCH + {{ loop.index0 }} )
{% endfor %}
+315
View File
@@ -0,0 +1,315 @@
from enum import Enum
from typing_extensions import List, Literal, Optional, Tuple, Union
from pydantic import BaseModel, model_validator
from pydantic_core import from_json, PydanticCustomError
class StrEnum(str, Enum):
@staticmethod
def _generate_next_value_(name, start, count, last_values):
return name
def __str__(self) -> str:
return self.value
ADCNameType = Literal["MAIN", "EXT", "SPI"]
class ADC(BaseModel):
name: ADCNameType
adc: str
sample_time: Optional[str] = None
dma: Optional[str] = None
dma_channel: Optional[str] = None
dma_stream: Optional[str] = None
dma_stream_irq: Optional[str] = None
dma_stream_irq_handler: Optional[str] = None
# SPI ADC fields
gpio_pin_sck: Optional[str] = None
gpio_pin_miso: Optional[str] = None
gpio_pin_mosi: Optional[str] = None
gpio_pin_cs: Optional[str] = None
StickEnum = StrEnum(
"StickEnum",
(
# 2 Gimbal radios
"LH",
"LV",
"RV",
"RH",
# Surface radios
"ST",
"TH",
),
)
MAX_POTS = 8
MAX_SLIDERS = 8
MAX_EXT = 16
MAX_RAW = 8
FlexInputEnum = StrEnum(
"FlexInputEnum",
(
[f"P{i}" for i in range(1, MAX_POTS)]
+ [f"SL{i}" for i in range(1, MAX_SLIDERS)]
+ [f"EXT{i}" for i in range(1, MAX_EXT)]
+ ["JSx", "JSy"]
),
)
SwitchInputEnum = StrEnum(
"SwitchInputEnum",
list([f"SW{chr(i)}" for i in range(ord("A"), ord("Z") + 1)]),
)
RawInputEnum = StrEnum("RawInputEnum", list([f"RAW{i}" for i in range(1, MAX_RAW)]))
InputNameType = Union[StickEnum, FlexInputEnum, SwitchInputEnum, RawInputEnum]
InputType = StrEnum(
"InputType",
(
"STICK",
"FLEX",
"SWITCH",
"VBAT",
"RTC_BAT",
"RAW",
"LUX",
),
)
FlexType = StrEnum(
"FlexType",
(
"NONE",
"POT",
"POT_CENTER",
"SLIDER",
"MULTIPOS",
"AXIS_X",
"AXIS_Y",
),
)
class StickInput(BaseModel):
name: StickEnum
type: Literal["STICK"]
adc: Optional[ADCNameType] = None
gpio: Optional[str] = None
pin: Optional[str] = None
channel: Optional[Union[str, int]] = None
inverted: Optional[bool] = False
pwm_channel: Optional[int] = None
class FlexInput(BaseModel):
name: FlexInputEnum
type: Literal["FLEX"]
adc: ADCNameType
gpio: Optional[str] = None
pin: Optional[str] = None
channel: Optional[Union[str, int]] = None
inverted: Optional[bool] = False
default: Optional[FlexType] = None
label: Optional[str] = None
short_label: Optional[str] = None
class SwitchInput(BaseModel):
name: SwitchInputEnum
type: Literal["SWITCH"]
adc: ADCNameType
gpio: Optional[str] = None
pin: Optional[str] = None
channel: Optional[Union[str, int]] = None
inverted: Optional[bool] = False
class RawInput(BaseModel):
name: RawInputEnum
type: Literal["RAW"]
adc: ADCNameType
gpio: Optional[str] = None
pin: Optional[str] = None
channel: Optional[Union[str, int]] = None
class VBatInput(BaseModel):
name: Literal["VBAT"]
type: Literal["VBAT"]
adc: ADCNameType
gpio: Optional[str] = None
pin: Optional[str] = None
channel: Optional[Union[str, int]] = None
class RTCBatInput(BaseModel):
name: Literal["RTC_BAT"]
type: Literal["RTC_BAT"]
adc: Literal["MAIN", "EXT"]
channel: str
class LuxInput(BaseModel):
name: Literal["LUX"]
type: Literal["LUX"]
adc: ADCNameType
gpio: Optional[str] = None
pin: Optional[str] = None
channel: Optional[Union[str, int]] = None
Input = Union[StickInput, FlexInput, SwitchInput, RawInput, VBatInput, RTCBatInput, LuxInput]
class ADCInputs(BaseModel):
adcs: List[ADC]
inputs: List[Input]
# MUST be the same order as 'EnumKeys'
KeyEnum = StrEnum(
"KeyEnum",
(
"KEY_MENU",
"KEY_EXIT",
"KEY_ENTER",
"KEY_PAGEUP",
"KEY_PAGEDN",
"KEY_UP",
"KEY_DOWN",
"KEY_LEFT",
"KEY_RIGHT",
"KEY_PLUS",
"KEY_MINUS",
"KEY_MODEL",
"KEY_TELE",
"KEY_SYS",
"KEY_SHIFT",
"KEY_BIND",
),
)
class Key(BaseModel):
name: str
label: str
key: KeyEnum
active_low: Optional[bool] = False
gpio: Optional[str] = None
pin: Optional[str] = None
@model_validator(mode="after")
def check_hardware(self: "Key") -> "Key":
if bool(self.gpio) != bool(self.pin):
raise PydanticCustomError(
"KeyHardwareError",
"Key missing either 'gpio' or 'pin'",
)
return self
SwitchHardwareTypeEnum = StrEnum(
"SwitchTypeEnum",
(
"2POS",
"3POS",
"ADC",
"FSWITCH",
),
)
SwitchTypeEnum = StrEnum(
"SwitchTypeEnum",
(
"2POS",
"3POS",
"TOGGLE",
"NONE",
),
)
SwitchDisplayType = Union[Tuple[int, int], Tuple]
class Switch(BaseModel):
name: str
type: SwitchHardwareTypeEnum
default: Optional[SwitchTypeEnum] = SwitchTypeEnum.NONE
flags: Optional[int] = 0
inverted: Optional[bool] = False
is_cfs: Optional[bool] = False
cfs_idx: Optional[int] = None
adc_input: Optional[str] = None
gpio: Optional[str] = None
pin: Optional[str] = None
gpio_high: Optional[str] = None
pin_high: Optional[str] = None
gpio_low: Optional[str] = None
pin_low: Optional[str] = None
display: Optional[SwitchDisplayType] = None
def _uses_single_gpio(self: "Switch") -> bool:
return bool(self.pin)
def _uses_two_gpios(self: "Switch") -> bool:
return bool(self.pin_high or self.pin_low)
def _uses_gpio(self: "Switch") -> bool:
return bool(self.gpio_high or self.gpio_low or self.gpio)
@model_validator(mode="after")
def check_hardware(self: "Switch") -> "Switch":
if bool(self.adc_input) == self._uses_gpio():
if self.adc_input:
raise PydanticCustomError(
"SwitchHardwareError",
"A switch is either using ADC or it's own GPIO pin(s)",
)
if self._uses_gpio() and (self._uses_single_gpio() == self._uses_two_gpios()):
raise PydanticCustomError(
"SwitchHardwareError",
"A switch based on GPIO pin(s) uses either a single or two GPIOs",
)
if self._uses_single_gpio():
if str(self.type) not in ["2POS", "FSWITCH"]:
raise PydanticCustomError(
"SwitchHardwareError",
"Single GPIO switch type is either '2POS' or 'FSWITCH'",
)
# TODO: check 'default' as well?
if self._uses_two_gpios():
if not self.pin_high or not self.pin_low:
raise PydanticCustomError(
"SwitchHardwareError",
"Switch missing 'pin_high' or 'pin_low'",
)
if str(self.type) != "3POS":
raise PydanticCustomError(
"SwitchHardwareError",
"Dual GPIO switch type is always '3POS",
)
# TODO: check 'default' as well?
return self
class HardwareDefinition(BaseModel):
adc_inputs: ADCInputs
switches: List[Switch]
keys: List[Key]
@staticmethod
def from_json(data: Union[str, bytes, bytearray]) -> "HardwareDefinition":
return HardwareDefinition.model_validate(from_json(data))
+11
View File
@@ -0,0 +1,11 @@
const hw_key_def _hw_key_defs[] = {
{% for key in keys %}
{ "{{ key.name }}", {{ key.key }} },
{% endfor %}
};
const char* const _hw_trim_defs[] = {
{% for trim in trims %}
"{{ trim.name }}",
{% endfor %}
};
+31
View File
@@ -0,0 +1,31 @@
//
// WARNING: DO NOT EDIT THIS FILE
// This file has been generated from the target's JSON hardware description
//
{% set regular_switches = switches | selectattr('is_cfs','==',False) | list %}
{% set display_switches = regular_switches | selectattr('display','!=',None) | list %}
const hw_switch_def _switch_defs[] = {
{% for switch in switches %}
{% if switch.is_cfs %}
{ "{{ switch.name }}", SWITCH_HW_{{ switch.type }}, SWITCH_{{ switch.default }}, true, {{ switch.cfs_idx }} },
{% else %}
{ "{{ switch.name }}", SWITCH_HW_{{ switch.type }}, SWITCH_{{ switch.default }} },
{% endif %}
{% endfor %}
};
constexpr uint8_t n_switches = {{ switches | count }};
{% if display_switches | count > 0 %}
const switch_display_pos_t _switch_display[] = {
{% for sw in switches %}
{% if sw.display %}
{ {{ sw.display[0] }}, {{ sw.display[1] }} },
{% else %}
{ 0, 0 },
{% endif %}
{% endfor %}
};
{% endif %}
+105
View File
@@ -0,0 +1,105 @@
//
// WARNING: DO NOT EDIT THIS FILE
// This file has been generated from the target's JSON hardware description
//
#include "stm32_gpio.h"
static const stm32_adc_input_t _ADC_inputs[] = {
{% for input in adc_inputs.inputs %}
{
// {{ input.name }}
// ADC_INPUT_{{ input.type }},
{{ input.gpio if input.gpio else 'nullptr' }},
{{ input.pin if input.gpio else '0' }},
{{ input.channel if input.channel else '0' }},
{{ '1 // inverted' if input.inverted else '0 // normal' }}
},
{% endfor %}
};
{% for adc in adc_inputs.adcs %}
{% set inputs = adc_inputs.inputs | selectattr('adc', '==', adc.name) | selectattr('channel','defined') %}
static const uint8_t _ADC_{{ adc.name }}_channels[] = {
{% for input in inputs %}
{{ adc_index[input.name] }}, // {{ input.name }}
{% endfor %}
};
{% endfor %}
{% set offset = namespace(value=0) %}
static const stm32_adc_t _ADC_adc[] = {
{% for adc in adc_inputs.adcs | selectattr('name', '!=', 'SPI') %}
{% set inputs = adc_inputs.inputs | selectattr('adc', '==', adc.name) | selectattr('channel','defined') %}
{% set input_count = inputs | list | count %}
{
{{ adc.adc }},
{{ adc.dma if adc.dma else 'nullptr' }},
{{ adc.dma_channel if adc.dma else '0' }},
{{ adc.dma_stream if adc.dma else '0' }},
{{ adc.dma_stream_irq if adc.dma else '(IRQn_Type)-1' }},
_ADC_{{ adc.name }}_channels,
{{ offset.value }},
{{ input_count }},
{{ adc.sample_time if adc.sample_time else '0' }}
},
{% set offset.value = offset.value + input_count %}
{% endfor %}
};
static const stm32_spi_adc_t _ADC_spi[] = {
{% for adc in adc_inputs.adcs | selectattr('name', '==', 'SPI') %}
{% set inputs = adc_inputs.inputs | selectattr('adc', '==', adc.name) | selectattr('channel','defined') %}
{% set input_count = inputs | list | count %}
{
.spi = {
{{ adc.adc }},
{{ adc.gpio_pin_sck }},
{{ adc.gpio_pin_miso }},
{{ adc.gpio_pin_mosi }},
{{ adc.gpio_pin_cs }},
},
_ADC_{{ adc.name }}_channels,
{{ input_count }}
},
{% endfor %}
};
{% for adc in adc_inputs.adcs | selectattr('name', '!=', 'SPI') %}
{% if adc.dma %}
extern "C" void {{ adc.dma_stream_irq_handler }} (void)
{
stm32_hal_adc_dma_isr(&_ADC_adc[{{ loop.index0 }}]);
}
{% endif %}
{% endfor %}
extern "C" void ADC_IRQHandler()
{
{% for adc in adc_inputs.adcs | selectattr('name', '!=', 'SPI') %}
stm32_hal_adc_isr(&_ADC_adc[{{ loop.index0 }}]);
{% endfor %}
}
#if defined(STM32H7)
extern "C" void ADC3_IRQHandler()
{
{% for adc in adc_inputs.adcs | selectattr('name', '!=', 'SPI') %}
stm32_hal_adc_isr(&_ADC_adc[{{ loop.index0 }}]);
{% endfor %}
}
#endif
{% for adc_gpio, adc_inputs in adc_gpios.items() | sort %}
static const uint32_t _ADC_{{ adc_gpio }}_pins[] = {
{% for input in adc_inputs %}
{{ input.pin }},
{% endfor %}
};
{% endfor %}
static const stm32_adc_gpio_t _ADC_GPIOs[] = {
{% for adc_gpio, adc_inputs in adc_gpios.items() | sort %}
{ {{ adc_gpio }}, _ADC_{{ adc_gpio }}_pins, {{ adc_inputs|count }} },
{% endfor %}
};
+63
View File
@@ -0,0 +1,63 @@
//
// WARNING: DO NOT EDIT THIS FILE
// This file has been generated from the target's JSON hardware description
//
{% set hw_keys = keys | rejectattr('pin', 'none') | rejectattr('gpio', 'none') | list %}
static inline void _init_keys()
{
{% for key_gpio, pins in key_gpios | dictsort %}
{% if key_gpio %}
stm32_gpio_enable_clock({{ key_gpio }});
{% endif %}
{% endfor %}
LL_GPIO_InitTypeDef pinInit;
LL_GPIO_StructInit(&pinInit);
pinInit.Mode = LL_GPIO_MODE_INPUT;
{% for key in hw_keys %}
pinInit.Pin = {{ key.pin }};
pinInit.Pull = {{ 'LL_GPIO_PULL_UP' if key.active_low else 'LL_GPIO_PULL_DOWN' }};
LL_GPIO_Init({{ key.gpio }}, &pinInit);
{% endfor %}
}
static inline uint32_t _read_keys()
{
uint32_t keys = 0;
{% for key in hw_keys %}
if ({{'!' if key.active_low }}LL_GPIO_IsInputPinSet({{ key.gpio }}, {{ key.pin }}))
keys |= (1 << {{ key.key }});
{% endfor %}
return keys;
}
{% set hw_trims = trims | selectattr('dec', 'defined') | selectattr('inc', 'defined') | list %}
static inline void _init_trims()
{
{% for trim_gpio, pins in trim_gpios | dictsort %}
stm32_gpio_enable_clock({{ trim_gpio }});
{% endfor %}
LL_GPIO_InitTypeDef pinInit;
LL_GPIO_StructInit(&pinInit);
pinInit.Mode = LL_GPIO_MODE_INPUT;
{% for trim in hw_trims %}
pinInit.Pin = {{ trim.dec.pin }};
pinInit.Pull = {{ 'LL_GPIO_PULL_UP' if trim.dec.active_low else 'LL_GPIO_PULL_DOWN' }};
LL_GPIO_Init({{ trim.dec.gpio }}, &pinInit);
pinInit.Pin = {{ trim.inc.pin }};
pinInit.Pull = {{ 'LL_GPIO_PULL_UP' if trim.inc.active_low else 'LL_GPIO_PULL_DOWN' }};
LL_GPIO_Init({{ trim.inc.gpio }}, &pinInit);
{% endfor %}
}
static inline uint32_t _read_trims()
{
uint32_t trims = 0;
{% for trim in hw_trims %}
if ({{'!' if trim.dec.active_low }}LL_GPIO_IsInputPinSet({{ trim.dec.gpio }}, {{ trim.dec.pin }}))
trims |= (1 << {{ loop.index0 * 2 }});
if ({{'!' if trim.inc.active_low }}LL_GPIO_IsInputPinSet({{ trim.inc.gpio }}, {{ trim.inc.pin }}))
trims |= (1 << {{ loop.index0 * 2 + 1 }});
{% endfor %}
return trims;
}
+15
View File
@@ -0,0 +1,15 @@
//
// WARNING: DO NOT EDIT THIS FILE
// This file has been generated from the target's JSON hardware description
//
{% set pwm_inputs = adc_inputs.inputs | selectattr('type', '==', 'STICK') | selectattr('pwm_channel', 'ne', none) | list %}
static const stick_pwm_input_t _PWM_inputs[] = {
{% for input in pwm_inputs %}
{
// ADC_INPUT_{{ input.type }}, {{ input.name }}
{{ input.pwm_channel }}, // channel
{{ '1 // inverted' if input.inverted else '0 // normal' }}
},
{% endfor %}
};
+68
View File
@@ -0,0 +1,68 @@
//
// WARNING: DO NOT EDIT THIS FILE
// This file has been generated from the target's JSON hardware description
//
{% set regular_switches = switches | selectattr('is_cfs','==',False) | list %}
{% set display_switches = regular_switches | selectattr('display','!=',None) | list %}
static const stm32_switch_t _switch_defs[] = {
{% for switch in switches %}
{% if switch.type == '2POS' %}
{
"{{ switch.name }}",
{{ switch.gpio or "nullptr" }}, {{ switch.pin or 0 }},
nullptr, 0,
SWITCH_HW_2POS, {{ 'SWITCH_HW_INVERTED' if switch.inverted else 0 }}, SWITCH_{{ switch.default }},
{% if switch.is_cfs %}
true, {{ switch.cfs_idx }}
{% endif %}
},
{% elif switch.type == '3POS' %}
{
"{{ switch.name }}",
{{ switch.gpio_high or "nullptr" }}, {{ switch.pin_high or 0 }},
{{ switch.gpio_low or "nullptr" }}, {{ switch.pin_low or 0 }},
SWITCH_HW_3POS, {{ 'SWITCH_HW_INVERTED' if switch.inverted else 0 }}, SWITCH_{{ switch.default }},
},
{% elif switch.type == 'ADC' %}
{
"{{ switch.name }}",
nullptr, {{ adc_index[switch.adc_input] }},
nullptr, 0,
SWITCH_HW_ADC, {{ 'SWITCH_HW_INVERTED' if switch.inverted else 0 }}, SWITCH_{{ switch.default }},
},
{% endif %}
{% endfor %}
};
static inline void _init_switches()
{
{% if switch_gpios|count > 0 %}
LL_GPIO_InitTypeDef pinInit;
LL_GPIO_StructInit(&pinInit);
pinInit.Mode = LL_GPIO_MODE_INPUT;
pinInit.Pull = LL_GPIO_PULL_UP;
{% for sw_gpio, pins in switch_gpios.items() | sort %}
{% if sw_gpio %}
stm32_gpio_enable_clock({{ sw_gpio }});
pinInit.Pin = {{ pins | join('|') }};
LL_GPIO_Init({{ sw_gpio }}, &pinInit);
{% endif %}
{% endfor %}
{% endif %}
}
constexpr uint8_t n_switches = {{ switches | count }};
{% if display_switches | count > 0 %}
const switch_display_pos_t _switch_display[] = {
{% for sw in switches %}
{% if sw.display %}
{ {{ sw.display[0] }}, {{ sw.display[1] }} },
{% else %}
{ 0, 0 },
{% endif %}
{% endfor %}
};
{% endif %}
+67
View File
@@ -0,0 +1,67 @@
"""Test that all templates render successfully for all board hw_defs.
Runs all .jinja templates against all .json board definitions in a single
process, accumulating failures rather than stopping at the first one.
Usage:
python3 test_templates.py <hw_defs_dir>
"""
import argparse
import contextlib
import io
import os
import sys
from pathlib import Path
from generator import generate_from_template
def main():
parser = argparse.ArgumentParser(description="Test all templates x all boards")
parser.add_argument("hw_defs_dir", help="Directory containing board .json files")
args = parser.parse_args()
hw_defs_dir = Path(args.hw_defs_dir)
if not hw_defs_dir.is_dir():
print(f"ERROR: {hw_defs_dir} is not a directory", file=sys.stderr)
sys.exit(1)
script_dir = Path(os.path.dirname(os.path.abspath(__file__)))
templates = sorted(script_dir.glob("*.jinja"))
json_files = sorted(hw_defs_dir.glob("*.json"))
if not templates:
print("ERROR: no .jinja templates found", file=sys.stderr)
sys.exit(1)
if not json_files:
print(f"ERROR: no .json files found in {hw_defs_dir}", file=sys.stderr)
sys.exit(1)
print(f"Testing {len(json_files)} boards x {len(templates)} templates "
f"= {len(json_files) * len(templates)} combinations")
failures = []
for json_file in json_files:
board = json_file.stem
for tmpl in templates:
try:
with contextlib.redirect_stdout(io.StringIO()):
generate_from_template(str(json_file), str(tmpl), board)
except SystemExit as e:
if e.code != 0:
failures.append((board, tmpl.name))
if failures:
print(f"\nFAILED ({len(failures)}):", file=sys.stderr)
for board, tmpl in failures:
print(f" board={board} template={tmpl}", file=sys.stderr)
sys.exit(1)
print(f"All combinations passed.")
if __name__ == "__main__":
main()
+36
View File
@@ -0,0 +1,36 @@
//
// WARNING: DO NOT EDIT THIS FILE
// This file has been generated from the target's JSON hardware description
//
{% set main_inputs = adc_inputs.inputs | selectattr('type', '==', 'STICK') | list %}
{% set flex_inputs = adc_inputs.inputs | selectattr('type', '==', 'FLEX') | list %}
struct legacy_input_t {
const char* legacy;
uint16_t src_raw;
};
static const legacy_input_t _legacy_inputs[] = {
// main inputs
{% for input in main_inputs %}
{% set li = legacy_inputs[input.name] %}
{ "{{ li.yaml }}", MIXSRC_FIRST_STICK + {{ loop.index0 }} },
{% endfor %}
// flex inputs
{% for input in flex_inputs %}
{% set li = legacy_inputs[input.name] %}
{ "{{ li.yaml }}", MIXSRC_FIRST_POT + {{ loop.index0 }} },
{% endfor %}
// trims
{% set trims_count = trims | count %}
{% for n in range(trims_count) %}
{% if n < (main_inputs | length) %}
{% set input = main_inputs[loop.index0] %}
{% set li = legacy_inputs[input.name] %}
{ "Trim{{ li.yaml }}", MIXSRC_FIRST_TRIM + {{ loop.index0 }} },
{% else %}
{ "TrimT{{ loop.index }}", MIXSRC_FIRST_TRIM + {{ loop.index0 }} },
{% endif %}
{% endfor %}
};
+98
View File
@@ -0,0 +1,98 @@
local toolName = "TNS|LUA event tests|TNE"
---- #########################################################################
---- # #
---- # Copyright (C) EdgeTX
---- #
---- # Based on code named
---- # opentx - https://github.com/opentx/opentx
---- # th9x - http://code.google.com/p/th9x
---- # er9x - http://code.google.com/p/er9x
---- # gruvin9x - http://code.google.com/p/gruvin9x
---- #
---- # License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
---- #
---- # This program is free software; you can redistribute it and/or modify
---- # it under the terms of the GNU General Public License version 2 as
---- # published by the Free Software Foundation.
---- #
---- # 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.
---- # #
---- #########################################################################
local virtual_next = 0
local virtual_prev = 0
local virtual_inc = 0
local virtual_dec = 0
local virtual_next_rept = 0
local virtual_prev_rept = 0
local virtual_page_next = 0
local virtual_page_prev = 0
local virtual_enter = 0
local virtual_enter_long = 0
local virtual_menu = 0
local virtual_menu_long = 0
-- Init
local function init()
end
-- Main
local function run(event)
if event == nil then
error("Cannot be run as a model script!")
return 2
end
if event == EVT_VIRTUAL_EXIT then
return 2
end
if event == EVT_VIRTUAL_NEXT then
virtual_next = virtual_next + 1
end
if event == EVT_VIRTUAL_PREV then
virtual_prev = virtual_prev + 1
end
if event == EVT_VIRTUAL_INC then
virtual_inc = virtual_inc + 1
end
if event == EVT_VIRTUAL_DEC then
virtual_dec = virtual_dec + 1
end
if event == EVT_VIRTUAL_NEXT_REPT then
virtual_next_rept = virtual_next_rept + 1
end
if event == EVT_VIRTUAL_PREV_REPT then
virtual_prev_rept = virtual_prev_rept + 1
end
if event == EVT_VIRTUAL_NEXT_PAGE then
virtual_page_next = virtual_page_next +1
end
if event == EVT_VIRTUAL_PREV_PAGE then
virtual_page_prev = virtual_page_prev + 1
killEvents(event);
end
if event == EVT_VIRTUAL_ENTER then
virtual_enter = virtual_enter + 1
end
if event == EVT_VIRTUAL_MENU then
virtual_menu = virtual_menu + 1
end
lcd.clear()
lcd.drawScreenTitle("LUA EVENT TEST", 0, 0)
lcd.drawText(1, 10, "PREV:"..virtual_prev)
lcd.drawText(LCD_W/2, 10, "NEXT: "..virtual_next)
lcd.drawText(1, 20, "DEC:"..virtual_dec)
lcd.drawText(LCD_W/2, 20, "INC:"..virtual_inc)
lcd.drawText(1, 30, "NXT PAGE: "..virtual_page_next)
lcd.drawText(LCD_W/2, 30, "PRV PAGE: "..virtual_page_prev)
lcd.drawText(1, 50, "ENTER: "..virtual_enter)
lcd.drawText(LCD_W/2, 50, "MENU: "..virtual_menu)
return 0
end
return { init=init, run=run }
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
This script parses debug log events related to Lua memory (de)allocations ( lines
starting wiht "LT: ") and produces a data for gnuplot program
Usage:
./simu 2>&1 | grep "^LT" | ../radio/util/lua_trace2plot.py > data.plot
gnuplot -e 'set xtics rotate; plot "data.plot" using 2:xtic(1) ; pause mouse close'
"""
from __future__ import print_function
import sys
if len(sys.argv) > 1:
inputFile = sys.argv[1]
inp = open(inputFile, "r")
else:
inp = sys.stdin
x = 0
memUsed = 0
while True:
skip = True
line = inp.readline()
if len(line) == 0:
break
line = line.strip('\r\n')
if len(line) == 0:
skip = True
if line.startswith("LT:"):
skip = False
if not skip:
parts = line.split()
if len(parts) >= 3:
data = parts[1].strip("[").strip("]").split(",")
alloc = int(data[0])
free = int(data[1])
line = parts[2]
if alloc > 0:
memUsed += alloc
print("'%s'\t%d" % (line, memUsed))
x += 1
if free < 0:
memUsed += free
print("'%s'\t%d" % (line, memUsed))
x += 1
inp.close()
+16
View File
@@ -0,0 +1,16 @@
echo off
echo Getting max amplitude..
sox %1.wav -n stat -v 2> %1_apl.txt
Set /P apl_value= < %1_apl.txt
echo Normalizing audio.. gain: %apl_value%
sox --show-progress -v %apl_value% %1.wav %1_norm.wav
copy %1_norm.wav %1.wav
echo Cleaning..
del %1_norm.wav
del %1_apl.txt
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env python
from __future__ import print_function
import sys
filename = sys.argv[1]
print(filename)
fr = open(filename)
fw = open(filename + ".new", "w")
ew = open(filename + ".en", "w")
hw = open(filename + ".stringsh", "w")
cw = open(filename + ".stringsc", "w")
replacements = {}
for line in fr.readlines():
pos_pstr = line.find("\""
MENU = False
if pos_pstr < 0:
pos_pstr = line.find("MENU(\"")
MENU = True
while pos_pstr >= 0:
# print line,
if MENU:
pos_endparenthesis = line.find('"', pos_pstr + 7)
else:
pos_endparenthesis = line.find("\")", pos_pstr)
pstr = line[pos_pstr + 6:pos_endparenthesis]
str_rep = "STR_" + pstr.upper()
for s in (" ", ".", "[", "]", "-", "!", "/", ")", "(", "%", "+", ":"):
str_rep = str_rep.replace(s, "")
if "14" in pstr or "32" in pstr or "@" in pstr or len(str_rep) <= 5:
pos_pstr = -1
else:
if MENU:
glob_str = line[pos_pstr + 5:pos_endparenthesis + 1]
str_rep = "STR_MENU" + pstr.upper()
else:
glob_str = line[pos_pstr:pos_endparenthesis + 2]
str_rep = "STR_" + pstr.upper()
for s in (" ", ".", "[", "]", "-", "!", "/", ")", "(", "%", "+", ":"):
str_rep = str_rep.replace(s, "")
line = line.replace(glob_str, str_rep)
if str_rep in replacements.keys():
if replacements[str_rep] != pstr:
print("!!!!! NON !!!!!")
else:
replacements[str_rep] = pstr
print(glob_str, "=>", pstr, str_rep)
ew.write("#define " + str_rep[1:] + " " * (17 - len(str_rep)) + '"%s"\n' % pstr)
hw.write("extern const char %s[];\n" % str_rep)
cw.write("const prog_char APM %s[] = %s;\n" % (str_rep, str_rep[1:]))
pos_pstr = line.find("\""
fw.write(line)
fw.close()
ew.close()
hw.close()
cw.close()
+30
View File
@@ -0,0 +1,30 @@
#!/bin/env python
from __future__ import print_function
import math
samples = 1024
amplitudes = (32000, 0, 4000, 0, 4000)
max = 0
min = 0
for i in range(samples):
sample = 0.0
for harmonic, amplitude in enumerate(amplitudes):
sample += math.sin(math.pi * 2 * i * (harmonic + 1) / samples) * amplitude / 2
sample = int(sample)
if sample > max:
max = sample
elif sample < min:
min = sample
print("%d," % sample, end=' ')
if i % 10 == 9:
print()
print()
print('Range is:', min, max)
if max > 32767 or min < -32768:
print("Invalid range!")
+194
View File
@@ -0,0 +1,194 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This program parses sport.log files
from __future__ import division, print_function
import sys
import struct
START_STOP = '\x7e'
BYTE_STUFF = '\x7d'
STUFF_MASK = '\x20'
DATA_FRAME = 0x10
lineNumber = 0
sportDataBuff = ""
quiet = False
def ParseFlVSS(packet, dataId, prim, appId, data, crc):
# if dataId != 0xa1:
# return
print("packet: %s (%4d)" % (dump(packet), lineNumber), end=' ')
cells = (data & 0xF0) >> 4
battnumber = data & 0xF
voltage1 = ((data & 0x000FFF00) >> 8) // 5
voltage2 = ((data & 0xFFF00000) >> 20) // 5
print(" FLVSS: no cells: %d, cell: %d: voltages: %0.2f %0.2f" % (cells, battnumber, voltage1 / 100., voltage2 / 100.))
def ParseASS(packet, dataId, prim, appId, data, crc):
print("packet: %s (%4d)" % (dump(packet), lineNumber), end=' ')
print(" ASS: %dkm/h" % (float(data) / 10))
def ParseRSSI(packet, dataId, prim, appId, data, crc):
print("packet: %s (%4d)" % (dump(packet), lineNumber), end=' ')
print(" RSSI: %d" % (data & 0xFF))
def ParseAdc(packet, dataId, prim, appId, data, crc):
print("packet: %s (%4d)" % (dump(packet), lineNumber), end=' ')
print(" A%d: %d" % (dataId - 0xf102, data & 0xFF))
def ParseBatt(packet, dataId, prim, appId, data, crc):
print("packet: %s (%4d)" % (dump(packet), lineNumber), end=' ')
print(" Batt: %d" % (data & 0xFF))
def ParseRAS(packet, dataId, prim, appId, data, crc):
print("packet: %s (%4d)" % (dump(packet), lineNumber), end=' ')
print(" RAS: %d" % (data & 0xFF))
def ParseVersion(packet, dataId, prim, appId, data, crc):
print("packet: %s (%4d)" % (dump(packet), lineNumber), end=' ')
print(" VERSION: %d" % data)
def ParseAirSpeed(packet, dataId, prim, appId, data, crc):
print("packet: %s (%4d)" % (dump(packet), lineNumber), end=' ')
print(" Aspd: %.1f km/h" % (data / 10.0))
appIdParsers = (
(0x0300, 0x030f, ParseFlVSS),
(0x0a00, 0x0a0f, ParseASS),
(0xf101, 0xf101, ParseRSSI),
(0xf102, 0xf103, ParseAdc),
(0xf104, 0xf104, ParseBatt),
(0xf105, 0xf105, ParseRAS),
(0xf106, 0xf106, ParseVersion),
)
def dump(data, maxLen=None):
if maxLen and len(data) > maxLen:
data = data[:maxLen]
return ":".join("{:02x}".format(ord(c)) for c in data)
def CheckSportCrc(packet):
return True
def ParseSportPacket(packet):
global lineNumber
(dataId, prim, appId, data, crc) = struct.unpack('<BBHIB', packet)
# print "dataId:%02x, prim:%02x, appId:%04x, data:%08x, crc:%02x)\n" % (dataId, prim, appId, data, crc)
if prim != DATA_FRAME:
print("unknown prim: %02x for packet %s in line %s" % (prim, dump(packet), lineNumber))
# process according to appId
for firstId, lastId, parser in appIdParsers:
if firstId <= appId <= lastId:
parser(packet, dataId, prim, appId, data, crc)
return
# no parser found
if not quiet:
print("\tdataId:%02x, prim:%02x, appId:%04x, data:%08x, crc:%02x)" % (dataId, prim, appId, data, crc))
print("\tparser for appId %02x not implemented" % appId)
def ParsePacket(packet):
global lineNumber
# unstuff packet
while True:
pos = packet.find(BYTE_STUFF)
if pos == -1:
break
# print "found stuff at %d in %s" % (pos, dump(packet, 20))
# if we have nex char, then unstuff it
if len(packet) > pos + 1:
unstuffed = ord(packet[pos]) ^ ord(STUFF_MASK)
# print "unstuffed data: %02x" % unstuffed
# buffer[pos] = chr(unstuffed)
packet = packet[:pos] + chr(unstuffed) + packet[pos + 2:]
# print "buff after unstuff %s" % dump(packet, 20)
else:
# missing data, wait for more data
print("unstuff missing data")
return
# print "packet: %s" % dump(packet, 10)
if len(packet) == 9:
# valid sport packet length
# print "\npacket: %s @%d" % (dump(packet), lineNumber)
# check crc
if not CheckSportCrc(packet):
print("error: wrong CRC for packet %s at line %d" % (dump(packet), lineNumber))
ParseSportPacket(packet)
else:
if len(packet) > 1:
print("warning: wrong length %s for packet %s at line %d" % (len(packet), dump(packet, 10), lineNumber))
def ParseSportData(data):
global sportDataBuff
# convert from hex
parts = data.split(' ')
binData = [chr(int(hex, 16)) for hex in parts]
# print binData
data = ''.join(binData)
sportDataBuff += data
# process whole packets
while True:
# find whole frame
# remove all data before START_STOP
# find next START_STOP
# print "start: %s" % dump(sportDataBuff, 10)
posStop = sportDataBuff.find(START_STOP)
# process pacet between two START_STOPs
if posStop > -1:
# print "found stop at %d" % posStop
ParsePacket(sportDataBuff[:posStop])
# remove processed data
sportDataBuff = sportDataBuff[posStop + 1:]
# print "after: %s" % dump(sportDataBuff, 10)
else:
break
inputFile = None
if len(sys.argv) > 1:
inputFile = sys.argv[1]
# open input
if inputFile:
inp = open(inputFile, 'r')
else:
inp = sys.stdin
while True:
line = inp.readline()
lineNumber += 1
if len(line) == 0:
break
line = line.strip('\r\n')
if len(line) == 0:
continue
# print line
parts = line.split(': ')
if len(parts) < 2:
print("weird data: \"%s\" at line %d" % (line, lineNumber))
continue
sportData = parts[1].strip()
# print "sd: %s" % sportData
ParseSportData(sportData)
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
try:
from PyQt5 import Qt, QtGui
except:
from PyQt4 import Qt, QtGui
menu = ["radio_setup", "model_select", "model_settings", "model_checklist", "files", "version"]
image = QtGui.QImage(24 * 8, 24, QtGui.QImage.Format_RGB32)
image.fill(QtGui.QColor(255, 255, 255))
painter = QtGui.QPainter()
painter.begin(image)
for i, str in enumerate(menu):
icon = QtGui.QImage("../src/bitmaps/taranis/std/%s.png" % str)
painter.drawImage(Qt.QPoint(24 * i, 0), icon)
painter.end()
image.save("../src/bitmaps/taranis/mainmenu.png")
+6
View File
@@ -0,0 +1,6 @@
def filename(idx):
ext = ".wav"
if isinstance(idx, int):
return "%04d%s" % (idx, ext)
else:
return idx + ext
+105
View File
@@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
# Czech language sounds configuration
from tts_common import filename
systemSounds = []
sounds = []
for i, s in enumerate(["nula", "jedna", "dva"]):
systemSounds.append((s, filename(i)))
for i in range(97):
systemSounds.append((str(3 + i), filename(3 + i)))
for i, s in enumerate(["sto", "dvěsta", "třista", "čtyřista", "pětset", "šestset", "sedmset", "osmset", "devětset", "tisíc"]):
systemSounds.append((s, filename(100 + i)))
for i, s in enumerate(["tisíce", "jeden", "jedno", "dvě", "celá", "celé", "celých", "mínus"]):
systemSounds.append((s, filename(110 + i)))
for i, (s, f) in enumerate([("volt", "volt0"), ("volty", "volt1"), ("voltů", "volt2"),("voltu", "volt3"),
("ampér", "amp0"), ("ampéry", "amp1"), ("ampérů", "amp2"), ("ampéru", "amp3"),
("miliampér", "mamp0"), ("miliampéry", "mamp1"), ("miliampérů", "mamp2"), ("miliampéru", "mamp3"),
("uzel", "knot0"), ("uzly", "knot1"), ("uzlů", "knot2"), ("uzlu", "knot3"),
("metr za sekundu", "mps0"), ("metry za sekundu", "mps1"), ("metrů za sekundu", "mps2"), ("metru za sekundu", "mps3"),
("stopa za sekundu", "fps0"), ("stopy za sekundu", "fps1"), ("stop za sekundu", "fps2"), ("stopy za sekundu", "fps3"),
("kilometr za hodinu", "kph0"), ("kilometry za hodinu", "kph1"), ("kilometrů za hodinu", "kph2"), ("kilometru za hodinu", "kph3"),
("míle za hodinu", "mph0"), ("míle za hodinu", "mph1"), ("mil za hodinu", "mph2"), ("míle za hodinu", "mph3"),
("metr", "meter0"), ("metry", "meter1"), ("metrů", "meter2"), ("metru", "meter3"),
("stopa", "foot0"), ("stopy", "foot1"), ("stop", "foot2"),("stopy", "foot3"),
("stupeň celsia", "celsius0"), ("stupně celsia", "celsius1"), ("stupňů celsia", "celsius2"), ("stupně celsia", "celsius3"),
("stupeň fahrenheita", "fahr0"), ("stupně fahrenheita", "fahr1"), ("stupňů fahrenheita", "fahr2"), ("stupně fahrenheita", "fahr3"),
("procento", "percent0"), ("procenta", "percent1"), ("procent", "percent2"), ("procenta", "percent3"),
("miliampérhodina", "mamph0"), ("miliampérhodiny", "mamph1"), ("miliampérhodin", "mamph2"), ("miliampérhodiny", "mamph3"),
("vat", "watt0"), ("vaty", "watt1"), ("vatů", "watt2"), ("vatu", "watt3"),
("milivat", "mwatt0"), ("milivaty", "mwatt1"), ("milivatů", "mwatt2"), ("milivatu", "mwatt3"),
("decibel", "db0"), ("decibely", "db1"), ("decibelů", "db2"), ("decibelu", "db3"),
("otáčka za minutu", "rpm0"), ("otáčky za minutu", "rpm1"), ("otáček za minutu", "rpm2"), ("otáčky za minutu", "rpm3"),
("", "g0"), ("", "g1"), ("", "g2"), ("", "g3"),
("stupeň", "degree0"), ("stupně", "degree1"), ("stupňů", "degree2"), ("stupně", "degree3"),
("radián", "rad0"), ("radiány", "rad1"), ("radiánů", "rad2"), ("radiánu", "rad3"),
("mililitr", "ml0"), ("mililitry", "ml1"), ("mililitrů", "ml2"), ("mililitru", "ml3"),
("dutá unce", "founce0"), ("duté unce", "founce1"), ("dutých uncí", "founce2"), ("duté unce", "founce3"),
("milliliter per minute", "mlpm0"), ("milliliters per minute", "mlpm1"),
("hodina", "hour0"), ("hodiny", "hour1"), ("hodin", "hour2"), ("hodiny", "hour3"),
("minuta", "minute0"), ("minuty", "minute1"), ("minut", "minute2"), ("minuty", "minute3"),
("sekunda", "second0"), ("sekundy", "second1"), ("sekund", "second2"), ("sekundy", "second3"),
]):
systemSounds.append((s, filename(f)))
for s, f in [("střed trimu", "midtrim"),
("maximum trimu", "maxtrim"),
("minimum trimu", "mintrim"),
("stopky 1 doběhly", "timovr1"),
("stopky 2 doběhly", "timovr2"),
("stopky 3 doběhly", "timovr3"),
("baterie rádia je vybitá", "lowbatt"),
("zapoměl jsi na mě!", "inactiv"),
("plyn není na nule", "thralert"),
("přepínače nejsou ve výchozí poloze", "swalert"),
("neplatná eeprom", "eebad"),
("Žádám o povolení ke startu.", "hello"),
("Tak zase příště!", "bye"),
("nízký RF signál", "rssi_org"),
("kritický RF signál", "rssi_red"),
("problém s anténou rádia", "swr_red"),
("telemetrie ztracena", "telemko"),
("telemetrie je funkční", "telemok"),
("trenér port odpojen", "trainko"),
("trenér port připojen", "trainok"),
("senzor ztracen", "sensorko"),
("servo přetíženo", "servoko"),
("r f přebuzeno", "rxko"),
("receiver still connected", "modelpwr"),
]:
systemSounds.append((s, filename(f)))
for i, (s, f) in enumerate([("podvozek je zasunut", "podvo0"),
("podvozek je vysunut", "podvo1"),
("klapky jsou zapnuty", "klapk1"),
("klapky jsou vypnuty", "klapk0"),
("trenér je zapnut", "trene1"),
("trenér je vypnutý", "trene0"),
("motor je vypnut", "motor0"),
("motor je aktivován", "motor1"),
("jsi příliš vysoko", "vysoko"),
("jsi příliš nízko", "nizko"),
("baterie modelu je vybitá", "bat0"),
("dochází palivo", "palivo"),
("nízký signál!", "nsigna"),
("kritický signál!", "ksigna"),
("rychlý režim aktivován", "rrychl"),
("termický režim aktivován", "rtermi"),
("normální režim aktivován", "rnorm"),
("režim start aktivován", "rstart"),
("režim přistání aktivován", "prista"),
("vario je zapnuto", "vario0"),
("vario je vypnuto", "vario1"),
("start povolen", "start1"),
("letový režim jedna", "fm-1"),
("letový režim dva", "fm-2"),
("letový režim tři", "fm-3"),
("letový režim čtyři", "fm-4"),
("letový režim pět", "fm-5"),
("letový režim šest", "fm-6"),
("letový režim sedm", "fm-7"),
("letový režim osm", "fm-8"),
]):
sounds.append((s, filename(f)))
+115
View File
@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
# German language sounds configuration
from tts_common import filename
systemSounds = []
sounds = []
for i in range(100):
systemSounds.append((str(i), filename(i)))
systemSounds.append(("ein", filename(100)))
systemSounds.append(("eine", filename(101)))
systemSounds.append(("hundert", filename(102)))
systemSounds.append(("tausend", filename(103)))
for i, s in enumerate(["komma", "und", "minus", "uhr", "minute", "minuten", "sekunde", "sekunden", "stunde", "stunden"]):
systemSounds.append((s, filename(104 + i)))
for i, (s, f) in enumerate([("Wolt", "volt0"),
("Ampär", "amp0"),
("MilliAmpär", "mamp0"),
("Knoten", "knot0"),
("Meter pro sekunde", "mps0"),
("Fuß pro sekunde", "fps0"),
("Kilometer pro stunde", "kph0"),
("Meilen pro Stunde", "mph0"),
("Meter", "meter0"),
("Fuß", "foot0"),
("Grad Celsius", "celsius0"),
("Grad Fahrenheit", "fahr0"),
("Prozent", "percent0"),
("Milliampärstunde", "mamph0"), ("Milliampärstunden", "mamph1"),
("Watt", "watt0"),
("Milli Watt", "mwatt0"),
("D B", "db0"),
("Umdrehungen pro Minute", "rpm0"),
("g", "g0"),
("Grad", "degree0"),
("radiant", "rad0"), ("radianten", "rad1"),
("Milliliter", "ml0"),
("Unze", "founce0"),("Unzen", "founce1"),
("Milliliter pro minute", "mlpm0"),
("Hertz", "hertz0"),
("Millisekunde", "ms0"), ("Millisekunden", "ms1"),
("Mikrosekunde", "us0"), ("Mikrosekunden", "us1"),
("Stunde", "hour0"), ("Stunden", "hour1"),
("Minute", "minute0"), ("Minuten", "minute1"),
("Sekunde", "second0"), ("Sekunden", "second1"),
]):
systemSounds.append((s, filename(f)))
for s, f in [("Trimmung zentriert", "midtrim"),
("Obere Trimmungsgrenze erreicht", "maxtrim"),
("Untere Trimmungsgrenze erreicht", "mintrim"),
("timer eins abgelaufen", "timovr1"),
("timer zwei abgelaufen", "timovr2"),
("timer drei abgelaufen", "timovr3"),
("Senderspannung schwach", "lowbatt"),
("Inaktivitätsalarm", "inactiv"),
("Gaskanal nicht Null, bitte prüfen", "thralert"),
("Schalter fehlpositioniert, bitte prüfen", "swalert"),
("EEPROM fehlerhaft", "eebad"),
("Willkommen bei open t x", "hello"),
("Funksignal schwach!", "rssi_org"),
("Funksignal kritisch!", "rssi_red"),
("Senderantenne fehlerhaft", "swr_red"),
("Telemetrie verloren", "telemko"),
("Telemetrie wiederhergestellt", "telemok"),
("Schülersignal verloren", "trainko"),
("Schülersignal wiederhergestellt", "trainok"),
("Sensor verloren", "sensorko"),
("servo Überlast", "servoko"),
("power Überlast", "rxko"),
("Empfänger noch verbunden", "modelpwr"),
("Fehler", "error"), # Not shure if this is needed
("Senderstrom zu hoch!", "highmah"), # Not shure if this is needed. Only used in PCBX9E
("Sendertemperatur zu hoch!", "hightemp"), # Not shure if this is needed. Only used in PCBX9E
]:
systemSounds.append((s, filename(f)))
for i, s in enumerate(["Uhr", "Uhr", "Sender", "Empfang", "A1", "A2", "Hoehe", "Motor",
"Treibstoff", "Temperatur", "Temperatur", "Geschwindigkeit", "Entfernung", "Höhe", "Lipo-Zelle",
"Zellen gesamt", "Spannung", "Strom", "Verbrauch", "Power", "Beschleunigung X", "Beschleunigung Y", "Beschleunigung Z",
"Richtung", "Variometer", "Minimum", "Maximum"]):
systemSounds.append((s, filename(134 + i)))
for i, (s, f) in enumerate([("Fahrwerk eingezogen", "gearup"),
("Fahrwerk ausgefahren", "geardn"),
("Klappen eingefahren", "flapup"),
("Klappen ausgefahren", "flapdn"),
("Landung", "lnding"),
("Trainer-Modus ein", "trnon"),
("Trainer-Modus aus", "trnoff"),
("Motor aus", "engoff"),
("Motor an", "engon"),
("zu hoch", "tohigh"),
("zu niedrig", "tolow"),
("Batterie schwach", "lowbatt"),
("Butterfly ein", "crowon"),
("Butterfly aus", "crowof"),
("Geschwindigkeits-Modus ist aktiviert", "fm-spd"),
("Thermik-Modus ist aktiviert", "fm-thm"),
("Normal-Modus ist aktiviert", "fm-nrm"),
("Flugmodus 1", "fm-1"),
("Flugmodus 2", "fm-2"),
("Flugmodus 3", "fm-3"),
("Flugmodus 4", "fm-4"),
("Flugmodus 5", "fm-5"),
("Flugmodus 6", "fm-6"),
("Flugmodus 7", "fm-7"),
("Flugmodus 8", "fm-8"),
("Flugmodus 9", "fm-9"),
]):
sounds.append((s, filename(f)))
+84
View File
@@ -0,0 +1,84 @@
The purpose of this document is to document the principle of TTS for CZ languages to non CZ speaker, with a strict focus on OpenTX TTS
## Large 'multiparts' numbers
This is for numbers requiring assembly of basic number blocks, like 110 in english : `ONE` `HUNDRED` `TEN`
## Plural units forms
4 forms of plural exist in otx cz tts, bellow is the exemple for volts
`(u"volt", u"volt0"), (u"volty", u"volt1"), (u"voltů", u"volt2"),(u"voltu", u"volt3")`
In this document, we will use unit0 to unit3 to describe those 4 variations
## Interger numbers with unit
The number takes a from like X UNIT
#### "one ..."
The number "one" has 3 gender depending on the unit F/M/N
The forms are "jedna","jeden", "jedno"
See unit table bellow to find out wich gender of one should be used
unit0 is used with the 3 gender of one.
#### "two ..."
The number "two" has 3 gender depending on the unit
The forms are "dva","dvě"
See unit table bellow to find out wich gender of two should be used
unit1 is used with the 2 gender of two.
### "tree to five"
unit1 is used
### "five and more"
unit2 is used
## decimal numbers with unit
X.Y UNIT
In this case unit is unit3. X and Y is allways FEM.
"point" has three forms depending on the value of X :
0 and 1 : "celá"
2, 3 , 4 : "celé"
5 :"celých"
Note : opentx audio only deals with PREC1 numbers, so Y can only be integer 1 to 9
# Unit table
| Index | Unit | Defined as | One | Two | Gender
| --- | --- |--- |--- |--- |---
| 0 | Raw unit (no unit) | UNIT_RAW | jedna | dvě | Female
| 1 | Volts | UNIT_VOLTS | jeden | dva | Male
| 2 | Amps | UNIT_AMPS | jeden | dva | Male
| 3 | Milliamps | UNIT_MILLIAMPS | jeden | dva | Male
| 4 | Knots | UNIT_KTS | jeden | dva | Male
| 5 | Meters per Second | UNIT_METERS_PER_SECOND | jeden | dva | Male
| 6 | Feet per Second | UNIT_FEET_PER_SECOND | jedna | dvě | Female
| 7 | Kilometers per Hour| UNIT_KMH | jeden | dva | Male
| 8 | Miles per Hour | UNIT_MPH | jedna | dvě | Female
| 9 | Meters | UNIT_METERS | jeden | dva | Male
| 10 | Feet | UNIT_FEET | jedna | dvě | Female
| 11 | Degrees Celsius | UNIT_CELSIUS | jeden | dva | Male
| 12 | Degrees Fahrenheit | UNIT_FAHRENHEIT | jeden | dva | Male
| 13 | Percent | UNIT_PERCENT | jedno | dvě | Neutral
| 14 | Milliamp per Hour | UNIT_MAH | jedna | dvě | Female
| 15 | Watts | UNIT_WATTS | jeden | dva | Male
| 16 | Milliwatts | UNIT_MILLIWATTS | jeden | dva | Male
| 17 | dB | UNIT_DB | jeden | dva | Male
| 18 | RPM | UNIT_RPMS | jedna | dvě | Female
| 19 | G | UNIT_G | jedno | dvě | Neutral
| 20 | Degrees | UNIT_DEGREE | jeden | dva | Male
| 21 | Radians | UNIT_RADIANS | jeden | dva | Male
| 22 | Milliliters | UNIT_MILLILITERS | jeden | dva | Male
| 23 | Fluid Ounces | UNIT_FLOZ | jedna | dvě | Female
| 24 | Hours | UNIT_HOURS | jedna | dvě | Female
| 25 | Minutes | UNIT_MINUTES | jedna | dvě | Female
| 26 | Seconds | UNIT_SECONDS | jedna | dvě | Female
+139
View File
@@ -0,0 +1,139 @@
# -*- coding: utf-8 -*-
# English language sounds configuration
from tts_common import filename
systemSounds = []
sounds = []
for i in range(100):
systemSounds.append((str(i), filename(i)))
for i in range(9):
systemSounds.append((str(100 * (i + 1)), filename(100 + i)))
for i, s in enumerate(["thousand", "and", "minus", "point"]):
systemSounds.append((s, filename(109 + i)))
for i, (s, f) in enumerate([("volt", "volt0"), ("volts", "volt1"),
("amp", "amp0"), ("amps", "amp1"),
("milliamp", "mamp0"), ("milliamps", "mamp1"),
("knot", "knot0"), ("knots", "knot1"),
("meter per second", "mps0"), ("meters per second", "mps1"),
("foot per second", "fps0"), ("feet per second", "fps1"),
("kilometer per hour", "kph0"), ("kilometers per hour", "kph1"),
("mile per hour", "mph0"), ("miles per hour", "mph1"),
("meter", "meter0"), ("meters", "meter1"),
("foot", "foot0"), ("feet", "foot1"),
("degree celsius", "celsius0"), ("degrees celsius", "celsius1"),
("degree fahrenheit", "fahr0"), ("degrees fahrenheit", "fahr1"),
("percent", "percent0"), ("percent", "percent1"),
("milliamp-hour", "mamph0"), ("milliamp-hours", "mamph1"),
("watt", "watt0"), ("watts", "watt1"),
("milli-watt", "mwatt0"), ("milli-watts", "mwatt1"),
("D B", "db0"),("D B", "db1"),
("r p m", "rpm0"),("r p m", "rpm1"),
("g", "g0"),("g", "g1"),
("degree", "degree0"), ("degrees", "degree1"),
("radian", "rad0"), ("radians", "rad1"),
("milliliter", "ml0"), ("milliliters", "ml1"),
("fluid ounce", "founce0"), ("fluid ounces", "founce1"),
("milliliter per minute", "mlpm0"), ("milliliters per minute", "mlpm1"),
("hertz", "hertz0"), ("hertz", "hertz1"),
("milisecond", "ms0"), ("miliseconds", "ms1"),
("microsecond", "us0"), ("microseconds", "us1"),
("kilometer", "km0"), ("kilometers", "km1"),
("hour", "hour0"), ("hours", "hour1"),
("minute", "minute0"), ("minutes", "minute1"),
("second", "second0"), ("seconds", "second1"),
]):
systemSounds.append((s, filename(f)))
for i, s in enumerate(["point zero", "point one", "point two", "point three",
"point four", "point five", "point six",
"point seven", "point eight", "point nine"]):
systemSounds.append((s, filename(167 + i)))
for s, f in [("trim center", "midtrim"),
("maximum trim reached", "maxtrim"),
("minimum trim reached", "mintrim"),
("timer 1 elapsed", "timovr1"),
("timer 2 elapsed", "timovr2"),
("timer 3 elapsed", "timovr3"),
("transmitter battery low", "lowbatt"),
("inactivity alarm", "inactiv"),
("throttle warning", "thralert"),
("switch warning", "swalert"),
("bad eeprom", "eebad"),
("Welcome to open tea ex!", "hello"),
("RF signal, low", "rssi_org"),
("RF signal, critical", "rssi_red"),
("radio antenna defective", "swr_red"),
("telemetry lost", "telemko"),
("telemetry recovered", "telemok"),
("trainer signal lost", "trainko"),
("trainer signal recovered", "trainok"),
("sensor lost", "sensorko"),
("servo overload", "servoko"),
("power overload", "rxko"),
("receiver still connected", "modelpwr"),
]:
systemSounds.append((s, filename(f)))
for i, (s, f) in enumerate([("armed", "armed"),
("disarmed", "disarm"),
("throttle cut", "thrcut"),
("throttle active", "thract"),
("gear!, up!", "gearup"),
("gear!, down!", "geardn"),
("flaps!, up!", "flapup"),
("flaps!, down!", "flapdn"),
("spoiler!, up!", "splrup"),
("spoiler!, down!", "splrdn"),
("trainer!, on!", "trnon"),
("trainer!, off!", "trnoff"),
("engine!, off!", "engoff"),
("too. high!", "tohigh"),
("too. low!", "tolow"),
("low. battery!", "lowbat"),
("crow!, on!", "crowon"),
("crow!, off!", "crowof"),
("rf. signal!, low!", "siglow"),
("rf. signal!, critical!", "sigcrt"),
("LQ", "lq"),
("RF mode", "rfmode"),
("high. speed. mode!, active", "spdmod"),
("thermal. mode!, on", "thmmod"),
("normal. mode!, on", "nrmmod"),
("landing. mode!, on", "lnding"),
("acro. mode!, on", "acro"),
("flight mode one", "fm-1"),
("flight mode two", "fm-2"),
("flight mode three", "fm-3"),
("flight mode four", "fm-4"),
("flight mode five", "fm-5"),
("flight mode six", "fm-6"),
("flight mode seven", "fm-7"),
("flight mode eight", "fm-8"),
("vario!, on", "vrion"),
("vario!, off", "vrioff"),
("flight mode power", "fm-pwr"),
("flight mode land", "fm-lnd"),
("flight mode float", "fm-flt"),
("flight mode speed", "fm-spd"),
("flight mode fast", "fm-fst"),
("flight mode normal", "fm-nrm"),
("flight mode cruise", "fm-crs"),
("flight mode acro", "fm-acr"),
("flight mode race", "fm-rce"),
("flight mode launch", "fm-lch"),
("flight mode ping", "fm-png"),
("flight mode thermal", "fm-thm"),
("flight mode thermal left", "fm-thl"),
("flight mode thermal right", "fm-thr"),
("flight mode stabilize", "fm-stb"),
("flight mode horizon", "fm-hor"),
("flight mode angle", "fm-ang"),
("flight mode idle up 1", "fm-id1"),
("flight mode idle up 2", "fm-id2"),
("low rate", "ratlow"),
("medium rate", "ratmed"),
("high rate", "rathi"),
]):
sounds.append((s, filename(f)))
+110
View File
@@ -0,0 +1,110 @@
# -*- coding: utf-8 -*-
# Spanish language sounds configuration
from tts_common import filename
systemSounds = []
sounds = []
for i in range(101):
systemSounds.append((str(i), filename(i)))
systemSounds.append(("ciento", filename(101)))
systemSounds.append(("doscientos", filename(102)))
systemSounds.append(("trescientos", filename(103)))
systemSounds.append(("cuatrocientos", filename(104)))
systemSounds.append(("quinientos", filename(105)))
systemSounds.append(("sescientos", filename(106)))
systemSounds.append(("setecientos", filename(107)))
systemSounds.append(("ochocientos", filename(108)))
systemSounds.append(("nuevecientos", filename(109)))
systemSounds.append(("mil", filename(110)))
for i, s in enumerate(["coma", "un", "una", "i", "meno", "hora", "horas", "minuto", "minutos", "segundo", "segundos"]):
systemSounds.append((s, filename(111 + i)))
for i, (s, f) in enumerate([("Voltio","volt0"),
("ampério", "amp0"),
("miliamperios", "mamp0"),
("knots", "knot0"),
("metros por segundo", "mps0"),
("pie por segundo", "fps0"),
("kilómetro por hora", "kph0"),
("millas por hora", "mph0"),
("metros", "meter0"),
("pie", "foot0"),
("grados", "celsius0"),
("fahrenheit", "fahr0"),
("por ciento", "percent0"),
("miliamperios por hora", "mamph0"),
("vatio", "watt0"),
("millivatio", "mwatt0"),
("D B", "db0"),
("R P M", "rpm0"),
("g", "g0"),
("grados", "degree0"),
("radianes ", "rad0"),
("mililitro", "ml0"),
("onzas", "founce0"),
("hora", "hour0"), ("horas", "hour1"),
("milliliter per minute", "mlpm0"), ("milliliters per minute", "mlpm1"),
("minuto", "minute0"), ("minutos", "minute1"),
("segundo", "second0"), ("segundos", "second1"),
]):
systemSounds.append((s, filename(f)))
for s, f in [("me tienes abandonada", "inactiv"),
("batería del transmisor baja", "lowbatt"),
("El acelerador está activado, por favor, corrijalo", "thralert"),
("Los interruptores no están en la posición correcta, por favor corrijalo", "swalert"),
("eeprom corrompida", "eebad"),
("error", "error"),
("trim centrado", "midtrim"),
("trim al máximo", "maxtrim"),
("trim al mínimo", "mintrim"),
("consumo alto", "highmah"),
("temperatura alta", "hightemp"),
("Bienvenido a open t equis", "hello"),
("señal baja", "rssi_org"),
("señal crítica", "rssi_red"),
("Problemas con la antena del transmisor", "swr_red"),
("Sin telemetría", "telemko"),
("Telemetría disponible", "telemok"),
("sobrecarga de servo", "servoko"),
("sobrecarga de potencia", "rxko"),
("El receptor aún está encendido", "modelpwr"),
("cronómetro uno terminado", "timovr1"),
("cronómetro dos terminado", "timovr2"),
("cronómetro tres terminado", "timovr3"),
]:
systemSounds.append((s, filename(f)))
for i, s in enumerate(["cronómetro", "cronómetro", "transmisión", "recepción", "A1", "A2", "altitud", "motor",
"combustible", "temperatura", "temperatura", "velocidad", "distancia", "altitude", "célula lipo",
"Total lipo", "voltaje", "corriente", "consumo", "potencia", "aceleración X", "aceleración Y", "aceleración Z",
"dirección", "variómetro", "minimo", "máximo"]):
systemSounds.append((s, filename(141 + i)))
for i, (s, f) in enumerate([("tren arriba.", "gearup"),
("tren abajo.", "geardn"),
("flaps arriba", "flapup"),
("flaps abajo", "flapdn"),
("aterrizaje", "attero"),
("modo de entrenamiento activado", "trnon"),
("modo de entrenamiento deshabilitado", "trnoff"),
("motor apagado", "engoff"),
("muy alto", "tohigh"),
("muy bajo", "tolow"),
("bateria baja", "lowbat"),
("crou, activo", "crowon"),
("crou, desligado", "crowof"),
("modo de velocidad", "spdmod"),
("modo de térmica", "thmmod"),
("modo de vuelo normal", "nrmmod"),
("modo de vuelo 1", "fltmd1"),
("modo de vuelo 2", "fltmd2"),
("modo de vuelo 3", "fltmd3"),
("modo de vuelo 4", "fltmd4"),
("modo de vuelo 5", "fltmd5"),
("modo de vuelo 6", "fltmd6"),
("modo de vuelo 7", "fltmd7"),
("modo de vuelo 8", "fltmd8"),
("modo de vuelo 9", "fltmd9"),
]):
sounds.append((s, filename(f)))
+121
View File
@@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
# French language sounds configuration
from tts_common import filename
systemSounds = []
sounds = []
for i in range(100):
systemSounds.append((str(i), filename(i)))
for i in range(10):
systemSounds.append((str(100 * (i + 1)), filename(100 + i)))
for i, s in enumerate(["une", "onze", "vingt et une", "trente et une", "quarante et une", "cinquante et une", "soixante et une", "soixante et onze", "quatre vingt une"]):
systemSounds.append((s, filename(110 + i)))
for i, s in enumerate(["virgule", "et", "moins", "minuit", "midi"]):
systemSounds.append((s, filename(119 + i)))
for i, (s, f) in enumerate([("volts", "volt0"),
("ampères", "amp0"),
("milli ampères", "mamp0"),
("noeud", "knot0"),
("mètres seconde", "mps0"),
("pieds par seconde", "fps0"),
("kilomètre heure", "kph0"),
("miles par heure", "mph0"),
("mètres", "meter0"),
("pieds", "foot0"),
("degrés", "celsius0"),
("degrés fahrenheit", "degf0"),
("pourcents", "percent0"),
("milli ampères heure", "mamph0"),
("watt", "watt0"),
("milli watt", "mwatt0"),
("d b", "db0"),
("tours minute", "rpm0"),
("g", "g0"),
("degrés", "degree0"),
("radians", "rad0"),
("millilitres", "ml0"),
("onces", "founce0"),
("millilitre minute", "mlpm0"),
("microsecond", "us0"),
("milisecond", "ms0"),
("hertz", "hertz0"),
("kilometre", "km0"),
("heure", "hour0"),
("minute", "minute0"),
("seconde", "second0"),
]):
systemSounds.append((s, filename(f)))
for i, s in enumerate(["virgule 0", "virgule 1", "virgule 2", "virgule 3", "virgule 4", "virgule 5", "virgule 6", "virgule 7", "virgule 8", "virgule 9"]):
systemSounds.append((s, filename(180 + i)))
for s, f in [("Trim centré", "midtrim"),
("Trim maximum atteint", "maxtrim"),
("Trim minimum atteint", "mintrim"),
("Chrono 1 à 0", "timovr1"),
("Chrono 2 à 0", "timovr2"),
("Chrono 3 à 0", "timovr3"),
("Batterie radio faible !", "lowbatt"),
("Radio inactive !", "inactiv"),
("Alerte manche des gaz", "thralert"),
("Alerte inters", "swalert"),
("éprome corrompue", "eebad"),
("Bienvenue sur o pun t x", "hello"),
("Signal RF, faible", "rssi_org"),
("Signal RF, critique", "rssi_red"),
("Antenne défectueuse", "swr_red"),
("Télémétrie perdue", "telemko"),
("Télémétrie retrouvée", "telemok"),
("Signal écolage perdu", "trainko"),
("Signal écolage retrouvé", "trainok"),
("Sonde de télémétrie perdue", "sensorko"),
("Servo en surcharge", "servoko"),
("Surcharge réception", "rxko"),
("Récepteur encore sous tension", "modelpwr"),
]:
systemSounds.append((s, filename(f)))
for i, (s, f) in enumerate([("armé", "armed"),
("désarmé", "disarm"),
("altitude", "alt"),
("température moteur", "moteur"),
("température contrôleur", "tctrl"),
("train rentré", "gearup"),
("train sorti", "geardn"),
("volets rentrés", "flapup"),
("volets sortis", "flapdn"),
("atterrissage", "attero"),
("écolage", "trnon"),
("fin écolage", "trnoff"),
("moteur coupé", "engoff"),
("high. speed. mode!, active", "spdmod"),
("thermal. mode!, on", "thmmod"),
("normal. mode!, on", "nrmmod"),
("landing. mode!, on", "lnding"),
("mode acro", "acro"),
("mode de vol un", "fm-1"),
("mode de vol deux", "fm-2"),
("mode de vol trois", "fm-3"),
("mode de vol quatre", "fm-4"),
("mode de vol 5", "fm-5"),
("mode de vol 6", "fm-6"),
("mode de vol sept", "fm-7"),
("mode de vol 8", "fm-8"),
("vario activé", "vrion"),
("vario désactivé", "vrioff"),
("mode de vol puissance", "fm-pwr"),
("mode de vol attérissage", "fm-lnd"),
("mode de vol amérissage", "fm-flt"),
("mode de vol vitesse", "fm-spd"),
("mode de vol rapide", "fm-fst"),
("mode de vol normal", "fm-nrm"),
("mode de vol de croisière", "fm-crs"),
("mode de vol acro", "fm-acr"),
("mode de vol course", "fm-rce"),
("mode de vol lancé", "fm-lch"),
("mode de vol ping", "fm-png"),
("mode de vol thermique", "fm-thm"),
("mode de vol thermique gauche", "fm-thl"),
("mode de vol thermique droit", "fm-thr"),
]):
sounds.append((s, filename(f)))
+121
View File
@@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
# Italian language sounds configuration
from tts_common import filename
systemSounds = []
sounds = []
for i in range(101):
systemSounds.append((str(i), filename(i)))
systemSounds.append(("mila", filename(101)))
systemSounds.append(("mille", filename(102)))
for i, s in enumerate(["virgola", "un", "e", "meno", "ora", "ore", "minuto", "minuti", "secondo", "secondi"]):
systemSounds.append((s, filename(103 + i)))
for i, (s, f) in enumerate([("volt", "volt0"),("volt", "volt1"),
("ampere", "amp0"),("ampere", "amp1"),
("milliampere", "mamp0"),("milliampere", "mamp1"),
("nodo", "knot0"), ("nodi", "knot1"),
("metri al secondo", "mps0"), ("metri al secondo", "msp1"),
("piedi al secondo", "fps0"), ("piedi al secondo", "fps1"),
("chilometri orari", "kph0"), ("chilometri orari", "kph1"),
("miglia orarie", "mph0"), ("miglia orarie", "mph1"),
("metro", "meter0"), ("metri", "meter1"),
("piede", "foot0"), ("piedi", "foot1"),
("grado celsius", "celsius0"), ("gradi celsius", "celsius1"),
("grado fahrenheit", "fahr0"), ("gradi fahrenheit", "fahr1"),
("percento", "percent0"), ("percento", "percent1"),
("milliampere ora", "mamph0"), ("milliampere ora", "mamph1"),
("watt", "watt0"), ("watt", "watt1"),
("milli-watt", "mwatt0"), ("milli-watt", "mwatt1"),
("d b", "db0"), ("d b", "db1"),
("r p m", "rpm0"), ("r p m", "rpm1"),
("g", "g0"), ("g", "g1"),
("grado", "degree0"), ("gradi", "degree1"),
("radiante", "rad0"), ("radianti", "rad1"),
("millilitro", "m10"), ("millilitri", "m11"),
("oncia fluida", "founce0"), ("once fluide", "founce1"),
("millilitro per minuto", "mlpm0"), ("millilitri per minuto", "mlpm1"),
("ora", "hour0"), ("ore", "hour1"),
("minuto", "minute0"), ("minuti", "minute1"),
("secondo", "second0"), ("secondi", "second1"),
]):
systemSounds.append((s, filename(f)))
for s, f in [("radio inattiva controllare", "inactiv"),
("batteria della radio scarica", "lowbatt"),
("controllo motore non in posizione, verificare", "thralert"),
("interruttori non in posizione, verificare", "swalert"),
("eeprom corrotta", "eebad"),
("errore", "error"),
("trim centrato", "midtrim"),
("massimo trim raggiunto", "maxtrim"),
("minimo trim raggiunto", "mintrim"),
("assorbimento elevato", "highmah"),
("temperatura elevata", "hightemp"),
("Segnale radio basso", "rssi_org"),
("Segnale radio critico", "rssi_red"),
("Problema all'antenna della radio", "swr_red"),
("Telemetria assente", "telemko"),
("Telemetria disponibile", "telemok"),
("Segnale trainer perso", "trainko"),
("Segnale trainer disponibile", "trainok"),
("Sensore perso", "sensorko"),
("sovraccarico servo", "servoko"),
("sovraccarico di alimentazione", "rxko"),
("ricevente ancora connessa", "modelpwr"),
]:
systemSounds.append((s, filename(f)))
for i, s in enumerate(["timer", "", "tensione", "tensione", "trasmissione", "ricezione", "altitudine", "motore",
"carburante", "temperatura", "temperatura", "velocità", "distanza", "altitudine", "cella lipo",
"totale lipo", "tensione", "corrente", "consumo", "potenza", "accelerazione X", "accelerazione Y", "accelerazione Z",
"direzione", "variometro", "minimo", "massimo"]):
systemSounds.append((s, filename(155 + i)))
for i, (s, f) in enumerate([("carrello chiuso", "gearup"),
("carrello aperto", "geardn"),
("flap rientrati", "flapup"),
("flap estesi", "flapdn"),
("spoiler su", "splrup"),
("spoiler giù", "splrdn"),
("atterraggio", "attero"),
("modalità maestro attiva", "trnon"),
("modalità maestro disattiva", "trnoff"),
("motore spento", "engoff"),
("troppo alto", "tohigh"),
("troppo basso", "tolow"),
("batteria scarica", "lowbat"),
("crow attivato", "crowon"),
("crow disattivato", "crowof"),
("segnale radio basso", "siglow"),
("segnale radio critico", "sigcrt"),
("modo velocità", "spdmod"),
("modo termica", "thmmod"),
("modo volo normale", "nrmmod"),
("modo atterraggio", "lnding"),
("modo acro", "acro"),
("fase di volo 1", "fltmd1"),
("fase di volo 2", "fltmd2"),
("fase di volo 3", "fltmd3"),
("fase di volo 4", "fltmd4"),
("fase di volo 5", "fltmd5"),
("fase di volo 6", "fltmd6"),
("fase di volo 7", "fltmd7"),
("variometro attivato", "vrion"),
("variometro disabilitato", "vrioff"),
("fase di volo power", "fm-pwr"),
("fase di volo land", "fm-lnd"),
("fase di volo planata", "fm-flt"),
("fase di volo velocità", "fm-spd"),
("fase di volo veloce", "fm-fst"),
("fase di volo normale", "fm-nrm"),
("fase di volo cruise", "fm-crs"),
("fase di volo acro", "fm-acr"),
("fase di volo race", "fm-rce"),
("fase di volo lancio", "fm-lch"),
("fase di volo ping", "fm-png"),
("fase di volo termica", "fm-thm"),
("fase di volo termica sinistra", "fm-thl"),
("fase di volo termica destra", "fm-thr"),
]):
sounds.append((s, filename(f)))
+104
View File
@@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-
# Portuguese language sounds configuration
from tts_common import filename
systemSounds = []
sounds = []
for i in range(101):
systemSounds.append((str(i), filename(i)))
systemSounds.append(("cento", filename(101)))
systemSounds.append(("duzentos", filename(102)))
systemSounds.append(("trezentos", filename(103)))
systemSounds.append(("quatrocentos", filename(104)))
systemSounds.append(("quinhentos", filename(105)))
systemSounds.append(("seiscentos", filename(106)))
systemSounds.append(("setecentos", filename(107)))
systemSounds.append(("oitocentos", filename(108)))
systemSounds.append(("novecentos", filename(109)))
systemSounds.append(("mil", filename(110)))
for i, s in enumerate(["virgula", "uma", "duas", "e", "menos", "hora", "horas", "minuto", "minutos", "segundo", "segundos"]):
systemSounds.append((s, filename(111 + i)))
for i, (s, f) in enumerate([("Volt", "volt0"),
("ampére", "amp0"),
("miliamperes", "mamp0"),
("knot", "knot0"),
("metros por segundo", "mps0"),
("foot per second", "fps0"),
("quilômetros por hora", "kph0"),
("mile per hour", "mph0"),
("metros", "meter0"),
("foot", "foot0"),
("degree celsius", "celsius0"),
("degree fahrenheit", "fahr0"),
("percent", "percent0"),
("miliamperes por hora", "mamph0"),
("watt", "watt0"),
("milli-watt", "mwatt0"),
("db", "db0"),
("r p m", "rpm0"),
("g", "g0"),
("degree", "degree0"),
("radian", "rad0"),
("milliliter", "ml0"),
("onças", "founce0"),
("milliliter per minute", "mlpm0"), ("milliliters per minute", "mlpm1"),
("horas", "hour0"),
("minutos", "minute0"),
("segundos", "second0"),
]):
systemSounds.append((s, filename(f)))
for s, f in [("atenção, o rádio foi esquecido ligado, por favor desligue-o", "inactiv"),
("bateria do rádio fraca", "lowbatt"),
("atenção,acelerador não está no mínimo", "thralert"),
("atenção, certifique-se que os interruptores estão na posição certa", "swalert"),
("eeprom corrompida", "eebad"),
("erro", "error"),
("trim centrado", "midtrim"),
("trim no máximo", "maxtrim"),
("trim no mínimo", "mintrim"),
("assorbimento elevato", "highmah"),
("temperatura elevata", "hightemp"),
("recepção de sinal muito baixa", "rssi_org"),
("recepção de sinal crítica", "rssi_red"),
("Problema com a antena do transmissor", "swr_red"),
("servo overload", "servoko"),
("power overload", "rxko"),
("receiver still connected", "modelpwr"),
]:
systemSounds.append((s, filename(f)))
for i, s in enumerate(["cronómetro", "cronómetro", "transmissão", "recepção", "A1", "A2", "altitude", "motor",
"combustível", "temperatura", "temperatura", "velocidade", "distância", "altitude", "célula lipo",
"Total lipo", "tensão", "corrente", "consumo", "potência", "aceleração X", "aceleração Y", "aceleração Z",
"Direcção", "variómetro", "mínimo", "máximo"]):
systemSounds.append((s, filename(141 + i)))
for i, (s, f) in enumerate([("trem, em cima.", "gearup"),
("trem, em baixo.", "geardn"),
("flaps recolhidos", "flapup"),
("flaps estendidos", "flapdn"),
("aterragem", "attero"),
("modo de treino ligado", "trnon"),
("modo de treino desligado", "trnoff"),
("motor desligado", "engoff"),
("muito alto", "tohigh"),
("muito baixo", "tolow"),
("bateria fraca", "lowbat"),
("crou, activo", "crowon"),
("crou, desligado", "crowof"),
("modo de velocidade", "spdmod"),
("modo de térmica", "thmmod"),
("modo de voo normal", "nrmmod"),
("fase de voo 1", "fltmd1"),
("fase de voo 2", "fltmd2"),
("fase de voo 3", "fltmd3"),
("fase de voo 4", "fltmd4"),
("fase de voo 5", "fltmd5"),
("fase de vôo 6", "fltmd6"),
("fase de voo 7", "fltmd7"),
("fase de voo 8", "fltmd8"),
("fase de voo 9", "fltmd9"),
]):
sounds.append((s, filename(f)))
+171
View File
@@ -0,0 +1,171 @@
# -*- coding: utf-8 -*-
# Russian language sounds configuration
from tts_common import filename
systemSounds = []
sounds = []
# 0 - 100
for i in range(100):
systemSounds.append((str(i), filename(i)))
# 100, 200, ... 900
for i in range(9):
systemSounds.append((str(100 * (i + 1)), filename(100 + i)))
for i, s in enumerate([u"тысяча", u"и", u"минус"]):
systemSounds.append((s, filename(109 + i)))
for i, s in enumerate([u"тысяча", u"тысячи", u"тысяч"]):
systemSounds.append((s, filename(200 + i)))
# decimal point
for i, s in enumerate([u"ровно", u"и одна десятая", u"и две десятых", u"и три десятых", u"и четыре десятых",
u"и пять десятых", u"и шесть десятых", u"и семь десятых", u"и восемь десятых", u"и девять десятых" ]):
systemSounds.append((s, filename(165 + i)))
# last digit = 1, female
for i, s in enumerate([u"одна", u"одиннадцать", u"двадцать одна", u"тридцать одна", u"сорок одна",
u"пятьдесят одна", u"шестьдесят одна", u"семьдесят одна", u"восемьдесят одна", u"девяносто одна"]):
systemSounds.append((s, filename(180 + i)))
# last digit = 2, female
for i, s in enumerate([u"две", u"двенадцать", u"двадцать две", u"тридцать две", u"сорок две",
u"пятьдесят две", u"шестьдесят две", u"семьдесят две", u"восемьдесят две", u"девяносто две"]):
systemSounds.append((s, filename(190 + i)))
for i, (s, f) in enumerate([(u"вольт", "volt0"), (u"вольт", "volt1"),
(u"вольта", "volt2"), (u"вольт", "volt5"),
(u"ампер", "amp0"), (u"ампер", "amp1"),
(u"ампера", "amp2"), (u"ампер", "amp5"),
(u"миллиампер", "mamp0"), (u"миллиампер", "mamp1"),
(u"миллиампера", "mamp2"), (u"миллиампер", "mamp5"),
(u"узлов", "knot0"), (u"узел", "knot1"),
(u"узла", "knot2"), (u"узлов", "knot5"),
(u"метров в секунду", "mps0"), (u"метр в секунду", "mps1"),
(u"метра в секунду", "mps2"), (u"метров в секунду", "mps5"),
(u"футов в секунду", "fps0"), (u"фут в секунду", "fps1"),
(u"фута в секунду", "fps2"), (u"футов в секунду", "fps5"),
(u"километров в час", "kph0"), (u"километр в час", "kph1"),
(u"километра в час", "kph2"), (u"километров в час", "kph5"),
(u"миль в час", "mph0"), (u"миля в час", "mph1"),
(u"мили в час", "mph2"), (u"миль в час", "mph5"),
(u"метров", "meter0"), (u"метр", "meter1"),
(u"метра", "meter2"), (u"метров", "meter5"),
(u"футов", "foot0"), (u"фут", "foot1"),
(u"фута", "foot2"), (u"футов", "foot5"),
(u"градусов", "celsius0"), (u"градус", "celsius1"),
(u"градуса", "celsius2"), (u"градусов", "celsius5"),
(u"градусов по фаренгейту", "fahr0"), (u"градус по фаренгейту", "fahr1"),
(u"градуса по фаренгейту", "fahr2"), (u"градусов по фаренгейту", "fahr5"),
(u"процентов", "percent0"), (u"процент", "percent1"),
(u"процента", "percent2"), (u"процентов", "percent5"),
(u"миллиампер в час", "mamph0"), (u"миллиампер в час", "mamph1"),
(u"миллиампера в час", "mamph2"), (u"миллиампер в час", "mamph5"),
(u"ватт", "watt0"), (u"ватт", "watt1"),
(u"ватта", "watt2"), (u"ватт", "watt5"),
(u"милливатт", "mwatt0"), (u"милливатт", "mwatt1"),
(u"милливатта", "mwatt2"), (u"милливатт", "mwatt5"),
(u"децибел", "db0"),(u"децибел", "db1"),
(u"децибела", "db2"),(u"децибел", "db5"),
(u"оборотов в минуту", "rpm0"),(u"оборот в минуту", "rpm1"),
(u"оборота в минуту", "rpm2"),(u"оборотов в минуту", "rpm5"),
(u"джи", "g0"),(u"джи", "g1"),
(u"джи", "g2"),(u"джи", "g5"),
(u"градусов", "degree0"), (u"градус", "degree1"),
(u"градуса", "degree2"), (u"градусов", "degree5"),
(u"радиан", "rad0"), (u"радиан", "rad1"),
(u"радиана", "rad2"), (u"радиан", "rad5"),
(u"миллилитров", "ml0"), (u"миллилитр", "ml1"),
(u"миллилитра", "ml2"), (u"миллилитров", "ml5"),
(u"унций", "founce0"), (u"унция", "founce1"),
(u"унции", "founce2"), (u"унций", "founce5"),
(u"часов", "hour0"), (u"час", "hour1"),
(u"часа", "hour2"), (u"часов", "hour5"),
(u"минут", "minute0"), (u"минута", "minute1"),
(u"минуты", "minute2"), (u"минут", "minute5"),
(u"секунд", "second0"), (u"секунда", "second1"),
(u"секунды", "second2"), (u"секунд", "second5"),
]):
systemSounds.append((s, filename(f)))
for s, f in [
(u"Триммер по центру", "midtrim"),
(u"Триммер максимум", "maxtrim"),
(u"Триммер минимум", "mintrim"),
(u"Таймер 1. Время вышло", "timovr1"),
(u"Таймер 2. Время вышло", "timovr2"),
(u"Таймер 3. Время вышло", "timovr3"),
(u"Аккумулятор садится!", "lowbatt"),
(u"Внимание, пульт не выключен!", "inactiv"),
(u"Проверь газ!", "thralert"),
(u"Проверь, тумблеры!", "swalert"),
(u"Ошибка памяти", "eebad"),
(u"Опен тэ, икс приветствует вас!", "hello"),
(u"Слабый сигнал!", "rssi_org"),
(u"Очень слабый сигнал!", "rssi_red"),
(u"Проблема с антэнной!", "swr_red"),
(u"Телеметрия потеряна", "telemko"),
(u"Телеметрия восстановлена", "telemok"),
(u"Тренерский сигнал потерян", "trainko"),
(u"Тренерский сигнал восстановлен", "trainok"),
(u"Сенсор отключен", "sensorko"),
(u"Серва перегружена", "servoko"),
(u"Перегрузка", "rxko"),
(u"Приемник всё ещё подключен!", "modelpwr"),
]:
systemSounds.append((s, filename(f)))
for i, (s, f) in enumerate([
(u"Аккумулятор садится!", "lowbat"),
(u"Шасси, убраны'", "gearup"),
(u"Шасси, выпущены", "geardn"),
(u"Закрылки убраны'", "flapup"),
(u"Закрылки выпущены", "flapdn"),
(u"Спойлеры выпущены", "splrup"),
(u"Спойлеры - убраны'", "splrdn"),
(u"Тренер: подключен", "trnon"),
(u"Тренер: отключен", "trnoff"),
(u"Двигатели запущены!", "armed"),
(u"Двигатели выключены", "disarm"),
(u"Слишком высоко", "tohigh"),
(u"Слишком низко", "tolow"),
(u"Низкий уровень сигнала", "siglow"),
(u"Критически низкий уровень сигнала", "sigcrt"),
(u"Полетный режим один", "fm-1"),
(u"Полетный режим два", "fm-2"),
(u"Полетный режим три", "fm-3"),
(u"Полетный режим четыре", "fm-4"),
(u"Полетный режим пять", "fm-5"),
(u"Полетный режим шесть", "fm-6"),
(u"Полетный режим семь", "fm-7"),
(u"Полетный режим восемь", "fm-8"),
(u"Режим,акро", "fm-acr"),
(u"Режим круиза", "fm-crs"),
(u"Режим взлета", "fm-lch"),
(u"Режим посадки", "fm-lnd"),
(u"Нормальный режим", "fm-nrm"),
(u"Ручной режим", "fm-man"),
(u"Стабилизация", "fm-stb"),
(u"Удержание позиции", "fm-hld"),
(u"Возврат в точку старта", "fm-rth"),
(u"Горизонт", "fm-hor"),
(u"Стабилизация", "fm-ang"),
(u"аирмод включен", "airmon"),
(u"аирмод выключен", "airmof"),
(u"Высота", "alt"),
(u"Удержание высоты", "althld"),
(u"Критический разряд!", "batcrt"),
(u"Время полета", "flttm"),
(u"Аварийный режим выключен", "fsoff"),
(u"Аварийный режим включен", "fson"),
(u"ДжиПи Эс", "gps"),
(u"Высокие расходы", "hirate"),
(u"Низкие расходы", "lorate"),
(u"Спутники найдены!", "satfix"),
(u"Спутники потеряны!", "satlst"),
(u"Бортовые огни выключены", "ledoff"),
(u"Бортовые огни включены", "ledon")
]):
sounds.append((s, filename(f)))
+272
View File
@@ -0,0 +1,272 @@
SYSTEM|0000|0
SYSTEM|0001|1
SYSTEM|0002|2
SYSTEM|0003|3
SYSTEM|0004|4
SYSTEM|0005|5
SYSTEM|0006|6
SYSTEM|0007|7
SYSTEM|0008|8
SYSTEM|0009|9
SYSTEM|0010|10
SYSTEM|0011|11
SYSTEM|0012|12
SYSTEM|0013|13
SYSTEM|0014|14
SYSTEM|0015|15
SYSTEM|0016|16
SYSTEM|0017|17
SYSTEM|0018|18
SYSTEM|0019|19
SYSTEM|0020|20
SYSTEM|0021|21
SYSTEM|0022|22
SYSTEM|0023|23
SYSTEM|0024|24
SYSTEM|0025|25
SYSTEM|0026|26
SYSTEM|0027|27
SYSTEM|0028|28
SYSTEM|0029|29
SYSTEM|0030|30
SYSTEM|0031|31
SYSTEM|0032|32
SYSTEM|0033|33
SYSTEM|0034|34
SYSTEM|0035|35
SYSTEM|0036|36
SYSTEM|0037|37
SYSTEM|0038|38
SYSTEM|0039|39
SYSTEM|0040|40
SYSTEM|0041|41
SYSTEM|0042|42
SYSTEM|0043|43
SYSTEM|0044|44
SYSTEM|0045|45
SYSTEM|0046|46
SYSTEM|0047|47
SYSTEM|0048|48
SYSTEM|0049|49
SYSTEM|0050|50
SYSTEM|0051|51
SYSTEM|0052|52
SYSTEM|0053|53
SYSTEM|0054|54
SYSTEM|0055|55
SYSTEM|0056|56
SYSTEM|0057|57
SYSTEM|0058|58
SYSTEM|0059|59
SYSTEM|0060|60
SYSTEM|0061|61
SYSTEM|0062|62
SYSTEM|0063|63
SYSTEM|0064|64
SYSTEM|0065|65
SYSTEM|0066|66
SYSTEM|0067|67
SYSTEM|0068|68
SYSTEM|0069|69
SYSTEM|0070|70
SYSTEM|0071|71
SYSTEM|0072|72
SYSTEM|0073|73
SYSTEM|0074|74
SYSTEM|0075|75
SYSTEM|0076|76
SYSTEM|0077|77
SYSTEM|0078|78
SYSTEM|0079|79
SYSTEM|0080|80
SYSTEM|0081|81
SYSTEM|0082|82
SYSTEM|0083|83
SYSTEM|0084|84
SYSTEM|0085|85
SYSTEM|0086|86
SYSTEM|0087|87
SYSTEM|0088|88
SYSTEM|0089|89
SYSTEM|0090|90
SYSTEM|0091|91
SYSTEM|0092|92
SYSTEM|0093|93
SYSTEM|0094|94
SYSTEM|0095|95
SYSTEM|0096|96
SYSTEM|0097|97
SYSTEM|0098|98
SYSTEM|0099|99
SYSTEM|0100|100
SYSTEM|0101|200
SYSTEM|0102|300
SYSTEM|0103|400
SYSTEM|0104|500
SYSTEM|0105|600
SYSTEM|0106|700
SYSTEM|0107|800
SYSTEM|0108|900
SYSTEM|0109|1000
SYSTEM|0110|č
SYSTEM|0111|ěčíóń
SYSTEM|0112|
SYSTEM|0165|đîâíî
SYSTEM|0166|č îäíŕ äĺń˙ňŕ˙
SYSTEM|0167|č äâĺ äĺń˙ňűő
SYSTEM|0168|č ňđč äĺń˙ňűő
SYSTEM|0169|č ÷ĺňűđĺ äĺń˙ňűő
SYSTEM|0170|č ď˙ňü äĺń˙ňűő
SYSTEM|0171|č řĺńňü äĺń˙ňűő
SYSTEM|0172|č ńĺěü äĺń˙ňűő
SYSTEM|0173|č âîńĺěü äĺń˙ňűő
SYSTEM|0174|č äĺâ˙ňü äĺń˙ňűő
SYSTEM|volt0|âîëüň
SYSTEM|volt1|âîëüň
SYSTEM|volt2|âîëüňŕ
SYSTEM|volt5|âîëüň
SYSTEM|amp0|ŕěďĺđ
SYSTEM|amp1|ŕěďĺđ
SYSTEM|amp2|ŕěďĺđŕ
SYSTEM|amp5|ŕěďĺđ
SYSTEM|mamp0|ěčëëčŕěďĺđ
SYSTEM|mamp1|ěčëëčŕěďĺđ
SYSTEM|mamp2|ěčëëčŕěďĺđŕ
SYSTEM|mamp5|ěčëëčŕěďĺđ
SYSTEM|knot0|óçëîâ
SYSTEM|knot1|óçĺë
SYSTEM|knot2|óçëŕ
SYSTEM|knot5|óçëîâ
SYSTEM|mps0|ěĺňđîâ â ńĺęóíäó
SYSTEM|mps1|ěĺňđ â ńĺęóíäó
SYSTEM|mps2|ěĺňđŕ â ńĺęóíäó
SYSTEM|mps5|ěĺňđîâ â ńĺęóíäó
SYSTEM|celsius0|ăđŕäóńîâ
SYSTEM|celsius1|ăđŕäóń
SYSTEM|celsius2|ăđŕäóńŕ
SYSTEM|celsius5|ăđŕäóńîâ
SYSTEM|db0|äĺöčáĺë
SYSTEM|db1|äĺöčáĺë
SYSTEM|db2|äĺöčáĺëŕ
SYSTEM|db5|äĺöčáĺë
SYSTEM|degree0|ăđŕäóńîâ
SYSTEM|degree1|ăđŕäóń
SYSTEM|degree2|ăđŕäóńŕ
SYSTEM|degree5|ăđŕäóńîâ
SYSTEM|fahr0|ăđŕäóńîâ ďî ôŕđĺíăĺéňó
SYSTEM|fahr1|ăđŕäóń ďî ôŕđĺíăĺéňó
SYSTEM|fahr2|ăđŕäóńŕ ďî ôŕđĺíăĺéňó
SYSTEM|fahr5|ăđŕäóńîâ ďî ôŕđĺíăĺéňó
SYSTEM|foot0|ôóňîâ
SYSTEM|foot1|ôóň
SYSTEM|foot2|ôóňŕ
SYSTEM|foot5|ôóňîâ
SYSTEM|founce0|óíöčé
SYSTEM|founce1|óíöč˙
SYSTEM|founce2|óíöčč
SYSTEM|founce5|óíöčé
SYSTEM|fps0|ôóňîâ â ńĺęóíäó
SYSTEM|fps1|ôóň â ńĺęóíäó
SYSTEM|fps2|ôóňŕ â ńĺęóíäó
SYSTEM|fps5|ôóňîâ â ńĺęóíäó
SYSTEM|g0|äćč
SYSTEM|g1|äćč
SYSTEM|g2|äćč
SYSTEM|g5|äćč
SYSTEM|hour0|÷ŕńîâ
SYSTEM|hour1|÷ŕń
SYSTEM|hour2|÷ŕńŕ
SYSTEM|hour5|÷ŕńîâ
SYSTEM|kph0|ęčëîěĺňđîâ â ÷ŕń
SYSTEM|kph1|ęčëîěĺňđ â ÷ŕń
SYSTEM|kph2|ęčëîěĺňđŕ â ÷ŕń
SYSTEM|kph5|ęčëîěĺňđîâ â ÷ŕń
SYSTEM|mamph0|ěčëëčŕěďĺđ â ÷ŕń
SYSTEM|mamph1|ěčëëčŕěďĺđ â ÷ŕń
SYSTEM|mamph2|ěčëëčŕěďĺđŕ â ÷ŕń
SYSTEM|mamph5|ěčëëčŕěďĺđ â ÷ŕń
SYSTEM|meter0|ěĺňđîâ
SYSTEM|meter1|ěĺňđ
SYSTEM|meter2|ěĺňđŕ
SYSTEM|meter5|ěĺňđîâ
SYSTEM|minute0|ěčíóň
SYSTEM|minute1|ěčíóňŕ
SYSTEM|minute2|ěčíóňű
SYSTEM|minute5|ěčíóň
SYSTEM|ml0|ěčëëčëčňđîâ
SYSTEM|ml1|ěčëëčëčňđ
SYSTEM|ml2|ěčëëčëčňđŕ
SYSTEM|ml5|ěčëëčëčňđîâ
SYSTEM|mph0|ěčëü â ÷ŕń
SYSTEM|mph1|ěčë˙ â ÷ŕń
SYSTEM|mph2|ěčëč â ÷ŕń
SYSTEM|mph5|ěčëü â ÷ŕń
SYSTEM|mwatt0|ěčëëčâŕňň
SYSTEM|mwatt1|ěčëëčâŕňň
SYSTEM|mwatt2|ěčëëčâŕňňŕ
SYSTEM|mwatt5|ěčëëčâŕňň
SYSTEM|percent0|ďđîöĺíňîâ
SYSTEM|percent1|ďđîöĺíň
SYSTEM|percent2|ďđîöĺíňŕ
SYSTEM|percent5|ďđîöĺíňîâ
SYSTEM|radian0|đŕäčŕí
SYSTEM|radian1|đŕäčŕí
SYSTEM|radian2|đŕäčŕíŕ
SYSTEM|radian5|đŕäčŕí
SYSTEM|rpm0|îáîđîňîâ â ěčíóňó
SYSTEM|rpm1|îáîđîň â ěčíóňó
SYSTEM|rpm2|îáîđîňŕ â ěčíóňó
SYSTEM|rpm5|îáîđîňîâ â ěčíóňó
SYSTEM|second0|ńĺęóíä
SYSTEM|second1|ńĺęóíäŕ
SYSTEM|second2|ńĺęóíäű
SYSTEM|second5|ńĺęóíä
SYSTEM|watt0|âŕňň
SYSTEM|watt1|âŕňň
SYSTEM|watt2|âŕňňŕ
SYSTEM|watt5|âŕňň
SYSTEM|eebad|Îřčáęŕ ďŕě˙ňč
SYSTEM|hello|Ňŕęč çäđŕâńňâóéňĺ!
SYSTEM|inactiv|ß ňóň! Ďđî ěĺí˙ çŕáűëč!
SYSTEM|lowbatt|Ŕęęóěóë˙ňîđ đŕçđ˙ćĺí
SYSTEM|maxtrim|Ňđčěěĺđ ěŕęńčěóě
SYSTEM|midtrim|Ňđčěěĺđ ďî öĺíňđó
SYSTEM|mintrim|Ňđčěěĺđ ěčíčěóě
SYSTEM|rssi_org|Ńëŕáűé ńčăíŕë!
SYSTEM|rssi_red|Î÷ĺíü ńëŕáűé ńčăíŕë!
SYSTEM|rxko|
SYSTEM|sensorko|Ńĺíńîđ îňęëţ÷ĺí
SYSTEM|servoko|
SYSTEM|swalert|Ďđîâĺđü ňóěáëĺđű!
SYSTEM|swr_red|Ďđîáëĺěű ń ŕíňĺííîé ďĺđĺäŕň÷čęŕ!
SYSTEM|telemko|Ďîňĺđ˙í ńčăíŕë ňĺëĺěĺňđčč
SYSTEM|telemok|Ňĺëĺěĺňđč˙ âîńńňŕíîâëĺíŕ
SYSTEM|thralert|Óáĺđč ăŕç!
SYSTEM|timovr1|Ňŕéěĺđ 1. Âđĺě˙ âűřëî
SYSTEM|timovr2|Ňŕéěĺđ 2. Âđĺě˙ âűřëî
SYSTEM|timovr3|Ňŕéěĺđ 3. Âđĺě˙ âűřëî
SYSTEM|trainko|Ňđĺíĺđńęčé ńčăíŕë ďîňĺđ˙í
SYSTEM|trainok|Ďî˙âčëń˙ ňđĺíĺđńęčé ńčăíŕë
SYSTEM|0180|îäíŕ
SYSTEM|0181|îäčííŕäöŕňü
SYSTEM|0182|äâŕäöŕňü îäíŕ
SYSTEM|0183|ňđčäöŕňü îäíŕ
SYSTEM|0184|ńîđîę îäíŕ
SYSTEM|0185|ď˙ňüäĺń˙ň îäíŕ
SYSTEM|0186|řĺńňüäĺń˙ň îäíŕ
SYSTEM|0187|ńĺěüäĺń˙ň îäíŕ
SYSTEM|0188|âîńĺěüäĺń˙ň îäíŕ
SYSTEM|0189|äĺâ˙íîńňî îäíŕ
SYSTEM|0190|äâĺ
SYSTEM|0191|äâĺíŕäöŕňü
SYSTEM|0192|äâŕäöŕňü äâĺ
SYSTEM|0193|ňđčäöŕňü äâĺ
SYSTEM|0194|ńîđîę äâĺ
SYSTEM|0195|ď˙ňüäĺń˙ň äâĺ
SYSTEM|0196|řĺńňüäĺń˙ň äâĺ
SYSTEM|0197|ńĺěüäĺń˙ň äâĺ
SYSTEM|0198|âîńĺěüäĺń˙ň äâĺ
SYSTEM|0199|äĺâ˙íîńňî äâĺ
SYSTEM|0200|ňűń˙÷ŕ
SYSTEM|0201|ňűń˙÷č
SYSTEM|0202|ňűń˙÷
+77
View File
@@ -0,0 +1,77 @@
// generated by generate_yaml.py
//
// Enums first
//
{% for enum in root.get_enums() %}
const struct YamlIdStr {{ enum.name }}[] = {
{% for elmt in enum.get_elmts() %}
{ {{ elmt.value }}, "{{ max_len(elmt.name) }}" },
{% endfor %}
{ 0, NULL }
};
{% endfor %}
//
// Structs last
//
{% for struct in root.get_structs() %}
{% if struct.name.startswith('union_') %}
static const struct YamlNode {{ struct.name }}_elmts[] = {
{% else %}
static const struct YamlNode {{ struct.name }}[] = {
{% if struct.use_idx %}
YAML_IDX,
{% endif%}
{% endif %}
{% for elmt in struct.get_elmts() %}
{% if elmt.skip %}
YAML_PADDING( {{ elmt.bits }} ),
{% elif elmt.type == 'string' %}
YAML_STRING("{{ max_len(elmt.name) }}", {{elmt.length}}),
{% elif elmt.type in ['signed','unsigned'] %}
{% if elmt.f_read and elmt.f_write %}
YAML_{{elmt.type|upper}}_CUST( "{{ max_len(elmt.name) }}", {{ elmt.bits }}, {{ elmt.f_read}}, {{ elmt.f_write}} ),
{% else %}
YAML_{{elmt.type|upper}}( "{{ max_len(elmt.name) }}", {{ elmt.bits }} ),
{% endif %}
{% elif elmt.type == 'enum' %}
YAML_ENUM("{{ max_len(elmt.name) }}", {{elmt.bits}}, {{elmt.var_type}}, {{ elmt.func }}),
{% elif elmt.type == 'struct' %}
YAML_STRUCT("{{ max_len(elmt.name) }}", {{elmt.bits}}, {{elmt.var_type}}, {{ elmt.func }}),
{% elif elmt.type == 'union' %}
YAML_UNION("{{ max_len(elmt.name) }}", {{elmt.bits}}, {{elmt.var_type}}_elmts, {{ elmt.func}}),
{% elif elmt.type == 'array' %}
YAML_ARRAY("{{ max_len(elmt.name) }}", {{elmt.bits // elmt.length}}, {{elmt.length}}, {{elmt.var_type}}, {{ elmt.func }}),
{% if padding_bits(elmt) > 0 %}
YAML_PADDING({{ padding_bits(elmt) }}),
{% endif %}
{% elif elmt.raw %}
{{ elmt.raw }},
{% else %}
// name='{{elmt.name}}', type='{{elmt.type}}', bits='{{elmt.bits}}', is_array='{{elmt.is_array}}', var_type='{{elmt.var_type}}', kind='{{elmt.cursor.kind}}' , type.kind='{{elmt.cursor.type.kind}}', raw='{{elmt.raw}}'
{% endif %}
{% endfor %}
YAML_END
};
{% if struct.name.startswith('union_') and struct.used_in_arrays %}
static const struct YamlNode {{ struct.name }}[] = {
YAML_IDX,
YAML_UNION("u", {{ max_bits(struct) }}, {{ struct.name }}_elmts, {{ struct.func }}),
YAML_END
};
{% endif %}
{% endfor %}
#define MAX_{{ root_node_name | upper }}_STR_LEN {{ get_max_len() }}
{% for tn in root_nodes %}
static const struct YamlNode __{{ tn }}_root_node = YAML_ROOT( struct_{{ tn }} );
const YamlNode* get_{{ tn | lower }}_nodes()
{
return &__{{ tn }}_root_node;
}
{% endfor %}