Java & Android

The shared library in the AMMC zip (ammc.dll, libammc.so, libammc.dylib) exports a JNI surface besides the plain C one, so Java can call it without any glue code of your own.

The bridge class

The native methods are bound by name, so the class must be com.skoky.AmmcBridge and the method names must match exactly:

package com.skoky;

public class AmmcBridge {

    static {
        System.loadLibrary("ammc");
    }

    /** Converts a hex-encoded P3 message into a JSON array of messages. */
    public static native String p3_to_json(String p3Hex);

    /** Decodes an auto-discovery response into "ip;decoder-id". */
    public static native String p3_network_to_json(String p3Hex);

    /** Encodes a JSON request into hex-encoded P3 bytes to send to the decoder. */
    public static native String encode(String json);

    /** Converts a P3 timestamp into milliseconds since the epoch. */
    public static native String time_to_millis(String rtcTime);

    /** Returns the library version. */
    public static native String version();
}

Using it

import com.skoky.AmmcBridge;

public class Demo {
    public static void main(String[] args) {
        System.out.println("AMMC " + AmmcBridge.version());

        String passing = "8e023300e5630000010001047a00000003041fd855000408589514394cd8"
                       + "040005026d0006025000080200008104501304008f";
        System.out.println(AmmcBridge.p3_to_json(passing));

        String discovery = "8e024200471a000016000804810a000a8104e5870a008f";
        System.out.println(AmmcBridge.p3_network_to_json(discovery));

        // bytes to send to the decoder to ask for its version
        String request = AmmcBridge.encode("{\"msg\":\"VERSION\"}");
    }
}

Put the library on the JVM's search path and run:

javac com/skoky/AmmcBridge.java Demo.java
java -Djava.library.path=apple_m Demo
AMMC 7.9.1
[{"decoder_id":"041350","gps_locked":false,"hits":80,"low_battery":false,"modified":false,"msg":"PASSING","passing_number":122,"resend":false,"rtc_time":"2013-03-19 21:36:33.607 +02:00","strength":109.0,"transponder":5625887}]
10.0.10.129;00:0A:87:E5

Notes

  • System.loadLibrary("ammc") resolves to ammc.dll on Windows and libammc.so / libammc.dylib elsewhere, so point java.library.path at the folder for your platform (windows64, linux_x86-64, apple_m). Use System.load("/full/path/to/libammc.dylib") to skip the search path.
  • p3_to_json always returns a JSON array, since one TCP read can carry several messages. It is empty for a message the library does not decode. Each call keeps nothing from the previous one, so a message split across two reads is lost by both. If you need every passing without gaps, run ammc-amb -w 9000 and read its WebSocket instead of parsing the socket yourself.
  • p3_network_to_json returns ip;decoder-id, not JSON, despite the name.
  • encode is the reverse direction: give it {"msg":"VERSION"}, {"msg":"NETWORKSETTINGS"} or {"msg":"PING"} and send the returned bytes to the decoder.
  • time_to_millis expects yyyy-MM-dd HH:mm:ss.SSSSSS without a timezone offset and returns 0 for anything else — so the rtc_time coming out of p3_to_json has to have its +02:00 suffix stripped first. In most cases parsing rtc_time with java.time.OffsetDateTime is simpler.
  • All methods are static and thread safe; sockets and reconnects stay in your application.

Android

The same class is used on Android, with the .so files under src/main/jniLibs/<abi>/. For a working application see our AMMC Demo timing app, which reads AMB and Vostok decoders with this library. Contact us for support on Android or another platform.