einheitensicher / spec v1.1 / c99

Einheitensichere Serialisierung
für Wissenschaft und Industrie.

Jeder Wert trägt Breite, Basis und physikalische Einheit direkt in sich — kein Schema, nur menschenlesbarer Text. Ein bloßes 9.81 ist nie mehrdeutig, und eine unpassende Einheit ist ein Parse-Fehler.

bovnar — telemetry.bvnr
telemetry.bvnr
examples telemetry.bvnr
1
2
3
4
5
6
7
8
# Jede Messung trägt eine validierte Einheit .host = "api.sensors.io"; .mass = 142.6 k~g; .sensor = { .pressure = <float:32,k~Pa> 101.325; .velocity = <float:64,m/s> 9.81; }; .matrix = [1,2,3]/[4,5,6];
◈ BOVNAR
UTF-8 Z. 8, Sp. 1 einheitensicher
scrollen

Ein Format, durchgängigKonfiguration rein, dekodierte Werte raus.

Sie haben die Syntax gesehen — hier läuft ein Raumfahrzeug damit. Ein Format, der ganze Kreislauf: Konfiguration rein, Telemetrie raus. Verändern Sie den Orbit; das Fahrzeug kodiert jede Messung in einen .bvnr-Frame, und die Bodenstation dekodiert ihn — die sichtbare Bahn wird ausschließlich aus diesen dekodierten Werten gezeichnet, ein vollständiger Round-Trip durch das Format. Einheiten, Bitbreiten und Typen reisen im Datenstrom selbst mit; kein Schema nötig. Fahren Sie über eine Anzeige, um die Zeile hervorzuheben, aus der sie dekodiert wurde.

Jeder Frame wird von der C-Referenzimplementierung selbst geparst, übersetzt nach WebAssembly und live in Ihrem Browser ausgeführt — exakt das bvnr_read() aus der nativen Bibliothek, keine JavaScript-Nachbildung. Dieselbe Engine treibt alle drei Demos und den Playground weiter unten an.

Wie es funktioniert — der vollständige Round-Trip
  1. Die Konfiguration links ist kein Formular — sie ist ein echtes .bvnr-Dokument, das Sie frei bearbeiten können. Nichts ändert sich, bis Sie Übernehmen drücken.
  2. Dann wird sie direkt aus dem Text gelesen. Ergibt etwas keinen Sinn, erhalten Sie eine verständliche Fehlermeldung mit Zeilenangabe, und die Live-Ansicht läuft mit der letzten gültigen Konfiguration weiter — eine fehlerhafte Änderung kann nichts kaputtmachen.
  3. Diese Werte setzen den Rahmen, den Rest übernimmt die Demo.
  4. Von da an wird jeder Messwert als .bvnr geschrieben und wieder eingelesen — dasselbe Format, das Sie eben bearbeitet haben. Einheiten und Typen reisen unmittelbar neben den Zahlen mit.
  5. Anzeigen und Diagramme zeigen also ausschließlich das, was durch das Format zurückkam, nie eine Zahl auf Treu und Glauben. Schalten Sie das Rauschen ein und sehen Sie zu, wie ein beschädigter Frame einfach weggesteckt wird.
  6. Fahren Sie über eine Anzeige, um genau die Zeile hervorzuheben, aus der sie gelesen wurde.
jede Zahl, die Sie sehen, legt diesen Weg zurück:
bearbeitenparsensimulierenkodieren(beschädigen?)parsenanzeigen
Raumfahrzeug-Konfiguration bearbeitbar · .bvnr
Orbit-Visualisierung ECI · XY-Projektion
Erde (Tag / Nacht) Raumfahrzeug (dekodierte Position)
Downlink-Frame .bvnr-Datenstrom
160×
Frame 0 MET 0 s Orbits 0.00 verworfen 0 Lock ACQ


      
Mit aktivem Resync wird eine beschädigte Zuweisung übersprungen und der Rest des Frames trotzdem dekodiert — der Wiederherstellungsmodus des Readers. Ohne ihn verwirft eine einzige fehlerhafte Zeile den ganzen Frame. Schalten Sie verrauschter Kanal um und vergleichen Sie.

Der echte Event-Stream.

Dies ist der on_verified-Callback-Stream des C-Referenzreaders, live in Ihrem Browser — der unveränderte C-Kern, übersetzt nach WebAssembly, keine JavaScript-Nachbildung. Dieselben Events, in derselben Reihenfolge, die auch bvnr_read() liefert. Nicht annotierte Werte erhalten die vom Validator synthetisierte Standardannotation; Verletzungen von Typ, Wertebereich, Basis und Einheit erscheinen auf dem separaten on_error-Kanal, genau so, wie der C-Kern sie meldet.

Editor
bereit
on_verified · Event-Baum 0 Events
Events erscheinen hier, während Sie tippen.
on_error
keine Fehler

Der Kontext reist mit der Zahl.

In Wissenschaft und Industrie entstehen die teuren Fehler nicht durch fehlerhafte Syntax, sondern durch Einheitenverwechslung: Pound-force als Newton gelesen, Fuß als Meter. Die Zahl lässt sich einwandfrei parsen; die 9.81 stimmt, aber der Kontext ist falsch. Bovnar behält diesen Kontext — Breite, Basis und Einheit — direkt bei jedem Wert, als schlichten, menschenlesbaren Text ohne externes Schema, und weist eine unpassende Einheit als Parse-Fehler zurück, statt sie zu einem stillen Bug werden zu lassen.

eine Zahl, ohne Kontext
9.81
Breite? Basis? Einheit? Reine Spekulation, solange weder ein externes Schema noch eine Namenskonvention aushilft.
ein Wert, im Kontext
.thrust = <float:64,k~g·m·s⁻²> 9.81;
Typ, Breite und Einheit reisen direkt im selben Byte-Strom wie der Wert. Kein Schema, keine Codegenerierung, keine Nachschlagetabelle.
01 Bitbreite
<uint:16> 443
Exakte Speicherbreite, direkt deklariert und niemals hergeleitet — von einem einzelnen Bit bis zu Tausenden. Ein uint:16 bleibt exakt 16 Bit breit: Er kann sich nicht stillschweigend auf 64 verbreitern und nicht als Float umgedeutet werden.
02 Zahlenbasis
<uint:32,_2> 11001010
Binär, hexadezimal oder dezimal — die Basis ist deklariert, also wird 11001010 nie für eine Dezimalzahl gehalten. Werte, deren Ziffern Buchstaben enthalten (etwa hexadezimal ff), werden als Zeichenketten in Anführungszeichen geschrieben: <uint:_16> "ff".
03 Physikalische Einheit
<float:64,k~g*m*s^-2> 1.0
SI-Basiseinheiten und abgeleitete Einheiten, IEC-Präfixe und zusammengesetzte Ausdrücke — validiert vom Parser, nicht von der lesenden Anwendung. Exponenten akzeptieren wahlweise eine Unicode-Hochzahl (s⁻²) oder ein reines ASCII-Caret (s^-2).
Sie tippen es von Hand? Beide Schreibweisen sind für den Parser gleichrangig — die Unicode-Hochzahlen (s⁻², ) und eine reine ASCII-Form, die ein Caret für den Exponenten und * anstelle des Trennzeichens · verwendet. k~g*m*s^-2 und k~g·m·s⁻² ergeben exakt dieselbe Einheit. Die ASCII-Form braucht keine exotischen Zeichen und ist damit die natürliche Wahl, wenn Sie .bvnr von Hand schreiben. Der Writer gibt standardmäßig Unicode-Hochzahlen aus, oder die Caret-Form, wenn Sie BVN_UNIT_ASCII_EXP setzen.

Das alles ist optional: Lassen Sie Annotationen weg und nutzen Sie klar definierte Standardwerte (uint:64, float:64), oder ergänzen Sie sie für die vollständige Parser-Validierung. Beide Modi sind eindeutig — und eine .bvnr-Datei bleibt UTF-8-Text, den Sie in jedem Editor lesen können, ganz ohne Toolchain.

Die Grammatik der Präzision.

Jede Zuweisung ist eine Deklaration: Die Annotation ist Teil des Wertes, keine Anmerkung dazu.

Typisierte Werte
Typ · Breite · Einheit
# Ganzzahlen .port = <uint:16> 443; .offset = <sint:64> -2147483648; .flags = <uint:32,_2> 11001010; # Gleitkommazahlen mit SI-Einheiten .temp = <float:32,°C> 36.6; .speed = <float:64,m/s> 9.81; .force = <float:64,k~g*m/s^2> 1.0; .buffer = <uint:64,Mi~B> 16;
Strukturen & Arrays
verschachtelt · mehrdimensional
# Verschachtelte Struktur .sensor = { .id = <uint:16> 7; .name = "pressure_01"; .val = <float:32,k~Pa> 101.325; }; # 3×3-Rotationsmatrix (Zeilen durch / getrennt) .rotation = [1.0, 0.0, 0.0]/ [0.0, 1.0, 0.0]/ [0.0, 0.0, 1.0];
<Familie:Breite,Einheit>
Typannotation
Acht Familien: uint sint float float_fix float_dec utf8 bool datetime (datetime ab Spec 1.1). Breite in Bit. Einheit in SI-Notation. Alles optional.
k~g*m*s^-2
Zusammengesetzte Einheiten
SI-Basiseinheiten und abgeleitete Einheiten, binäre IEC-Präfixe, zusammengesetzte Ausdrücke. Einheiten werden vom Parser validiert. Kein externes Schema nötig.
\x00 … \x00
Octet-Streams
Rohe Binärdaten ohne Base64-Overhead, gerahmt von Nullbytes. Text und Binärdaten stehen nebeneinander im selben Dokument.

Gebaut für Exaktheit.

Typsystem
Starke, optionale Typisierung
Acht Typfamilien. Die numerischen Familien nehmen eine explizite Bitbreite, und uint, sint und float zusätzlich eine Zahlenbasis. Standardwerte sind definiert, nicht geraten. Annotieren Sie, wo Präzision zählt; lassen Sie es weg, wo sie es nicht tut.
<uint:16> 443 <float:32,_16> "1.0p+0" <sint:64> -9223372036
SI-Einheiten
Physikalische Einheiten als First-Class-Konzept
Alle sieben SI-Basiseinheiten, 22 abgeleitete SI-Einheiten und binäre IEC-Präfixe. Zusammengesetzte Einheiten, Exponenten und Präfixprüfung sind Teil der Grammatik.
<float:64,k~g*m*s^-2> 1.0 <uint:64,Gi~B> 16 <float:32,m/s^2> 9.807
Streaming
Inkrementeller Reader im SAX-Stil
Parsen aus dem Speicher, von einem Dateideskriptor oder aus einem Socket über ein symmetrisches Callback-Paar on_unverified / on_verified. Während des Parsens selbst wird kein Heap-Speicher belegt.
bvnr_open_read_source( r, &src, NULL, &opts); bvnr_read(r);
Robustheit
Fehlerbehebung & Resync
Der optionale Resync-Modus überspringt beschädigte Zuweisungen und parst weiter. Er eignet sich für Log-Streams, unzuverlässige Übertragungswege und Dauertelemetrie.
opts.continue_on_error = true; /* Parser überspringt beschädigten Datensatz, macht mit dem nächsten weiter */
DOM-API
Baumbasierter Zugriff
Bauen Sie aus jedem Bovnar-Stream ein navigierbares DOM auf und durchlaufen, durchsuchen und verändern Sie es mit einer sauberen C-API. Oder nutzen Sie die Dict-artige Python-Schnittstelle.
bvn_dom_lookup(doc, ".sensor.val"); doc["sensor"]["val"] # Python
Python
Reines ctypes + NumPy-Brücke
Keine kompilierte Erweiterung. Kein Cython. Eine vollständige High-Level- und Streaming-API — dazu eine NumPy-Brücke ohne Zwischenschicht, die typisierte Arrays direkt in ein ndarray lädt, samt zugehöriger physikalischer Einheit.
bovnar.to_numpy(arr, return_unit=True) # → (ndarray float64, 'm/s²')

In Minuten startklar.

01
Aus dem Speicher lesen
Callbacks werden über bvnr_read_flags_t registriert. Beide Callbacks erhalten den Event-Typ und die geparsten Daten.
#include "bovnar.h" static bool on_event(void *ud, bvnr_event_t ev, bvnr_data_t *d) { if (ev == ev_data) printf("val=%.*s\n", (int)d->length, (const char *)d->data); return true; } int main(void) { const char *src = ".velocity = <float:64,m/s> 9.81;"; bvnr_read_flags_t opts = {0}; opts.on_verified = on_event; bvnr_reader_t *r = bvnr_reader_create(); bvnr_open_read_mem(r, src, strlen(src), NULL, 0, &opts); bvnr_read(r); bvnr_reader_destroy(r); }
02
Typisierte Werte schreiben
Die High-Level-Schreibhilfen nehmen einen Schlüssel als Zeichenkette und einen typisierten Wert direkt entgegen. Die Ausgabe wird mit den korrekten Annotationen erzeugt.
#include "bovnar.h" int main(void) { char buf[256]; bvnr_writer_t *w = bvnr_writer_create(); bvnr_sink_t sink; bvnr_sink_to_mem(&sink, (uint8_t *)buf, sizeof(buf)); bvnr_open_write_sink(w, &sink, true, NULL); bool ok; value_unit_t u = bvn_parse_unit( (const uint8_t *)"m/s", &ok); bvnr_write_float_unit( w, "velocity", 64, 9.81, u); bvnr_write_finish(w); uint64_t n = bvnr_writer_bytes_written(w); bvnr_writer_destroy(w); fwrite(buf, 1, (size_t)n, stdout); // → .velocity = <float:64,m/s> 9.81e+0; }
01
High-Level-API
Dict-artige loads / dumps-Schnittstelle. Reines ctypes — keine kompilierte Erweiterung nötig.
import bovnar data = { "sensor_id": 42, "temperature": 36.6, "unit": "celsius", } raw = bovnar.dumps(data) doc = bovnar.loads(raw) print(doc["sensor_id"]) # 42 print(doc["temperature"]) # 36.6
02
Streaming-Event-API
Volle Kontrolle über die Parse-Events. Prüfen Sie Einheiten, Typannotationen und rohe Textdarstellungen, sobald sie eintreffen.
from bovnar import Reader, Event, unit_to_str def on_event(ev, data): if ev == Event.DATA: u = data.value_unit if u.num_components: print(data.raw_str(), unit_to_str(u)) src = b".velocity = <float:64,m/s> 9.81;" Reader().read_mem( src, on_verified=on_event ) # → 9.81 m/s
03
Typisierte Round-Trips mit Quantity
loads(typed=True) verpackt jeden typisierten Wert in ein Quantity, das seinen exakten Text, seine Bitbreite und seine Einheit bewahrt. Geben Sie das Dict unverändert an dumps() zurück und Sie erhalten einen verlustfreien Round-Trip.
import bovnar src = b".pressure = <float:32,Pa> 101325.0;" doc = bovnar.loads(src, typed=True) q = doc["pressure"] # Quantity('101325.0', FLOAT [Pa]) print(q.raw) # '101325.0' print(q.unit_str()) # 'Pa' # dumps() gibt Annotation + Rohtext unverändert wieder aus out = bovnar.dumps(doc) assert bovnar.loads(out, typed=True) == doc
04
NumPy-Brücke
Typisierte Arrays landen direkt in einem numpy.ndarray — Bovnar-Breiten werden auf native dtypes abgebildet (float:32 → float32), und die Einheit des gesamten Arrays reist mit. NumPy ist ein optionales, verzögert geladenes Extra (pip install "bovnar[numpy]").
import bovnar, numpy as np src = b".accel = <float:64,m/s^2> " \ b"[9.81,0.02,-9.79]/[0.05,9.80,0.01];" doc = bovnar.loads(src, typed=True) a, unit = bovnar.to_numpy( doc["accel"], return_unit=True) # a.shape == (2, 3) a.dtype == float64 unit == 'm/s²' g = np.linalg.norm(a, axis=1) # vektorisiert, zeilenweise out = bovnar.array_to_bvnr("g_mag", g, unit=unit) # → .g_mag = <float:64,m/s²> [9.810…, 9.800…];
01
Die Bibliothek bauen
Erfordert CMake ≥ 3.21 und einen C99-konformen Compiler. Es gibt keine externen Abhängigkeiten, weder beim Bauen noch zur Laufzeit — nicht einmal libm.
# Klonen und bauen cmake -B build . cmake --build build # Release-Build cmake -B build \ -DCMAKE_BUILD_TYPE=Release . cmake --build build # Ergebnisse: # build/libbvnr.a # build/libbvnr.so # build/bovnar (CLI)
02
Linken & testen
Die Testsuite umfasst Unit-Tests, Round-Trip-Tests über Socket-Paare und Fuzzing-Harnesses. Benchmarks laufen über die CLI, mit bovnar bench.
# Ihre Anwendung linken gcc my_app.c \ -I include \ -L build \ -lbvnr \ -o my_app # Tests ausführen cd build && ctest \ --output-on-failure # Python-Tests export LIBBOVNAR_PATH=\ $(pwd)/build/libbvnr.so cd python && pytest tests -v

Wenn eine falsche Einheit ein Ausfall ist.

Greifen Sie zu Bovnar, wenn Einheiten mit den Daten reisen müssen und der Empfänger möglicherweise kein gemeinsames Schema kennt. Jede Messung ist konstruktionsbedingt einheitensicher — eine unpassende Einheit ist ein Parse-Fehler und kein stiller Bug, der erst im Produktivbetrieb auffällt.

Wissenschaftliche Messtechnik & Metrologie

Messwerte bleiben selbstbeschreibend, vom Labortisch bis zum veröffentlichten Datensatz — Breite, Basis und physikalische Einheit reisen bei jedem Wert mit.

Industrielle Telemetrie & Steuerung

Drücke, Durchflüsse und Temperaturen überschreiten Prozessgrenzen ohne gemeinsamen Vertrag, und Dimensionsfehler werden schon beim Parsen erkannt.

IoT-Sensornetze

Heterogene Geräte senden menschenlesbare, typgenaue Datensätze, die jeder Empfänger eigenständig validieren kann — ganz ohne zentrales Verzeichnis.

Langzeitarchivierung von Messdaten

Was heute geschrieben wird, bedeutet auch in Jahrzehnten noch exakt dasselbe — die Bedeutung steckt in der Datei, nicht in verlorengegangenen Werkzeugen.

Alles, was Sie zum Ausliefern brauchen.

Zehn Dokumente — vom fünfminütigen Tutorial bis zur formalen EBNF-Grammatik und zum Konformitäts­protokoll. Lesen Sie sie unten direkt auf der Seite, oder laden Sie den vollständigen Satz als PDF herunter.

00
Tutorial
Praxisnahe Einführung für Entwickler. In Minuten produktiv mit Bovnar.
Hier anfangen
01
Spezifikation (v1.1)
Vollständige lexikalische und syntaktische Grammatik, Typsystem, Arrays, Strukturen, Octet-Streams und Validierungsregeln.
Maßgeblich für das Format
02
Referenz zum Einheitensystem
SI-Basiseinheiten und abgeleitete Einheiten, binäre IEC-Präfixe, zusammengesetzte Ausdrücke, Exponenten und Präfixprüfung.
Physikalische Ebene
03
Lese- & Schreib-API
Vollständige C-API für Streaming-Reader und -Writer. Kommentierte Beispiele für jede typisierte Schreibhilfe.
C-Integration
04
Python-Anbindung
Reine ctypes-Schnittstelle — keine kompilierte Erweiterung. High-Level-loads/dumps sowie Reader/Writer für Streaming.
Python-Integration
05
Formale EBNF-Grammatik
Maschinenlesbare Grammatik. Die normative Referenz für alle, die Parser und Werkzeuge implementieren.
Parser-Referenz
06
FAQ
Häufige Fragen zum Format, zum Typsystem, zu Einheiten, zur C-API, zur Python-Anbindung und zu den Grenzen im Betrieb.
Häufige Fragen
07
Konformitäts-Testwerkzeug
319 Testfälle, IUT-Protokoll zur unabhängigen Überprüfung von Implementierungen, TAP-Ausgabe und CTest-Integration.
Überprüfung
08
Referenz zu Einheiten & Währungen
Alle 163 physikalischen Einheiten, 166 Fiat-Währungen und 50 Kryptowährungen. SI-/IEC-Präfixe, zusammengesetzte Ausdrücke und die Unterscheidung mehrdeutiger Symbole.
Kurzreferenz
09
Streaming, Framing & Multiplexing
Endlose Streams, Framing über mehrere Dokumente, Octet-Multiplexing und Dokument-im-Dokument — Anwendungen, die auf der Event-API aufsetzen, ohne Änderung am 1.0-Wire-Format.
Streaming-Ebene