Native engineering
A Sensor Protocol Core That Does Not Own the Socket
The native host owns connectivity. A shared C++ core turns bytes into validated events. That boundary made framing, timeouts and uncertain sensor state easier to reason about.
Senior AI Engineer · M.S. Computer Science, Tsinghua University · See the products I built
In this article · 6 sections
The engineering decisions
- Keep sockets, threads and timers outside the shared parser.
- Validate frame boundaries and coordinate data before producing app events.
- Clear stale state and distinguish synthetic parser tests from real-device compatibility.
Separate the transport from the protocol
A mobile sensor integration has at least two different problems: maintaining a connection on a phone, and interpreting the bytes arriving through it. I kept the shared protocol implementation in a pure C++17 library. It owns no socket, thread or timer. The host supplies byte sequences and the current time, and receives events.
Android integrates that boundary through NDK/JNI, while the iOS host uses Objective-C++ alongside Swift. The shared core does not need to pretend that those platforms have identical connection lifecycles. It only needs a precise contract for parsing and event assembly. Injecting the clock also makes timeout behavior reproducible in a desktop test.
A read is not a packet
The parser handles split frames, multiple frames in one incoming buffer, garbage before a frame and stream termination. It checks the declared size and checksum within a bounded payload model. A transport delivering some bytes does not establish that those bytes form a complete, valid message.
The shot assembler then associates related reports and uses injected-time deadlines to decide when an assembly cannot continue. Unknown encrypted report types are surfaced as non-fatal events instead of taking down the connection. That keeps protocol evolution separate from corruption: an unfamiliar message and a malformed known message are different situations.
Unknown is a real UI state
Ball-placement reports carry readiness, detected centers and a region polygon. The decoder validates counts, array lengths and finite coordinates before exposing the data. A malformed report invalidates the affected field. An unknown report cannot establish that the device is ready or that the ball is centered.
A no-ball report clears the last visible dot. Connection changes and a new shot clear positions too. Keeping an attractive but stale marker on screen would suggest knowledge the device no longer provides. Likewise, a connection without a region map needs an explicit waiting state rather than a guessed boundary.
| Observation | Required interpretation |
|---|---|
| Valid ball + region data | Show the measured sensor-relative position. |
| No ball detected | Remove the previous position. |
| Connection change or new shot | Discard stale placement state. |
| Missing region or unknown readiness | Show uncertainty, not an invented ready state. |
| Malformed coordinates | Invalidate the affected field. |
Use the measured plane, not an assumed world axis
The placement view projects ball centers and the actual region polygon onto the polygon’s own plane. It does not assume that XY or XZ must be the ground. The available protocol also does not define which world axis points toward the player, so the display is sensor-relative.
That constraint changes the language of the UI. An arrow may point toward the measured boundary, but it must not promise physical left or right, or a real-world distance that the source does not establish. A ball outside camera visibility has no knowable current location. The correct response is to remove the dot, not extrapolate an appealing trajectory.
Decoding is neither authentication nor unit conversion
The core hands raw numeric values to the platform boundary, where unit conversion happens once. That avoids a yard-to-meter conversion being applied in both the native layer and the UI. The code also distinguishes absent values from a numeric zero when the underlying measurement is unavailable.
The protocol’s encryption compatibility material is not authentication. Successfully decoding a packet is not a reason to trust it for account state or a leaderboard. The integration needs those trust boundaries even when the checksum and parser have accepted the frame.
Synthetic tests establish parser behavior
The desktop suite runs with address and undefined-behavior sanitizers when available. Its synthetic vectors can establish behavior for split frames, malformed lengths and deterministic timeouts. They cannot by themselves establish compatibility with every device or firmware.
Real-device acceptance needs captured inputs compared with the vendor tool’s displayed values. Region replay after a late connection, outside-region tracking and physical orientation require device-specific checks. The project also has separate September Android shot-decoding evidence. I keep that observation distinct from an assertion that every placement behavior or the iOS host has passed the same physical tests.
Project context and evidence
Written from my 2026 GolfJoy native protocol-core design and validation records. Public links below support the product and attribution context. Internal project records are summarized, with customer records, credentials, and employer source code omitted.
- GolfJoy Play on Google Play ↗
Official Android listing and the source of the sample interface image.
- Native app case-study section ↗
Public native product context and platform integration details.
- GolfJoy engineering case study ↗
Product interfaces and the company attribution for my five delivered projects.

