first testing, started the code

This commit is contained in:
janik
2025-10-07 17:10:35 +07:00
parent a5e5a3b96d
commit 7ff466fff5
176 changed files with 62614 additions and 153 deletions

179
python/LUT.py Normal file
View File

@@ -0,0 +1,179 @@
#!/usr/bin/env python3
"""
Generate a C-style PWM lookup table.
Defaults:
- max PWM: 0xD480 (54400)
- period: 10 ms
- spacing: 20 us -> 500 samples
- mode: 'half' (one half-wave over the period)
- thresholds and zero-boundary configurable
- emits uint16_t array
Examples:
python LUT.py --mode half --period-us 10000 --spacing-us 20 --array-name pwm_sine --per-line 10
python LUT.py --mode full --hex
"""
import argparse
from math import sin, pi
def positive_int(v):
iv = int(v, 0)
if iv <= 0:
raise argparse.ArgumentTypeError("value must be > 0")
return iv
def nonneg_int(v):
iv = int(v, 0)
if iv < 0:
raise argparse.ArgumentTypeError("value must be >= 0")
return iv
def build_table(max_pwm,
period_us,
spacing_us,
upper,
lower,
zero_boundary,
amplitude,
phase_deg,
samples,
mode):
# Determine sample count
if samples is None:
if period_us % spacing_us != 0:
raise ValueError(
"period_us must be a multiple of spacing_us, or pass --samples.")
n = period_us // spacing_us
else:
n = int(samples)
if n <= 1:
raise ValueError("samples must be >= 2")
# Normalize thresholds
if upper is None:
upper = max_pwm
upper = min(max_pwm, max(0, int(upper)))
lower = min(max_pwm, max(0, int(lower)))
if lower > upper:
raise ValueError("lower must be <= upper")
if amplitude <= 0 or amplitude > 1.0:
raise ValueError("amplitude must be in (0, 1]")
if zero_boundary < 0:
raise ValueError("zero_boundary must be >= 0")
# Precompute
phase_rad = phase_deg * pi / 180.0
vals = []
if mode == "half":
# One half-wave across the whole table: theta in [0 .. π]
# Use (n-1) in denominator so first=0 and last=0 exactly.
denom = max(1, n - 1)
for k in range(n):
theta = pi * (k / denom) + phase_rad
s = sin(theta)
if s < 0:
s = 0.0 # half-wave rectified
raw = int(round(s * amplitude * max_pwm))
clipped = max(lower, min(upper, raw))
if clipped < zero_boundary:
clipped = 0
vals.append(clipped)
elif mode == "full":
# Full unipolar: theta in [0 .. 2π), offset to [0..1]
for k in range(n):
theta = 2.0 * pi * (k / n) + phase_rad
s = (sin(theta) * amplitude + 1.0) * 0.5
raw = int(round(s * max_pwm))
clipped = max(lower, min(upper, raw))
if clipped < zero_boundary:
clipped = 0
vals.append(clipped)
else:
raise ValueError("mode must be 'half' or 'full'")
return vals
def format_c_array(values, array_name, c_type, radix_hex, per_line, add_header, comment):
n = len(values)
lines = []
if comment:
lines.append("/* " + comment + " */")
if add_header:
lines.append("#include <stdint.h>")
lines.append("const {ctype} {name}[{n}] = {{".format(ctype=c_type, name=array_name, n=n))
def fmt(v):
if radix_hex:
return "0x{0:04X}".format(v & 0xFFFF)
return str(v)
for i in range(0, n, per_line):
chunk = values[i:i+per_line]
trailing_comma = "," if (i + per_line) < n else ""
lines.append(" " + ", ".join(fmt(v) for v in chunk) + trailing_comma)
lines.append("};")
return "\n".join(lines)
def main():
p = argparse.ArgumentParser(description="Generate a C-style PWM lookup table.")
p.add_argument("--max-pwm", type=positive_int, default=0xD480, help="Max PWM (default 0xD480=54400)")
p.add_argument("--period-us", type=positive_int, default=10_000, help="Period in us (default 10000 = 10 ms)")
p.add_argument("--spacing-us", type=positive_int, default=20, help="Spacing in us (default 20)")
p.add_argument("--samples", type=positive_int, default=None, help="Override sample count")
p.add_argument("--mode", choices=["half", "full"], default="half",
help="Wave mode: 'half' = one half-wave (default), 'full' = full unipolar cycle")
p.add_argument("--upper", type=nonneg_int, default=None, help="Upper clamp (default: max-pwm)")
p.add_argument("--lower", type=nonneg_int, default=0, help="Lower clamp (default: 0)")
p.add_argument("--zero-boundary", type=nonneg_int, default=544, help="Values below this set to 0 after clipping")
p.add_argument("--amplitude", type=float, default=0.8, help="Amplitude scale in (0,1] (default 1.0)")
p.add_argument("--phase-deg", type=float, default=0.0, help="Phase offset degrees (default 0)")
p.add_argument("--array-name", default="pwm_sine", help="C array name (default pwm_sine)")
p.add_argument("--c-type", default="uint16_t", help="C integer type (default uint16_t)")
p.add_argument("--hex", action="store_true", help="Emit hex values")
p.add_argument("--per-line", type=positive_int, default=10, help="Values per line (default 10)")
p.add_argument("--no-header", action="store_true", help="Do not emit #include line")
p.add_argument("--comment", default=None, help="Optional leading block comment")
args = p.parse_args()
vals = build_table(
max_pwm=args.max_pwm,
period_us=args.period_us,
spacing_us=args.spacing_us,
upper=args.upper,
lower=args.lower,
zero_boundary=args.zero_boundary,
amplitude=args.amplitude,
phase_deg=args.phase_deg,
samples=args.samples,
mode=args.mode
)
if args.comment is None:
args.comment = ("Auto-generated PWM table: mode={m}, max_pwm={mp} (0x{mp:X}), "
"period_us={per}, spacing_us={sp}, samples={n}, "
"upper={up}, lower={lo}, zero_boundary={zb}, "
"amplitude={a}, phase_deg={ph}".format(
m=args.mode, mp=args.max_pwm, per=args.period_us,
sp=args.spacing_us, n=len(vals),
up=(args.upper if args.upper is not None else args.max_pwm),
lo=args.lower, zb=args.zero_boundary,
a=args.amplitude, ph=args.phase_deg))
out = format_c_array(
values=vals,
array_name=args.array_name,
c_type=args.c_type,
radix_hex=args.hex,
per_line=args.per_line,
add_header=not args.no_header,
comment=args.comment
)
print(out)
if __name__ == "__main__":
main()