magdevandClaude Fable 5 85a75b321d Add MIT licence
Declared with the PEP 639 license / license-files fields, so the text ships in
the wheel and the image rather than only the repository. Records the terms of
the two things this repository deliberately does not contain: Boxsim, which is
Visaton freeware and is never redistributed, and kicad-visaton-lib, which is
CC-BY-SA-4.0 and is copied into any image built by tools/build-image.sh.

Verified: pip install exposes License-Expression MIT and packages the text as
dist-info/licenses/LICENSE, both on the host and in the image.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-29 12:35:09 +02:00
2026-09-29 12:35:09 +02:00
2026-09-29 12:35:09 +02:00
2026-09-29 12:35:09 +02:00
2026-09-29 12:35:09 +02:00
2026-09-29 12:35:09 +02:00

boxsim

Read Boxsim loudspeaker projects and turn their crossovers into KiCad projects.

Boxsim is Visaton's free loudspeaker simulation program. Its project files (.bpj) and driver files (.bch) use an undocumented container, reverse engineered here. boxsim decodes them and generates a KiCad 10 project whose schematic mirrors the crossover you drew in Boxsim, with every part bound to a symbol and footprint from kicad-visaton-lib.

Install

pip install .

Nothing is required at runtime beyond Python 3.11+. KiCad 10 is a hard dependency of the verification gate (boxsim build --verify) only, and lives on the host.

Point the tool at the Visaton library and the Boxsim parts catalogue:

git clone ssh://git@src.bundespruefstelle.ch:2022/magdev/kicad-visaton-lib.git
export BOXSIM_KICAD_LIB=/path/to/kicad-visaton-lib
export BOXSIM_CATALOG="/path/to/BoxSim 2.1 DE/Weichenbauteile/Visatonteile.txt"

The symbol library must be in the current KiCad format. Convert an older one with kicad-cli sym upgrade --force symbols/Visaton.kicad_sym.

Use

boxsim decode  project.bpj -o project.ini     # raw INI payload
boxsim extract project.bpj -o project.json    # structured model, symbols resolved
boxsim netlist project.bpj                    # crossover netlist
boxsim build   project.bpj out/ --verify      # a KiCad 10 project

build writes <name>.kicad_sch, <name>.kicad_pro and project-local sym-lib-table / fp-lib-table. No board is generated: use KiCad's Update PCB from Schematic, which works because every Visaton symbol carries a Footprint property.

Docker

./tools/build-image.sh
docker run --rm -v "$PWD:/work" boxsim:0.1.0 build project.bpj out/

The image bundles the tool, the Visaton library and the catalogue. It contains no KiCad and no Boxsim.exe, so decode, extract, netlist and build work inside it while --verify does not. Run the gate on the host.

The file format

Layer Content
1 8-byte plaintext magic MyIni2CK
2 XOR against a keystream from a table-shuffled three-LCG generator, seeded (123, 456) for every file ever written
3 A custom LZ plus canonical-Huffman codec ("A2F")
4 INI-style text: #Section headers, key<TAB>value, CRLF, German decimal commas, ;-separated arrays

Files written by Boxsim 1.2 use an older container without the magic and are rejected.

The Krypt keystream

Three linear congruential generators feeding a 256-entry shuffle table (Boxsim 2.1: init at VA 0xa12be4, next byte at 0xa12c9c). All arithmetic mod 2^32:

init(s1=123, s2=456):
    s3 = (s1 mod 0xFFFE) * (s2 mod 0x7F23)
    256 times: step(); table[i] = s1 + s2

step():  s1 = s1*0x10B95 + 1
         s2 = s2*0x10BA9 + 1        (reset to 0 if >= 0xFFFFFFFF)
         s3 = s3*0x1155D + 1

next():  step(); i = s3 and 0xFF
         out = table[i]; table[i] = s1 + s2
         return out mod 256

The A2F codec

A bit stream read MSB first through a 31-bit accumulator, with symbols from a canonical Huffman code over 356 symbols decoded via a 65536-entry direct lookup table. All code lengths start at 9.

Symbol Meaning
0x000-0x0FF literal byte
0x100 end of stream
0x101 new code lengths follow, rebuild the tables
0x102 define N entries of the 64-entry digram dictionary
0x103-0x10A match, length 3, distance in k+3 bits
0x10B-0x114 match, length 4, distance in k+3 bits
0x115-0x123 match, unary-coded length class, distance in k+3 bits
0x124-0x163 emit the two bytes of digram dictionary entry k

Distances take k+3 raw bits biased by 2**(k+3) - 7, so the classes tile 1.. with no gaps. Code lengths are coded as two zero bits meaning "same as previous", otherwise a nibble plus one, where the value 5 escapes to a second nibble.

Boxsim decodes from a staging buffer and reads past the end of the input for the final symbol's lookup window, so the decoder tolerates a bounded number of zero padding bytes. Beyond that a truncated file fails loudly.

The crossover grid

The crossover editor is an 11 x 18 node grid. A slot number encodes position and orientation:

Range Meaning
1-180 horizontal, 10 per row, joins node(r,c) to node(r,c+1)
181-367 vertical, 11 per row, joins node(r,c) to node(r+1,c)
368-375 free link, endpoints given by _nod1 and _nod2

Nodes are numbered row-major and zero based: node = row * 11 + col.

Keys in a #Weiche<n> section:

Key Meaning
_Part<N>_typ 0 empty, 1 wire, 2 resistor, 3 capacitor, 4 inductor, 5 driver, 6 amplifier input
_Part<N>_value SI units: Ohm, Farad, Henry
_Part<N>_Rvalue inductor DC resistance in Ohm
_Part<N>_bem Visaton series name, matching Visatonteile.txt exactly
_Part<N>_chassisnr zero-based driver index
_Part<N>_verpolt polarity, -1 meaning inverted

Other sections: #General, #AktFilter<n> and #Aktfilter<n> (active filters, note the differing case), #Chassis<n> (name plus Thiele-Small parameters and measured response arrays), #Einbau<n> (enclosure, Vb in m³), #Schallwand<n> (baffle geometry in m), #GemGeh (shared enclosure).

How the parts are resolved

Weichenbauteile/Visatonteile.txt is the authoritative parts list: 95 parts across 11 series, with inductor DC resistance in the fourth column. Its series strings are exactly the _bem values Boxsim writes, so resolution is a lookup rather than a guess.

Values are matched numerically against the library rather than formatted into symbol names, because the library's own strings are irregular (MKP 1.0μF beside MKP 10μF, SP-Spule 1.0mH (0.6) beside LR-Spule 10mH).

  • An empty _bem is inferred from a sibling part of the same type and value in the same crossover, else from the widest series for that type. Every inference is logged.
  • A _Rvalue that differs from the catalogue is reported, never corrected: it is a legitimate edit in Boxsim.
  • A value Visaton does not stock is a hard error naming the nearest stocked values. Nothing is substituted silently.

The library contains one symbol the catalogue does not list, MKT 22μF. It is a real Visaton part (radial film capacitor, 250 V) that Boxsim does not offer in its picker, so no project can reference it.

Verification

ERC only proves a schematic is well formed; it would happily pass one wired to the wrong cells. So boxsim build --verify compares the netlist KiCad extracts from the generated sheet against the netlist derived independently from the Boxsim grid, and both derivations must agree.

The codec is held to the same standard. tools/difftest.py decodes every container in the Boxsim distribution twice, once with the pure Python codec and once by emulating Boxsim's own ExpandA2F under Unicorn, and requires byte-identical output:

.venv/bin/python tools/difftest.py --self-test
# 134 byte-identical, 270 rejected by both as pre-2.0 containers

Both gates are checked against a deliberately broken input so they are known to fail when they should: --self-test corrupts a digest, and test_gate_has_teeth mis-wires a component.

The oracle is development-only. It needs unicorn, pefile and a copy of Boxsim.exe:

pip install -e '.[oracle,dev]'
export BOXSIM_EXE="/path/to/Boxsim.exe"

Tests

pytest                                  # fast, no Boxsim.exe needed
ruff check boxsim tools tests

Tests needing KiCad or the Boxsim distribution skip themselves when those are absent.

Licence

MIT, see LICENSE.

Two things this repository deliberately does not contain, and their terms if you assemble them yourself:

  • Boxsim.exe and the Boxsim distribution are Visaton freeware. Only the development-only oracle touches the binary, and no part of it is redistributed here or in the image.
  • kicad-visaton-lib is CC-BY-SA-4.0. tools/build-image.sh copies it into the image it builds, so an image you publish carries that licence alongside this one.
S
Description
Boxsim project file format reverse enigneering
Readme MIT
391 KiB
1 Watchers 0 Forks
Languages
Python 97.3%
Shell 1.4%
Dockerfile 1.3%