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>
8.1 KiB
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
_bemis 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
_Rvaluethat 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.exeand 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.shcopies it into the image it builds, so an image you publish carries that licence alongside this one.