add GAD coding for Location Services

GAD, Universal Geographical Area Description:
- raw coding for all GAD elements.
- SI-units encoding and decoding for Ellipsoid point with uncertainty circle,
  which I presume is the typical "at most N meters away from cell tower located
  at X,Y", which corresponds to the TA positioning currently being implemented.
- other SI-units GAD element encodings are so far not implemented.

Add encoding and decoding tests.

In gsm/protocol/gsm_23_032.h are the raw coding structs as defined in 3GPP TS
23.032.

In gsm/gad.h are structs carrying consistent units based on meters and degrees,
for convenient / less error prone handling of GAD data, and for human readable
representations of the GAD data.

The separation of the two is desirable because OsmoBSC will receive GAD data
from OsmoSMLC on the Lb interface, and pass on this data to the MSC via the A
interface. It is better to pass the GAD data as-is without de/encoding.

Change-Id: I7a9dd805a91b1ebb6353bde0cd169218acbf223c
diff --git a/tests/gad/gad_test.c b/tests/gad/gad_test.c
new file mode 100644
index 0000000..ca8c4fa
--- /dev/null
+++ b/tests/gad/gad_test.c
@@ -0,0 +1,143 @@
+#include <stdio.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/gad.h>
+
+void test_gad_lat_lon_dec_enc_stability()
+{
+	uint32_t lat_enc;
+	uint32_t lon_enc;
+	printf("--- %s\n", __func__);
+	for (lat_enc = 0x0; lat_enc <= 0xffffff; lat_enc++) {
+		int32_t lat_dec = osmo_gad_dec_lat(lat_enc);
+		uint32_t enc2 = osmo_gad_enc_lat(lat_dec);
+		uint32_t want_enc = lat_enc;
+		/* "-0" == 0, because the highest bit is defined as a sign bit. */
+		if (lat_enc == 0x800000)
+			want_enc = 0;
+		if (enc2 != want_enc) {
+			printf("ERR: lat=%u --> %d --> %u\n", lat_enc, lat_dec, enc2);
+			printf("%d -> %u\n", lat_dec + 1, osmo_gad_enc_lat(lat_dec + 1));
+			OSMO_ASSERT(false);
+		}
+	}
+	printf("osmo_gad_dec_lat() -> osmo_gad_enc_lat() of %u values successful\n", lat_enc);
+	for (lon_enc = 0; lon_enc <= 0xffffff; lon_enc++) {
+		int32_t lon_dec = osmo_gad_dec_lon(lon_enc);
+		uint32_t enc2 = osmo_gad_enc_lon(lon_dec);
+		uint32_t want_enc = lon_enc;
+		if (enc2 != want_enc) {
+			printf("ERR: lon=%u 0x%x --> %d --> %u\n", lon_enc, lon_enc, lon_dec, enc2);
+			printf("%d -> %u\n", lon_dec + 1, osmo_gad_enc_lon(lon_dec + 1));
+			printf("%d -> %u\n", lon_dec - 1, osmo_gad_enc_lon(lon_dec - 1));
+			OSMO_ASSERT(false);
+		}
+	}
+	printf("osmo_gad_dec_lon() -> osmo_gad_enc_lon() of %u values successful\n", lon_enc);
+}
+
+struct osmo_gad gad_test_values[] = {
+	{
+		.type = GAD_TYPE_ELL_POINT_UNC_CIRCLE,
+		.ell_point_unc_circle = {
+			/* Values rounded to the nearest encodable value, for test result matching */
+			.lat = 23000006,
+			.lon = 42000002,
+			.unc = 442592,
+		},
+	},
+};
+
+void test_gad_enc_dec()
+{
+	int i;
+	printf("--- %s\n", __func__);
+
+	for (i = 0; i < ARRAY_SIZE(gad_test_values); i++) {
+		struct osmo_gad *t = &gad_test_values[i];
+		struct msgb *msg = msgb_alloc(1024, __func__);
+		union gad_raw raw_write;
+		union gad_raw raw_read;
+		struct osmo_gad dec_pdu;
+		int rc;
+		struct osmo_gad_err *err;
+		void *loop_ctx = msg;
+		rc = osmo_gad_enc(&raw_write, t);
+		if (rc <= 0) {
+			printf("[%d] %s: ERROR: osmo_gad_enc() failed\n", i, osmo_gad_type_name(t->type));
+			goto loop_end;
+		}
+		rc = osmo_gad_raw_write(msg, &raw_write);
+		if (rc <= 0) {
+			printf("[%d] %s: ERROR: osmo_gad_raw_write() failed\n", i, osmo_gad_type_name(t->type));
+			goto loop_end;
+		}
+		if (rc != msg->len) {
+			printf("[%d] %s: ERROR: osmo_gad_raw_write() returned length %d but msgb has %d bytes\n",
+			       i, osmo_gad_type_name(t->type),
+			       rc, msg->len);
+			goto loop_end;
+		}
+
+		memset(&raw_read, 0xff, sizeof(raw_read));
+		rc = osmo_gad_raw_read(&raw_read, &err, loop_ctx, msg->data, msg->len);
+		if (rc) {
+			printf("[%d] ERROR: osmo_gad_raw_read() failed: %s\n", i, err->logmsg);
+			printf("    encoded data: %s\n", osmo_hexdump(msg->data, msg->len));
+			goto loop_end;
+		}
+
+		memset(&dec_pdu, 0xff, sizeof(dec_pdu));
+		rc = osmo_gad_dec(&dec_pdu, &err, loop_ctx, &raw_read);
+		if (rc) {
+			printf("[%d] ERROR: failed to decode pdu: %s\n", i, err->logmsg);
+			printf("    encoded data: %s\n", osmo_hexdump(msg->data, msg->len));
+			goto loop_end;
+		}
+
+		if (memcmp(t, &dec_pdu, sizeof(dec_pdu))) {
+			char strbuf[128];
+			printf("[%d] %s: ERROR: decoded PDU != encoded PDU\n", i,
+			       osmo_gad_type_name(t->type));
+			osmo_gad_to_str_buf(strbuf, sizeof(strbuf), t);
+			printf("     original struct: %s\n", strbuf);
+			osmo_gad_to_str_buf(strbuf, sizeof(strbuf), &dec_pdu);
+			printf("      decoded struct: %s\n", strbuf);
+			goto loop_end;
+		}
+
+		printf("[%d] %s: ok\n", i, osmo_gad_type_name(t->type));
+		printf("    encoded data: %s\n", msgb_hexdump(msg));
+
+loop_end:
+		msgb_free(msg);
+	}
+}
+
+void test_gad_to_str()
+{
+	int i;
+	printf("--- %s\n", __func__);
+
+	for (i = 0; i < ARRAY_SIZE(gad_test_values); i++) {
+		struct osmo_gad *t = &gad_test_values[i];
+		char buf[1024];
+		int rc;
+		rc = osmo_gad_to_str_buf(buf, sizeof(buf), t);
+
+		printf("[%d] ", i);
+		if (rc <= 0)
+			printf("%s: ERROR: osmo_gad_to_str_buf() failed\n", osmo_gad_type_name(t->type));
+		else
+			printf("%s\n", buf);
+	}
+}
+
+int main()
+{
+	test_gad_lat_lon_dec_enc_stability();
+	test_gad_enc_dec();
+	test_gad_to_str();
+	return 0;
+}