aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2021-01-10 09:48:54 +0100
committerAndreas Eversberg <jolly@eversberg.eu>2021-06-06 11:50:35 +0200
commit8d4d48aa0841551c6e292d5d55293ec908b839ed (patch)
tree7308ef95d84b31c03546d0c7f4efa3183b83d9d7
parentef88fea8a0a6c91e181977aafda01825285c6afc (diff)
Add MPT1327 / Regionet43 (Buendelfunk) network
-rw-r--r--.gitignore1
-rw-r--r--README3
-rw-r--r--configure.ac1
-rw-r--r--docs/index.html1
-rw-r--r--docs/mpt1327.html39
-rw-r--r--src/Makefile.am1
-rw-r--r--src/jolly/Makefile.am1
-rwxr-xr-xsrc/libdebug/debug.c1
-rw-r--r--src/mpt1327/Makefile.am47
-rw-r--r--src/mpt1327/dsp.c349
-rw-r--r--src/mpt1327/dsp.h7
-rw-r--r--src/mpt1327/main.c398
-rw-r--r--src/mpt1327/message.c566
-rw-r--r--src/mpt1327/message.h227
-rwxr-xr-xsrc/mpt1327/mpt1327.c1661
-rwxr-xr-xsrc/mpt1327/mpt1327.h155
16 files changed, 3457 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 14d9342..d7a0d68 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,6 +66,7 @@ src/jtacs/jtacs
src/r2000/radiocom2000
src/imts/imts
src/imts/imts-dialer
+src/mpt1327/mpt1327
src/jolly/jollycom
src/eurosignal/eurosignal
src/tv/osmotv
diff --git a/README b/README
index 66777d1..dca1cee 100644
--- a/README
+++ b/README
@@ -14,6 +14,7 @@ generated simultaniously using SDR. Currently supported networks:
* JTACS (Japanese version of TACS)
* Radiocom 2000 (French network)
* IMTS / MTS ((Improved) Mobile Telephone Service)
+ * MPT1327 (Trunked Radio) aka known as 'Buendelfunk'
* Eurosignal (ERuRD paging service)
* JollyCom (Unofficial network, invented by the author)
* C-Netz BSC (Connecting to a C-Netz Base Station)
@@ -23,6 +24,7 @@ Additionally the following communication services are implemented:
* TV Transmitter with test Images
* Radio transmitter / receiver
* Analog Modem Emulation (AM7911)
+ * German classic 'Zeitansage' (time announcement)
USE AT YOUR OWN RISK!
@@ -68,3 +70,4 @@ which seems not to exist anymore...
Peter, Peter and Friedhelm and Stephan for providing documentation and hardware
for C-Netz Base Station and other C-Netz documents.
+Carsten Wollesen for donating MPT1327 radios and programming tools.
diff --git a/configure.ac b/configure.ac
index 239f453..65ccdc2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -97,6 +97,7 @@ AC_OUTPUT(
src/jtacs/Makefile
src/r2000/Makefile
src/imts/Makefile
+ src/mpt1327/Makefile
src/jolly/Makefile
src/eurosignal/Makefile
src/tv/Makefile
diff --git a/docs/index.html b/docs/index.html
index 0f618d5..44ce6ae 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -108,6 +108,7 @@ Implemented networks:
<li><a href="amps.html">AMPS - Advanced Mobile Phone Service</a> (USA)</li>
<li><a href="tacs.html">TACS / JTACS - Total Access Communication System</a> (UK/Italy/Japan)</li>
<li><a href="radiocom2000.html">Radiocom 2000</a> (France)</li>
+ <li><a href="mpt1327.html">MPT1327/Regionet43 (B&uuml;ndelfunk)</a> (Europe)</li>
<li><a href="eurosignal.html">Eurosignal</a> (Europe)</li>
</ul>
</td></tr></table></center>
diff --git a/docs/mpt1327.html b/docs/mpt1327.html
new file mode 100644
index 0000000..bfdd6cf
--- /dev/null
+++ b/docs/mpt1327.html
@@ -0,0 +1,39 @@
+<html>
+<head>
+<link href="style.css" rel="stylesheet" type="text/css" />
+<title>osmocom-analog</title>
+</head>
+<body>
+<center><table><tr><td>
+
+<h2><center>MPT1327</center></h2>
+
+<center><!--img src="radiocom2000.jpg"/--></center>
+
+<center><h1>*this doc is under construction*</h1></center>
+
+<ul>
+ <li><a href="#history">History</a>
+ <li><a href="#howitworks">How it works</a>
+ <li><a href="#basestation">Setup of a base station</a>
+</ul>
+
+<p class="toppic">
+<a name="history"></a>
+History
+</p>
+
+<p class="toppic">
+<a name="howitworks"></a>
+How it works
+</p>
+
+<p class="toppic">
+<a name="basestation"></a>
+Setup of a base station
+</p>
+
+<hr><center>[<a href="index.html">Back to main page</a>]</center><hr>
+</td></tr></table></center>
+</body>
+</html>
diff --git a/src/Makefile.am b/src/Makefile.am
index 3bd7136..344ffd7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -51,6 +51,7 @@ SUBDIRS += \
jtacs \
r2000 \
imts \
+ mpt1327 \
jolly \
eurosignal \
tv \
diff --git a/src/jolly/Makefile.am b/src/jolly/Makefile.am
index 346da7e..89ba1a5 100644
--- a/src/jolly/Makefile.am
+++ b/src/jolly/Makefile.am
@@ -16,7 +16,6 @@ jollycom_LDADD = \
$(top_builddir)/src/libmobile/libmobile.a \
$(top_builddir)/src/libosmocc/libosmocc.a \
$(top_builddir)/src/libdisplay/libdisplay.a \
- $(top_builddir)/src/libgoertzel/libgoertzel.a \
$(top_builddir)/src/libjitter/libjitter.a \
$(top_builddir)/src/libsquelch/libsquelch.a \
$(top_builddir)/src/libdtmf/libdtmf.a \
diff --git a/src/libdebug/debug.c b/src/libdebug/debug.c
index 07f7b12..44902ea 100755
--- a/src/libdebug/debug.c
+++ b/src/libdebug/debug.c
@@ -50,6 +50,7 @@ struct debug_cat {
{ "amps", "\033[1;34m" },
{ "r2000", "\033[1;34m" },
{ "imts", "\033[1;34m" },
+ { "mpt1327", "\033[1;34m" },
{ "jollycom", "\033[1;34m" },
{ "eurosignal", "\033[1;34m" },
{ "frame", "\033[0;36m" },
diff --git a/src/mpt1327/Makefile.am b/src/mpt1327/Makefile.am
new file mode 100644
index 0000000..ed0341e
--- /dev/null
+++ b/src/mpt1327/Makefile.am
@@ -0,0 +1,47 @@
+AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
+
+bin_PROGRAMS = \
+ mpt1327
+
+mpt1327_SOURCES = \
+ mpt1327.c \
+ dsp.c \
+ message.c \
+ main.c
+mpt1327_LDADD = \
+ $(COMMON_LA) \
+ ../anetz/libgermanton.a \
+ $(top_builddir)/src/liboptions/liboptions.a \
+ $(top_builddir)/src/libdebug/libdebug.a \
+ $(top_builddir)/src/libmobile/libmobile.a \
+ $(top_builddir)/src/libosmocc/libosmocc.a \
+ $(top_builddir)/src/libdisplay/libdisplay.a \
+ $(top_builddir)/src/libjitter/libjitter.a \
+ $(top_builddir)/src/libsquelch/libsquelch.a \
+ $(top_builddir)/src/libdtmf/libdtmf.a \
+ $(top_builddir)/src/libtimer/libtimer.a \
+ $(top_builddir)/src/libsamplerate/libsamplerate.a \
+ $(top_builddir)/src/libemphasis/libemphasis.a \
+ $(top_builddir)/src/libfsk/libfsk.a \
+ $(top_builddir)/src/libfm/libfm.a \
+ $(top_builddir)/src/libfilter/libfilter.a \
+ $(top_builddir)/src/libwave/libwave.a \
+ $(top_builddir)/src/libsample/libsample.a \
+ $(top_builddir)/src/libg711/libg711.a \
+ -lm
+
+if HAVE_ALSA
+mpt1327_LDADD += \
+ $(top_builddir)/src/libsound/libsound.a \
+ $(ALSA_LIBS)
+endif
+
+if HAVE_SDR
+mpt1327_LDADD += \
+ $(top_builddir)/src/libsdr/libsdr.a \
+ $(top_builddir)/src/libam/libam.a \
+ $(top_builddir)/src/libfft/libfft.a \
+ $(UHD_LIBS) \
+ $(SOAPY_LIBS)
+endif
+
diff --git a/src/mpt1327/dsp.c b/src/mpt1327/dsp.c
new file mode 100644
index 0000000..4b6b7dc
--- /dev/null
+++ b/src/mpt1327/dsp.c
@@ -0,0 +1,349 @@
+/* audio processing
+ *
+ * (C) 2021 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define CHAN mpt1327->sender.kanal
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include "../libsample/sample.h"
+#include "../libmobile/call.h"
+#include "../libdebug/debug.h"
+#include "../libtimer/timer.h"
+#include "mpt1327.h"
+#include "dsp.h"
+#include "message.h"
+
+#define PI M_PI
+
+/* signaling */
+#define MAX_DEVIATION 2500.0
+#define MAX_MODULATION 2550.0
+#define SPEECH_DEVIATION 1500.0 /* deviation of speech (no emphasis) */
+#define TX_PEAK_FSK (1500.0 / SPEECH_DEVIATION)
+#define BIT_RATE 1200.0
+#define BIT_ADJUST 0.1 /* how much do we adjust bit clock on frequency change */
+#define F0 1800.0
+#define F1 1200.0
+#define MAX_DISPLAY 1.4 /* something above speech level */
+
+/* carrier loss detection */
+#define MUTE_TIME 0.1 /* time to mute after loosing signal */
+
+void dsp_init(void)
+{
+}
+
+static int fsk_send_bit(void *inst);
+static void fsk_receive_bit(void *inst, int bit, double quality, double level);
+
+/* Init FSK of transceiver */
+int dsp_init_sender(mpt1327_t *mpt1327, double squelch_db)
+{
+ int rc;
+
+ PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Init DSP for Transceiver.\n");
+
+ /* init squelch */
+ squelch_init(&mpt1327->squelch, mpt1327->sender.kanal, squelch_db, MUTE_TIME, MUTE_TIME);
+
+ /* set modulation parameters */
+ sender_set_fm(&mpt1327->sender, MAX_DEVIATION, MAX_MODULATION, SPEECH_DEVIATION, MAX_DISPLAY);
+
+ PDEBUG(DDSP, DEBUG_DEBUG, "Using FSK level of %.3f (%.3f KHz deviation)\n", TX_PEAK_FSK, SPEECH_DEVIATION * TX_PEAK_FSK / 1e3);
+
+ /* init fsk */
+ if (fsk_mod_init(&mpt1327->fsk_mod, mpt1327, fsk_send_bit, mpt1327->sender.samplerate, BIT_RATE, F0, F1, TX_PEAK_FSK, 1, 0) < 0) {
+ PDEBUG_CHAN(DDSP, DEBUG_ERROR, "FSK init failed!\n");
+ return -EINVAL;
+ }
+ if (fsk_demod_init(&mpt1327->fsk_demod, mpt1327, fsk_receive_bit, mpt1327->sender.samplerate, BIT_RATE, F0, F1, BIT_ADJUST) < 0) {
+ PDEBUG_CHAN(DDSP, DEBUG_ERROR, "FSK init failed!\n");
+ return -EINVAL;
+ }
+
+ mpt1327->dmp_frame_level = display_measurements_add(&mpt1327->sender.dispmeas, "Frame Level", "%.1f %% (last)", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 150.0, 100.0);
+ mpt1327->dmp_frame_quality = display_measurements_add(&mpt1327->sender.dispmeas, "Frame Quality", "%.1f %% (last)", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 100.0, 100.0);
+
+ /* repeater */
+ rc = jitter_create(&mpt1327->repeater_dejitter, mpt1327->sender.samplerate / 5);
+ if (rc < 0) {
+ PDEBUG(DDSP, DEBUG_ERROR, "Failed to create and init repeater buffer!\n");
+ goto error;
+ }
+ return 0;
+
+error:
+ dsp_cleanup_sender(mpt1327);
+ return rc;
+}
+
+/* Cleanup transceiver instance. */
+void dsp_cleanup_sender(mpt1327_t *mpt1327)
+{
+ PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Cleanup DSP for Transceiver.\n");
+
+ fsk_mod_cleanup(&mpt1327->fsk_mod);
+ fsk_demod_cleanup(&mpt1327->fsk_demod);
+
+ jitter_destroy(&mpt1327->repeater_dejitter);
+}
+
+/* Check for SYNC bits, then collect data bits */
+static void fsk_receive_bit(void *inst, int bit, double quality, double level)
+{
+ mpt1327_t *mpt1327 = (mpt1327_t *)inst;
+ int i;
+
+ /* normalize FSK level */
+ level /= TX_PEAK_FSK;
+
+// printf("bit=%d quality=%.4f\n", bit, quality);
+ if (!mpt1327->rx_in_sync) {
+ mpt1327->rx_sync = (mpt1327->rx_sync << 1) | bit;
+
+ /* level and quality */
+ mpt1327->rx_level[mpt1327->rx_count & 0xff] = level;
+ mpt1327->rx_quality[mpt1327->rx_count & 0xff] = quality;
+ mpt1327->rx_count++;
+
+ /* check if sync pattern match */
+ if (mpt1327->rx_sync != mpt1327->sync_word)
+ return;
+
+ /* average level and quality */
+ level = quality = 0;
+ for (i = 0; i < 16; i++) {
+ level += mpt1327->rx_level[(mpt1327->rx_count - 1 - i) & 0xff];
+ quality += mpt1327->rx_quality[(mpt1327->rx_count - 1 - i) & 0xff];
+ }
+ level /= 16.0; quality /= 16.0;
+// printf("sync (level = %.2f, quality = %.2f\n", level, quality);
+
+ /* do not accept garbage */
+ if (quality < 0.65)
+ return;
+
+ /* rest sync register */
+ mpt1327->rx_sync = 0;
+ mpt1327->rx_in_sync = 1;
+ mpt1327->rx_count = 0;
+
+ /* mute audio from now on */
+ mpt1327->rx_mute = 1;
+
+ return;
+ }
+
+ /* read bits */
+ mpt1327->rx_bits = (mpt1327->rx_bits << 1) | (bit & 1);
+ mpt1327->rx_level[mpt1327->rx_count] = level;
+ mpt1327->rx_quality[mpt1327->rx_count] = quality;
+ if (++mpt1327->rx_count != 64)
+ return;
+
+ /* check parity */
+ if (mpt1327_checkbits(mpt1327->rx_bits, NULL) != (mpt1327->rx_bits & 0xffff)) {
+ PDEBUG(DDSP, DEBUG_NOTICE, "Received corrupt codeword or noise.\n");
+ mpt1327->rx_in_sync = 0;
+ mpt1327->rx_mute = 0;
+ return;
+ }
+
+ /* reset counter for next frame */
+ mpt1327->rx_count = 0;
+
+ /* average level and quality */
+ level = quality = 0;
+ for (i = 0; i < 64; i++) {
+ level += mpt1327->rx_level[i];
+ quality += mpt1327->rx_quality[i];
+ }
+ level /= 64.0; quality /= 64.0;
+
+ /* update measurements */
+ display_measurements_update(mpt1327->dmp_frame_level, level * 100.0, 0.0);
+ display_measurements_update(mpt1327->dmp_frame_quality, quality * 100.0, 0.0);
+
+ /* convert level so that received level at TX_PEAK_FSK results in 1.0 (100%) */
+ mpt1327_receive_codeword(mpt1327, mpt1327->rx_bits, quality, level);
+}
+
+/* Process received audio stream from radio unit. */
+void sender_receive(sender_t *sender, sample_t *samples, int length, double __attribute__((unused)) rf_level_db)
+{
+ mpt1327_t *mpt1327 = (mpt1327_t *) sender;
+ sample_t *spl;
+ int pos;
+ int i;
+ int was_mute = mpt1327->rx_mute; /* remember, so always mute whole chunk */
+ int was_pressel_on = mpt1327->pressel_on;
+
+ /* if channel is off, do nothing */
+ if (mpt1327->dsp_mode == DSP_MODE_OFF) {
+ /* measure squelch even if channel is turned off */
+ if (!isinf(mpt1327->squelch.threshold_db))
+ squelch(&mpt1327->squelch, rf_level_db, (double)length / (double)mpt1327->sender.samplerate);
+ return;
+ }
+
+ /* fsk signal */
+ fsk_demod_receive(&mpt1327->fsk_demod, samples, length);
+
+ /* on traffic channel mute and indicate signal strength */
+ if (mpt1327->dsp_mode == DSP_MODE_TRAFFIC) {
+ /* process signal mute/loss, also for signalling tone */
+ if (!isinf(mpt1327->squelch.threshold_db)) {
+ /* use squelch to unmute and reset call timer */
+ switch (squelch(&mpt1327->squelch, rf_level_db, (double)length / (double)mpt1327->sender.samplerate)) {
+ case SQUELCH_LOSS:
+ case SQUELCH_MUTE:
+ memset(samples, 0, sizeof(*samples) * length);
+ break;
+ default:
+ mpt1327_signal_indication(mpt1327);
+ }
+ } else {
+ /* muting audio while pressel is off */
+ if (!was_pressel_on || !mpt1327->pressel_on)
+ memset(samples, 0, sizeof(*samples) * length);
+ }
+ /* muting audio while receiving frame */
+ if (was_mute || mpt1327->rx_mute)
+ memset(samples, 0, sizeof(*samples) * length);
+ }
+
+ if (mpt1327->dsp_mode == DSP_MODE_TRAFFIC) {
+ /* if repeater mode, store sample in jitter buffer */
+ if (mpt1327->repeater)
+ jitter_save(&mpt1327->repeater_dejitter, samples, length);
+
+ if (mpt1327->unit && mpt1327->unit->callref) {
+ int count;
+
+ count = samplerate_downsample(&mpt1327->sender.srstate, samples, length);
+ spl = mpt1327->sender.rxbuf;
+ pos = mpt1327->sender.rxbuf_pos;
+ for (i = 0; i < count; i++) {
+ spl[pos++] = samples[i];
+ if (pos == 160) {
+ call_up_audio(mpt1327->unit->callref, spl, 160);
+ pos = 0;
+ }
+ }
+ mpt1327->sender.rxbuf_pos = pos;
+ } else
+ mpt1327->sender.rxbuf_pos = 0;
+ } else
+ mpt1327->sender.rxbuf_pos = 0;
+}
+
+static int fsk_send_bit(void *inst)
+{
+ mpt1327_t *mpt1327 = (mpt1327_t *)inst;
+
+ /* send frame bit (prio) */
+ if (!mpt1327->tx_bit_num || mpt1327->tx_count == mpt1327->tx_bit_num) {
+ /* request frame */
+ mpt1327->tx_bit_num = mpt1327_send_codeword(mpt1327, &mpt1327->tx_bits);
+ if (mpt1327->tx_bit_num == 0) {
+ return -1;
+ }
+ mpt1327->tx_count = 0;
+ }
+ return (mpt1327->tx_bits >> (63 - mpt1327->tx_count++)) & 1;
+
+ return -1;
+}
+
+/* Provide stream of audio toward radio unit */
+void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
+{
+ mpt1327_t *mpt1327 = (mpt1327_t *) sender;
+
+ if (mpt1327->dsp_mode == DSP_MODE_OFF) {
+ memset(power, 0, length);
+ memset(samples, 0, sizeof(*samples) * length);
+ return;
+ }
+
+ memset(power, 1, length);
+
+ if (mpt1327->dsp_mode == DSP_MODE_TRAFFIC) {
+ jitter_load(&mpt1327->sender.dejitter, samples, length);
+ /* if repeater mode, sum samples from jitter buffer to samples */
+ if (mpt1327->repeater) {
+ sample_t uplink[length];
+ int i;
+ jitter_load(&mpt1327->repeater_dejitter, uplink, length);
+ for (i = 0; i < length; i++)
+ samples[i] += uplink[i];
+ }
+ } else
+ memset(samples, 0, sizeof(*samples) * length);
+
+ /* If there is something to modulate (pending TX frame),
+ * overwrite audio with FSK audio. */
+ fsk_mod_send(&mpt1327->fsk_mod, samples, length, 0);
+}
+
+const char *mpt1327_dsp_mode_name(enum dsp_mode mode)
+{
+ static char invalid[16];
+
+ switch (mode) {
+ case DSP_MODE_OFF:
+ return "OFF";
+ case DSP_MODE_TRAFFIC:
+ return "TRAFFIC";
+ case DSP_MODE_CONTROL:
+ return "CONTROL";
+ }
+
+ sprintf(invalid, "invalid(%d)", mode);
+ return invalid;
+}
+
+void mpt1327_set_dsp_mode(mpt1327_t *mpt1327, enum dsp_mode mode, int repeater)
+{
+ //NOTE: DO NOT RESET FRAME, because mode may change before frame has been sent!
+
+ if (mode == DSP_MODE_CONTROL)
+ mpt1327->sync_word = 0xc4d7;
+ if (mode == DSP_MODE_TRAFFIC)
+ mpt1327->sync_word = 0x3b28;
+
+ if (repeater)
+ jitter_reset(&mpt1327->repeater_dejitter);
+ mpt1327->repeater = repeater;
+
+ PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "DSP mode %s -> %s\n", mpt1327_dsp_mode_name(mpt1327->dsp_mode), mpt1327_dsp_mode_name(mode));
+ mpt1327->dsp_mode = mode;
+}
+
+void mpt1327_reset_sync(mpt1327_t *mpt1327)
+{
+ mpt1327->rx_in_sync = 0;
+ mpt1327->rx_sync = 0;
+ mpt1327->rx_mute = 0;
+}
+
diff --git a/src/mpt1327/dsp.h b/src/mpt1327/dsp.h
new file mode 100644
index 0000000..5352958
--- /dev/null
+++ b/src/mpt1327/dsp.h
@@ -0,0 +1,7 @@
+
+void dsp_init(void);
+int dsp_init_sender(mpt1327_t *mpt1327, double squelch_db);
+void dsp_cleanup_sender(mpt1327_t *mpt1327);
+void mpt1327_set_dsp_mode(mpt1327_t *mpt1327, enum dsp_mode mode, int repeater);
+void mpt1327_reset_sync(mpt1327_t *mpt1327);;
+
diff --git a/src/mpt1327/main.c b/src/mpt1327/main.c
new file mode 100644
index 0000000..bd90f4d
--- /dev/null
+++ b/src/mpt1327/main.c
@@ -0,0 +1,398 @@
+/* MPT1327 main
+ *
+ * (C) 2021 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "../libsample/sample.h"
+#include "../libmobile/main_mobile.h"
+#include "../libdebug/debug.h"
+#include "../libtimer/timer.h"
+#include "../anetz/freiton.h"
+#include "../anetz/besetztton.h"
+#include "../liboptions/options.h"
+#include "mpt1327.h"
+#include "dsp.h"
+#include "message.h"
+
+/* settings */
+int num_freq = 0;
+static int num_chan_type = 0;
+static double squelch_db = -INFINITY;
+static enum mpt1327_band band = BAND_REGIONET43_SUB1;
+static enum mpt1327_chan_type chan_type[MAX_SENDER] = { CHAN_TYPE_CC_TC };
+static int16_t sys = -1;
+static int wt = 10;
+static int per = 5;
+static int pon = 1;
+static int timeout = 30;
+
+void print_image(void) {}
+
+void print_help(const char *arg0)
+{
+ main_mobile_print_help(arg0, "-O ... | -I ... ");
+ /* - - */
+ printf(" -B --band <name> | list\n");
+ printf(" Select frequency Band (default = '%s')\n", mpt1327_band_name(band));
+ printf(" -T --channel-type <channel type> | list\n");
+ printf(" Give channel type, use 'list' to get a list. (default = '%s')\n", chan_type_short_name(chan_type[0]));
+ printf(" -O --operator <OPID> <NDD> <LAB>\n");
+ printf(" -> decimal, '0x' for hex or all binary digits\n");
+ printf(" Give System Identity Code of regional network (1st bit = 0)\n");
+ printf(" OPID: Operator Identity (7 binary digits)\n");
+ printf(" -> Check subscription data of mobile unit\n");
+ printf(" NDD: Network Dependent Data (4 binary digts)\n");
+ printf(" -> Check subscription data of mobile unit (must be '0001' or greater)\n");
+ printf(" -> Change it to force re-registering of mobile unit.\n");
+ printf(" LAB: Label for multiple control channels (3 binary digits)\n");
+ printf(" -> Use '001' to allow all categories\n");
+ printf(" -N --net <NET> <NDD> <LAB>\n");
+ printf(" -> decimal, '0x' for hex or all binary digits\n");
+ printf(" Give System Identity Code of national network (1st bit = 1)\n");
+ printf(" NET: Network Identity (2 binary digits)\n");
+ printf(" -> Check subscription data of mobile unit (must be '000000001' or greater)\n");
+ printf(" -> Change it to force re-registering of mobile unit.\n");
+ printf(" NDD: Network Dependent Data (9 binary digts)\n");
+ printf(" LAB: Label for multiple control channels (3 binary digits)\n");
+ printf(" -> Use '001' to allow all categories\n");
+ printf(" -S --sysdef wt=5 | wt=10 | wt=15\n");
+ printf(" Number of slots the Radio Unit waits for response. A slot lasts about\n");
+ printf(" 107 ms. (default = %d)\n", wt);
+ printf(" -S --sysdef per=<secs> | per=0\n");
+ printf(" Interval of periodic messages from the Radio Unit while transmitting\n");
+ printf(" speech. Use 1..31 to enable and 0 to disable. Also the 'timeout' value\n");
+ printf(" must be greater than value given here. (default = %d)\n", per);
+ printf(" -S --sysdef pon=1 | pon=0\n");
+ printf(" The Radio Unit must send 'Pressel On' message to unmute the uplink.\n");
+ printf(" If disabled, squelch must be enabled. (default = %d)\n", pon);
+ printf(" -S --sysdef timeout=<secs> | timeout=off\n");
+ printf(" The Traffic Channel is released, if no radio transmits for given amount of time.\n");
+ printf(" (default = %d)\n", timeout);
+ printf(" -Q --squelch <dB> | auto\n");
+ printf(" Use given RF level to detect transmission on Traffic Channel, if\n");
+ printf(" 'Pressel On' is disabled.\n");
+ printf(" and stays below this level, the connection is released.\n");
+ printf(" Use 'auto' to do automatic noise floor calibration to detect loss.\n");
+ printf(" Only works with SDR! (disabled by default)\n");
+
+ printf("\nstation-id: Give 7 digits of Radio Unit's prefix/ident, you don't need to\n");
+ printf(" enter it for every start of this program.\n");
+ main_mobile_print_hotkeys();
+ printf("Press 'i' key to dump list of seen Radio Units.\n");
+}
+
+static void add_options(void)
+{
+ main_mobile_add_options();
+ option_add('B', "band", 1);
+ option_add('T', "channel-type", 1);
+ option_add('O', "operator", 3);
+ option_add('N', "net", 3);
+ option_add('S', "sysdef", 1);
+ option_add('Q', "squelch", 1);
+}
+
+static int read_sys(const char *param, const char *value, int digits)
+{
+ int result = 0;
+ int i;
+
+ if ((int)strlen(value) < digits) {
+ result = strtoul(value, NULL, 0);
+ if (result >= (1 << digits)) {
+ fprintf(stderr, "Given '%s' value is out of range for %d binary digits, use '-h' for help!\n", param, digits);
+ return -EINVAL;
+ }
+ return result;
+ }
+
+ if ((int)strlen(value) > digits) {
+ fprintf(stderr, "Given '%s' value must have exactly %d binary digits, use '-h' for help!\n", param, digits);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < (int)strlen(value); i++) {
+ if (value[i] < '0' || value[i] > '1') {
+ fprintf(stderr, "Given '%s' value must only have binary digits of '0' or '1', use '-h' for help!\n", param);
+ return -EINVAL;
+ }
+ result = (result << 1) | (value[i] - '0');
+ }
+
+ return result;
+}
+
+static int handle_options(int short_option, int argi, char **argv)
+{
+ int rc;
+ const char *p;
+
+ switch (short_option) {
+ case 'B':
+ if (!strcmp(argv[argi], "list")) {
+ mpt1327_band_list();
+ return 0;
+ }
+ rc = mpt1327_band_by_short_name(argv[argi]);
+ if (rc < 0) {
+ fprintf(stderr, "Given band '%s' is illegal, use '-h' for help!\n", argv[argi]);
+ return -EINVAL;
+ }
+ band = rc;
+ break;
+ case 'T':
+ if (!strcmp(argv[argi], "list")) {
+ mpt1327_channel_list();
+ return 0;
+ }
+ rc = mpt1327_channel_by_short_name(argv[argi]);
+ if (rc < 0) {
+ fprintf(stderr, "Error, channel type '%s' unknown. Please use '-t list' to get a list. I suggest to use the default.\n", argv[argi]);
+ return -EINVAL;
+ }
+ OPT_ARRAY(num_chan_type, chan_type, rc)
+ break;
+ case 'O':
+ sys = 0x0000;
+ rc = read_sys("OID", argv[argi + 0], 7);
+ if (rc < 0)
+ return rc;
+ sys = sys | (rc << 7);
+ rc = read_sys("NDD", argv[argi + 1], 4);
+ if (rc < 0)
+ return rc;
+ sys = sys | (rc << 3);
+ rc = read_sys("LAB", argv[argi + 2], 3);
+ if (rc < 0)
+ return rc;
+ sys = sys | rc;
+ break;
+ case 'N':
+ sys = 0x4000;
+ rc = read_sys("NET", argv[argi + 0], 2);
+ if (rc < 0)
+ return rc;
+ sys = sys | (rc << 12);
+ rc = read_sys("NDD", argv[argi + 1], 9);
+ if (rc < 0)
+ return rc;
+ sys = sys | (rc << 3);
+ rc = read_sys("LAB", argv[argi + 2], 3);
+ if (rc < 0)
+ return rc;
+ sys = sys | rc;
+ break;
+ case 'S':
+ p = strchr(argv[argi], '=');
+ if (!p) {
+ fprintf(stderr, "Given sysdef parameter '%s' requires '=' character to set value, use '-h' for help!\n", argv[argi]);
+ return -EINVAL;
+ }
+ p++;
+ if (!strncasecmp(argv[argi], "wt=", p - argv[argi])) {
+ wt = atoi(p);
+ if (wt != 5 && wt != 10 && wt != 15) {
+sysdef_oor:
+ fprintf(stderr, "Given sysdef parameter '%s' out of range, use '-h' for help!\n", argv[argi]);
+ return -EINVAL;
+ }
+ } else
+ if (!strncasecmp(argv[argi], "per=", p - argv[argi])) {
+ per = atoi(p);
+ if (per < 0 || per >31)
+ goto sysdef_oor;
+ } else
+ if (!strncasecmp(argv[argi], "pon=", p - argv[argi])) {
+ pon = atoi(p);
+ if (pon != 0 && pon != 1)
+ goto sysdef_oor;
+ } else
+ if (!strncasecmp(argv[argi], "timeout=", p - argv[argi])) {
+ timeout = atoi(p);
+ } else
+ {
+ fprintf(stderr, "Given sysdef parameter '%s' unknown, use '-h' for help!\n", argv[argi]);
+ return -EINVAL;
+ }
+ break;
+ case 'Q':
+ if (!strcasecmp(argv[argi], "auto"))
+ squelch_db = 0.0;
+ else
+ squelch_db = atof(argv[argi]);
+ break;
+ default:
+ return main_mobile_handle_options(short_option, argi, argv);
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int rc, argi;
+ const char *station_id = "";
+ int mandatory = 0;
+ int i;
+
+ /* init tones */
+ init_freiton();
+ init_besetzton();
+// init_ansage();
+
+ console_digits = "0123456789*#";
+ main_mobile_init();
+
+ /* handle options / config file */
+ add_options();
+ rc = options_config_file(argc, argv, "~/.osmocom/analog/mpt1327.conf", handle_options);
+ if (rc < 0)
+ return 0;
+ argi = options_command_line(argc, argv, handle_options);
+ if (argi <= 0)
+ return argi;
+
+ if (argi < argc) {
+ station_id = argv[argi];
+ if (strlen(station_id) != 7) {
+ printf("Given station ID '%s' does not have 4 digits\n", station_id);
+ return 0;
+ }
+ }
+
+ if (!num_kanal) {
+ printf("No channel (\"Kanal\") is specified, I suggest channel 1.\n\n");
+ mandatory = 1;
+ }
+ if (use_sdr) {
+ /* set audiodev */
+ for (i = 0; i < num_kanal; i++)
+ audiodev[i] = "sdr";
+ num_audiodev = num_kanal;
+ /* set channel types for more than 1 channel */
+ if (num_kanal > 1 && num_chan_type == 0) {
+ chan_type[0] = CHAN_TYPE_CC;
+ for (i = 1; i < num_kanal; i++)
+ chan_type[i] = CHAN_TYPE_TC;
+ num_chan_type = num_kanal;
+ }
+
+ }
+ if (num_kanal == 1 && num_audiodev == 0)
+ num_audiodev = 1; /* use default */
+ if (num_kanal != num_audiodev) {
+ fprintf(stderr, "You need to specify as many sound devices as you have channels.\n");
+ exit(0);
+ }
+ if (num_kanal == 1 && num_chan_type == 0)
+ num_chan_type = 1; /* use default */
+ if (num_kanal != num_chan_type) {
+ fprintf(stderr, "You need to specify as many channel types as you have channels.\n");
+ exit(0);
+ }
+
+ if (sys < 0) {
+ fprintf(stderr, "No System Identity Code is specified, make them match with your radio unit.\n\n");
+ mandatory = 1;
+ }
+ if (isinf(squelch_db) && pon == 0) {
+ fprintf(stderr, "'Pressel On' message (PON) and squelch are turned off. Enable one of them.\n\n");
+ mandatory = 1;
+ }
+ if (!isinf(squelch_db) && pon == 1) {
+ fprintf(stderr, "'Pressel On' message (PON) and squelch are turned on. Disable one of them.\n\n");
+ mandatory = 1;
+ }
+ if (pon && timeout <= per) {
+ fprintf(stderr, "The defined timeout value is lower than the Periodic message interval (PER). Define a greater timeout.\n\n");
+ mandatory = 1;
+ }
+ if (pon && (timeout && !per)) {
+ fprintf(stderr, "You must enable Periodic message interval (PER), if you use timeout (and have no squelch).\n\n");
+ mandatory = 1;
+ }
+ if (!pon && !timeout) {
+ fprintf(stderr, "Warning: 'Pressel On' message (PON) and timeout is both disabled. There will be no way to detect loss of Radio Unit.\n\n");
+ }
+
+ if (do_de_emphasis || do_pre_emphasis) {
+ printf("Don't use pre-/de-emphasis, it is not used for Speech, nor for signaling.\n\n");
+ mandatory = 1;
+ }
+
+ if (mandatory) {
+ print_help(argv[0]);
+ return 0;
+ }
+
+ /* no SDR, no squelch */
+ if (!use_sdr && !isinf(squelch_db)) {
+ fprintf(stderr, "Cannot use squelch without SDR! Analog receivers don't give use RSSI.\n");
+ goto fail;
+ }
+
+ printf("Using Sysdef 0x%04x:\n", sys);
+ if (!(sys & 0x4000)) {
+ printf("OID=%d NDD=%d LAB=%d\n", (sys >> 7) & 0x7f, (sys >> 3) & 0xf, sys & 0x7);
+ } else {
+ printf("NET=%d NDD=%d LAB=%d\n", (sys >> 12) & 0x3, (sys >> 3) & 0x1ff, sys & 0x7);
+ }
+
+ /* inits */
+ fm_init(fast_math);
+ dsp_init();
+ init_codeword();
+ init_sysdef(sys, wt, per, pon, timeout);
+
+ /* create transceiver instance */
+ for (i = 0; i < num_kanal; i++) {
+ rc = mpt1327_create(band, kanal[i], chan_type[i], audiodev[i], use_sdr, samplerate, rx_gain, tx_gain, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, squelch_db);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to create transceiver instance. Quitting!\n");
+ goto fail;
+ }
+ printf("base station on channel %s ready, please tune transmitter to %.4f MHz and receiver to %.4f MHz. (%s %.3f MHz offset)\n", kanal[i], mpt1327_channel2freq(band, atoi(kanal[i]), 0) / 1e6, mpt1327_channel2freq(band, atoi(kanal[i]), 1) / 1e6, mpt1327_band_name(band), mpt1327_channel2freq(band, atoi(kanal[i]), 2) / 1e6);
+ }
+
+ mpt1327_check_channels();
+
+ main_mobile("mpt1327", &quit, latency, interval, NULL, station_id, 7);
+
+fail:
+ /* destroy transceiver instance */
+ while (sender_head)
+ mpt1327_destroy(sender_head);
+
+ /* exits */
+ fm_exit();
+ flush_units();
+
+ options_free();
+
+ return 0;
+}
+
diff --git a/src/mpt1327/message.c b/src/mpt1327/message.c
new file mode 100644
index 0000000..c850594
--- /dev/null
+++ b/src/mpt1327/message.c
@@ -0,0 +1,566 @@
+/* message transcoding
+ *
+ * (C) 2021 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "../libdebug/debug.h"
+#include "message.h"
+
+static struct mpt1327_parameter_names {
+ const char *name;
+ const char *description;
+} mpt1327_parameter_names[] = {
+ { "<constant>", "Constant" },
+ { "PFIX", "Group Prefix" },
+ { "IDENT1", "Called Party Number" },
+ { "D", "Data Call" },
+ { "CHAN", "Channel Number" },
+ { "IDENT2", "Calling Party Number" },
+ { "(N)", "Aloha Number" },
+ { "P", "Parity" },
+ { "CAT", "Category" },
+ { "TYPE", "Type" },
+ { "FUNC", "Function" },
+ { "CHAN4", "Last 4 Bits of Channel Number" },
+ { "WT", "Delay Parameter" },
+ { "RSVD", "Reserved" },
+ { "(M)", "Address Qualifier" },
+ { "QUAL", "Qualifies FUNC" },
+ { "DT", "Data" },
+ { "LEVEL", "Priority level" },
+ { "EXT", "Extended Addressing" },
+ { "FLAG1", "Flag 1" },
+ { "FLAG2", "Flag 2" },
+ { "PARAMETERS", "Parameters" },
+ { "SD", "Speech and/or Data" },
+ { "DIV", "Diversion" },
+ { "INFO", "Info" },
+ { "STATUS", "Status" },
+ { "SLOTS", "Slots for Data Message" },
+ { "POINT", "Demand Acknowledgement" },
+ { "CHECK", "Availability Check" },
+ { "E", "Emergency Call" },
+ { "AD", "Data is appended" },
+ { "DESC", "Type of Data" },
+ { "A", "B" },
+ { "B", "B" },
+ { "SPARE", "Spare" },
+ { "REVS", "Bit Reversals" },
+ { "OPER", NULL },
+ { "SYS", NULL },
+ { "CONT", NULL },
+ { "SYSDEF", NULL },
+ { "PER", NULL },
+ { "IVAL", NULL },
+ { "PON", NULL },
+ { "ID", NULL },
+ { "ADJSITE", NULL },
+ { "SOL", NULL },
+ { "LEN", NULL },
+ { "PREFIX2", NULL },
+ { "KIND", NULL },
+ { "PORT", NULL },
+ { "FAD", NULL },
+ { "INTER", NULL },
+ { "HADT", NULL },
+ { "MODEM", NULL },
+ { "O/R", NULL },
+ { "RATE", NULL },
+ { "TRANS", NULL },
+ { "RNITEL", NULL },
+ { "TNITEL", NULL },
+ { "JOB", NULL },
+ { "REASON", NULL },
+ { "ATRANS", NULL },
+ { "EFLAGS", NULL },
+ { "TASK", NULL },
+ { "ONES", NULL },
+ { "ITENUM", NULL },
+ { "USERDATA", NULL },
+ { "I/G", NULL },
+ { "MORE", NULL },
+ { "LASTBIT", NULL },
+ { "FRAGL", NULL },
+ { "RTRANS", NULL },
+ { "W/F", NULL },
+ { "P/N", NULL },
+ { "DN", NULL },
+ { "SPRE", NULL },
+ { "SX", NULL },
+ { "CAUSE", NULL },
+ { "I/T", NULL },
+ { "RESP", NULL },
+ { "TOC", NULL },
+ { "CCS", "Codeword Completion Sequence" },
+ { "LET", "Link Establishmen Time" },
+ { "PREAMBLE", NULL },
+ { "PARAMETERS1", NULL },
+ { "PARAMETERS2", NULL },
+ { "BCD11", "11 Digits encoded as BCD" },
+ { "RSA", NULL },
+ { "FCW", NULL },
+ { "SP", NULL },
+ { "EXCHANGE", NULL },
+ { "Number", NULL },
+ { "GF", NULL },
+ { "PFIXT", NULL },
+ { "IDENTT", NULL },
+ { "FORM", NULL },
+ { "PFIX2", NULL },
+};
+
+char *mpt1327_bcd = "0123456789R*#RR"; /* last digit is NULL */
+
+static struct definitions {
+ int specific_only;
+ enum mpt1327_codeword_dir dir;
+ enum mpt1327_codeword_type type;
+ char *def;
+ const char *short_name;
+ const char *long_name;
+} definitions[] = {
+ /* Filler */
+ { 1, MPT_DOWN, MPT_FILLER, "0 RSVD:47=00000000000000000000000000000000000000000000000 P:16", "filler", "Filler Data" },
+ /* GTC Message */
+ { 0, MPT_DOWN, MPT_GTC, "1 PFIX:7 IDENT1:13 0 D:1 CHAN:10 IDENT2:13 (N):2 P:16", "GTC", "Go To Traffic Channel" },
+ /* Category '000' Messages: Aloha Messages (Type '00') */
+ { 0, MPT_DOWN, MPT_ALH, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=00 FUNC:3=000 CHAN4:4 WT:3 RSVD:2 (M):5 (N):4 P:16", "ALH", "Aloha: Any single codeword message invited" },
+ { 0, MPT_DOWN, MPT_ALHS, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=00 FUNC:3=001 CHAN4:4 WT:3 RSVD:2 (M):5 (N):4 P:16", "ALHS", "Aloha: Messages invited, except RQD" },
+ { 0, MPT_DOWN, MPT_ALHD, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=00 FUNC:3=010 CHAN4:4 WT:3 RSVD:2 (M):5 (N):4 P:16", "ALHD", "Aloha: Messages invited, except RQS" },
+ { 0, MPT_DOWN, MPT_ALHE, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=00 FUNC:3=011 CHAN4:4 WT:3 RSVD:2 (M):5 (N):4 P:16", "ALHE", "Aloha: Emergency requests (RQE) only invited" },
+ { 0, MPT_DOWN, MPT_ALHR, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=00 FUNC:3=100 CHAN4:4 WT:3 RSVD:2 (M):5 (N):4 P:16", "ALHR", "Aloha: Registration (RQR) or emergency requests (RQE) invited" },
+ { 0, MPT_DOWN, MPT_ALHX, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=00 FUNC:3=101 CHAN4:4 WT:3 RSVD:2 (M):5 (N):4 P:16", "ALHX", "Aloha: Messages invited, except RQR" },
+ { 0, MPT_DOWN, MPT_ALHF, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=00 FUNC:3=110 CHAN4:4 WT:3 RSVD:2 (M):5 (N):4 P:16", "ALHF", "Aloha: Fall-back mode" },
+ /* Category '000' Messages: Acknowledgement Messages (Type '01') */
+ { 0, MPT_BOTH, MPT_ACK, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=01 FUNC:3=000 IDENT2:13 QUAL:1 (N):4 P:16", "ACK", "Ack: General acknowledgement" },
+ { 0, MPT_BOTH, MPT_ACKI, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=01 FUNC:3=001 IDENT2:13 QUAL:1 (N):4 P:16", "ACKI", "Ack: Intermediate acknowledgement, more signalling to follow" },
+ { 0, MPT_BOTH, MPT_ACKQ, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=01 FUNC:3=010 IDENT2:13 QUAL:1 (N):4 P:16", "ACKQ", "Ack: Acknowledge, call queued" },
+ { 0, MPT_BOTH, MPT_ACKX, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=01 FUNC:3=011 IDENT2:13 QUAL:1 (N):4 P:16", "ACKX", "Ack: Acknowledge, message rejected" },
+ { 0, MPT_BOTH, MPT_ACKV, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=01 FUNC:3=100 IDENT2:13 QUAL:1 (N):4 P:16", "ACKV", "Ack: Acknowledge, called unit unavailable" },
+ { 0, MPT_BOTH, MPT_ACKE, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=01 FUNC:3=101 IDENT2:13 QUAL:1 (N):4 P:16", "ACKE", "Ack: Acknowledge emergency call" },
+ { 0, MPT_BOTH, MPT_ACKT, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=01 FUNC:3=110 IDENT2:13 QUAL:1 (N):4 P:16", "ACKT", "Ack: Acknowledge, try on given address" },
+ { 0, MPT_BOTH, MPT_ACKB, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=01 FUNC:3=111 IDENT2:13 QUAL:1 (N):4 P:16", "ACKB", "Ack: Acknowledge, call-back, or negative acknowledgement" },
+ /* Category '000' Messages: Request Messages (Type '10') */
+ { 0, MPT_UP, MPT_RQS, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=000 IDENT2:13 DT:1 LEVEL:1 EXT:1 FLAG1:1 FLAG2:1 P:16", "RQS", "Request: Request Simple call" },
+ { 0, MPT_UP, MPT_RQSpare, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=001 PARAMETERS:18 P:16", "RQSpstr", "Request: Spare. Available for customisation" },
+ { 0, MPT_UP, MPT_RQX, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=010 IDENT2:13 RSVD:5 P:16", "RQX", "Request: Request call cancel / abort transaction" },
+ { 0, MPT_UP, MPT_RQT, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=011 IDENT2:13 SD:2 DIV:1 FLAG1:1 FLAG2:1 P:16", "RQT", "Request: Request call diversion" },
+ { 0, MPT_UP, MPT_RQE, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=100 IDENT2:13 D:1 RSVD:1 EXT:1 FLAG1:1 FLAG2:1 P:16", "RQE", "Request: Request emergency call" },
+ { 0, MPT_UP, MPT_RQR, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=101 INFO:15 RSVD:3 P:16", "RQR", "Request: Request to register" },
+ { 0, MPT_UP, MPT_RQQ, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=110 IDENT2:13 STATUS:5 P:16", "RQQ", "Request: Request status transaction" },
+ { 0, MPT_UP, MPT_RQC, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=111 IDENT2:13 SLOTS:2 EXT:1 FLAG1:1 FLAG2:1 P:16", "RQC", "Request: Request to send short data message" },
+ /* Category '000' Messages: Ahoy Messages (Type '10') */
+ { 0, MPT_DOWN, MPT_AHY, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=000 IDENT2:13 D:1 POINT:1 CHECK:1 E:1 AD:1 P:16", "AHY", "Ahoy: General availability check" },
+ { 0, MPT_DOWN, MPT_AHYSpare, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=001 PARAMETERS:18 P:16", "AHYSpare", "Ahoy: Spare for customisation" },
+ { 0, MPT_DOWN, MPT_AHYX, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=010 IDENT2:13 POINT:5 P:16", "AHYX", "Ahoy: Cancel alert/waiting state" },
+ { 0, MPT_DOWN, MPT_AHYP, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=101 IDENT2:13 RSVD:5 P:16", "AHYP", "Ahoy: Called Unit Presence Monitoring" },
+ { 0, MPT_DOWN, MPT_AHYQ, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=110 IDENT2:13 STATUS:5 P:16", "AHYQ", "Ahoy: Status message" },
+ { 0, MPT_DOWN, MPT_AHYC, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=10 FUNC:3=111 IDENT2:13 SLOTS:2 DESC:3 P:16", "AHYC", "Ahoy: Short data invitation" },
+ /* Category '000' Messages: Miscellaneous Control Messages (Type '11') */
+ { 0, MPT_DOWN, MPT_MARK, "1 CHAN4:4 A:1 SYS:15 1 CAT:3=000 TYPE:2=11 FUNC:3=000 B:18 P:16", "MARK", "Misc: Control channel marker" },
+ { 0, MPT_BOTH, MPT_MAINT, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=11 FUNC:3=001 CHAN:10 OPER:3 RSVD:5 P:16", "MAINT", "Misc: Call maintenance message" },
+ { 0, MPT_DOWN, MPT_CLEAR, "1 CHAN:10 CONT:10 1 CAT:3=000 TYPE:2=11 FUNC:3=010 RSVD:4 SPARE:2 REVS:12=101010101010 P:16", "CLEAR", "Misc: Clear down from allocated channel" },
+ { 0, MPT_DOWN, MPT_MOVE, "1 PFIX:7 IDENT1:13 1 CAT:3=000 TYPE:2=11 FUNC:3=011 CONT:10 (M):5 RSVD:2 SPARE:1 P:16", "MOVE", "Misc: Move to specified control channel" },
+ { 0, MPT_DOWN, MPT_BCAST0, "1 SYSDEF:5=00000 SYS:15 1 CAT:3=000 TYPE:2=11 FUNC:3=100 CHAN:10 SPARE:2 RSVD:6 P:16", "BCAST", "Misc: Broadcast message: Announce control channel" },
+ { 0, MPT_DOWN, MPT_BCAST1, "1 SYSDEF:5=00001 SYS:15 1 CAT:3=000 TYPE:2=11 FUNC:3=100 CHAN:10 SPARE:2 RSVD:6 P:16", "BCAST", "Misc: Broadcast message: Withdraw control channel" },
+ { 0, MPT_DOWN, MPT_BCAST2, "1 SYSDEF:5=00010 SYS:15 1 CAT:3=000 TYPE:2=11 FUNC:3=100 PER:1 IVAL:5 PON:1 ID:1 RSVD:2 SPARE:8 P:16", "BCAST", "Misc: Broadcast message: Specify call maintenance parameter" },
+ { 0, MPT_DOWN, MPT_BCAST3, "1 SYSDEF:5=00011 SYS:15 1 CAT:3=000 TYPE:2=11 FUNC:3=100 RSVD:4 SPARE:14 P:16", "BCAST", "Misc: Broadcast message: Specify registration parameters" },
+ { 0, MPT_DOWN, MPT_BCAST4, "1 SYSDEF:5=00100 SYS:15 1 CAT:3=000 TYPE:2=11 FUNC:3=100 CHAN:10 SPARE:2 RSVD:2 ADJSITE:4 P:16", "BCAST", "Misc: Broadcast message: Broadcast adjected site control channel number" },
+ { 0, MPT_DOWN, MPT_BCAST5, "1 SYSDEF:5=00101 SYS:15 1 CAT:3=000 TYPE:2=11 FUNC:3=100 CHAN:10 SPARE:2 RSVD:2 ADJSITE:4 P:16", "BCAST", "Misc: Broadcast message: Vote now advice" },
+ /* Category '001' Messages */
+ { 0, MPT_DOWN, MPT_SAMO, "1 PFIX:7 IDENT1:13 1 CAT:3=001 TYPE:1=0 PARAMETERS:22 P:16", "SAMO", "Sam: Outbound Single Address Message" },
+ { 0, MPT_UP, MPT_SAMIU, "1 PFIX:7 IDENT1:13 1 CAT:3=001 TYPE:1=0 SOL:1=1 PARAMETERS:21 P:16", "SAMIU", "Sam: Inbound Unsolicited Single Address Message" },
+ { 0, MPT_UP, MPT_SAMIS, "1 PARAMETERS1:20 1 CAT:3=001 TYPE:1=0 SOL:1=0 DESC:3 PARAMETERS2:18 P:16", "SAMIS", "Sam: Inbound Solicited Single Address Message" },
+ { 0, MPT_BOTH, MPT_HEAD, "1 PFIX:7 IDENT1:13 1 CAT:3=001 TYPE:1=1 LEN:2 PREFIX2:7 IDENT2:13 P:16", "HEAD", "Short Data Message Header" },
+ /*0Category '010' Messages */
+ { 0, MPT_UP, MPT_RQD, "1 PFIX:7 IDENT1:13 1 CAT:3=010 KIND:1=1 PORT:3 FAD:1 IDENT2:13 INTER:1 LEVEL:1 HADT:1 E:1 MODEM:1 P:16", "RQD", "Request Standard Data Communication" },
+ { 0, MPT_DOWN, MPT_AHYD, "1 PFIX:7 IDENT1:13 1 CAT:3=010 KIND:1=1 PORT:3 RSVD:1 IDENT2:13 INTER:1 POINT:1 HADT:1 E:1 AD:1 P:16", "AHYD", "Availability Check for Standard Data" },
+ { 0, MPT_DOWN, MPT_GTT, "1 PFIX:7 IDENT1:13 1 CAT:3=010 KIND:1=0 CHAN:10 O/R:1 RATE:1 TRANS:10 P:16", "GTT", "Go To Transaction" },
+ { 0, MPT_UP, MPT_DRUGI, "1 PFIX:7 IDENT1:13 1 CAT:3=010 KIND:1=0 RNITEL:6 TNITEL:6 TRANS:10 P:16", "DRUGI", "Standard Data Random access, Radio Unit General Information" },
+ /* Category '101' Messages */
+ { 0, MPT_BOTH, MPT_DACKD, "1 PFIX:7 IDENT1:13 1 CAT:3=101 KIND:1=0 JOB:4=0101 RSVD:5 REASON:3 TRANS:10 P:16", "DACKD", "Standard Data general purpose acknowlegement" },
+ { 0, MPT_DOWN, MPT_DACK_DAL, "1 ATRANS:10 RTRANS:10 1 CAT:3=101 KIND:1=0 JOB:4=0000 W/F:3 P/N:1 RSVD:2 DN:5 TNITEL:6 ITENUM:1 P:16", "DACK+DAL", "Standard Data Codeword + DAL" },
+ { 0, MPT_DOWN, MPT_DACK_DALG, "1 ATRANS:10 RTRANS:10 1 CAT:3=101 KIND:1=0 JOB:4=0001 W/F:3 P/N:1 RSVD:2 DN:5 TNITEL:6 ITENUM:1 P:16", "DACK+DALG", "Standard Data Codeword + DALG" },
+ { 0, MPT_DOWN, MPT_DACK_DALN, "1 ATRANS:10 RTRANS:10 1 CAT:3=101 KIND:1=0 JOB:4=0010 W/F:3 P/N:1 RSVD:2 DN:5 TNITEL:6 ITENUM:1 P:16", "DACK+DALN", "Standard Data Codeword + DALN" },
+ { 0, MPT_BOTH, MPT_DACK_GO, "1 ATRANS:10 RTRANS:10 1 CAT:3=101 KIND:1=0 JOB:4=0011 RSVD:3 P/N:1 RSVD:1 RNITEL:6 TNITEL:6 ITENUM:1 P:16", "DACK+'GO'", "Standard Data Codeword + 'GO'" },
+ { 0, MPT_BOTH, MPT_DACKZ, "1 ATRANS:10 SPRE:10 1 CAT:3=101 KIND:1=0 JOB:4=0100 SX:3 SPRE:7 CAUSE:8 P:16", "DACKZ", "Standard Data Acknowledgement for expedited data" },
+ { 0, MPT_DOWN, MPT_DAHY, "1 TRANS:10 RSVD:10 1 CAT:3=101 KIND:1=0 JOB:4=1000 RSVD:10 SPARE:8 P:16", "DAHY", "Standard Data General ahoy" },
+ { 0, MPT_DOWN, MPT_DAHYZ, "1 SPRE:10 RSVD:10 1 CAT:3=101 KIND:1=0 JOB:4=1100 SX:3 SPRE:7 CAUSE:8 P:16", "DAHYZ", "Standard Data ahoy containing expedited data" },
+ { 0, MPT_DOWN, MPT_DAHYX, "1 PFIX:7 IDENT1:13 1 CAT:3=101 KIND:1=0 JOB:4=1110 I/T:1 RESP:1 SPRE:3 TOC:3 TRANS:10 P:16", "DHAYX", "Standard Data ahoy containing expedited data" },
+ { 0, MPT_BOTH, MPT_RLA, "1 TRANS:10 RSVD:10 1 CAT:3=101 KIND:1=0 JOB:4=1111 RSVD:12 SPARE:6 P:16", "RLA", "Repeat last ACK" },
+ { 0, MPT_UP, MPT_DRQG, "1 TRANS:10 SPARE:7 RSVD:3 1 CAT:3=101 KIND:1=0 JOB:4=1010 RSVD:18 P:16", "DRQG", "Repeat group message" },
+ { 0, MPT_UP, MPT_DRQZ, "1 TRANS:10 SPRE:10 1 CAT:3=101 KIND:1=0 JOB:4=1100 SX:3 SPRE:7 CAUSE:8 P:16", "DRQZ", "Request containing expedited data" },
+ { 0, MPT_UP, MPT_DRQX, "1 PFIX:7 IDENT1:13 1 CAT:3=101 KIND:1=0 JOB:4=1110 SPRE:5 TOC:3 TRANS:10 P:16", "DRQX", "Request to close a transaction" },
+ { 0, MPT_BOTH, MPT_SACK, "1 ATRANS:10 EFLAGS:10 1 CAT:3=101 KIND:1=1 TASK:1=0 RSVD:2 EFLAGS:13 ONES:4 AD:1 ITENUM:1 P:16", "SACK", "Standard Data Selective Acknowledgement Header" },
+ { 0, MPT_BOTH, MPT_SITH_I, "1 TRANS:10 USERDATA:10 1 CAT:3=101 KIND:1=1 TASK:1=1 I/G:1=0 MORE:1 LASTBIT:6 FRAGL:6 TNITEL:6 ITENUM:1 P:16", "SITH", "Standard Data Address Codeword (Individual) Dataitem" },
+ { 0, MPT_DOWN, MPT_SITH_G, "1 TRANS:10 USERDATA:10 1 CAT:3=101 KIND:1=1 TASK:1=1 I/G:1=1 MORE:1 LASTBIT:6 FRAGL:8 RSVD:4 ITENUM:1 P:16", "SITH", "Standard Data Address Codeword (Group) Dataitem" },
+ /* Startup & CCSC */
+ { 1, MPT_DOWN, MPT_START_SYNC, "LET:32=00000000000000000000000000000000 PREAMBLE:16=1010101010101010 1100010011010111", "Startup", "Startup sequence on CC" },
+ { 0, MPT_DOWN, MPT_CCSC, "0 SYS:15 CCS:16 PREAMBLE:16=1010101010101010 P:16", "CCSC/DCSC", "System Identification" },
+ { 1, MPT_DOWN, MPT_START_SYNT, "LET:32=00000000000000000000000000000000 PREAMBLE:16=1010101010101010 0011101100101000", "SYNT", "Startup sequence on TC" },
+
+ /* Data codewords following ACKT(QUAL=0) address codeword */
+ { 1, MPT_DOWN, MPT_ACKT_DT1, "0 RSA:1 FCW:2 BCD11:44 P:16", "ACKT Data 1", "Ack: Acknowledge, try on given address; Data Word 1" },
+ { 1, MPT_DOWN, MPT_ACKT_DT2, "0 RSVD:10 SP:1=0 PARAMETERS:36 P:16", "ACKT Data 2", "Ack: Acknowledge, try on given address; Data Word 2" },
+ { 1, MPT_DOWN, MPT_ACKT_DT3, "0 RSVD:10 SP:1=1 RSVD:21 EXCHANGE:2 Number:13 P:16", "ACKT Data 3", "Ack: Acknowledge, try on given address; Data Word 3" },
+ { 1, MPT_DOWN, MPT_ACKT_DT4, "0 RSVD:26 GF:1 PFIXT:7 IDENTT:13 P:16", "ACKT Data 4", "Ack: Acknowledge, try on given address; Data Word 4" },
+ /* Data codeword following AHY address codeword */
+ { 1, MPT_DOWN, MPT_AHY_DT, "0 FORM:3=000 RSVD:24 PFIX2:7 IDENT2:13 P:16", "AHY Data", "Ahoy: General availability check; Data Word" },
+ /* Data codeword following AHYQ address codeword */
+ { 1, MPT_DOWN, MPT_AHYQ_DT, "0 RSVD:27 PFIX:7 IDENT2:13 P:16", "AHYQ Data", "Ahoy: Status message; Data Word" },
+ /* Data codewords appended to SAMIS, Mode 1 */
+ { 1, MPT_UP, MPT_SAMIS_DT, "0 RSVD:3 BCD11:44 P:16", "SAMIS Data", "Sam: Inbound Solicited Single Address Message; Data Word" },
+ /* Data codeword(s) following HEAD address codeword */
+ { 1, MPT_DOWN, MPT_HEAD_DT, "0 RSA:1 PARAMETERS:46 P:16", "HEAD Data", "Short Data Message Header; Data Word" },
+ /* Data codeword following AHYD address codeword */
+ { 1, MPT_DOWN, MPT_AHYD_DT, "0 FORM:3=000 RSVD:24 PFIX2:7 IDENT2:13 P:16", "AHYD Data", "Availability Check for Standard Data; Data Word" },
+ /* Data codeword following Standard Data Acknowledgement Header SACK */
+ { 1, MPT_DOWN, MPT_SACK_DT, "0 ONES:4 EFLAGS:40 RSVD:3 P:16", "SACK Data", "Standard Data Selective Acknowledgement; Data Word" },
+};
+
+static struct mpt1327_defintion {
+ int specific_only;
+ enum mpt1327_codeword_dir dir;
+ enum mpt1327_codeword_type type;
+ const char *short_name;
+ const char *long_name;
+ uint64_t bits, mask;
+ enum mpt1327_parameters params[64];
+} mpt1327_definitions[_NUM_MPT_DEFINITIONS];
+
+static void _CHECK_MAX_BITS(int bits, const char *name)
+{
+ if (bits == 64) {
+ fprintf(stderr, "Message '%s' exceeds 64 bits, please fix!\n", name);
+ abort();
+ }
+}
+
+void init_codeword(void)
+{
+ uint64_t bits, mask;
+ int num_bits;
+ enum mpt1327_parameters params[64];
+ char *param_text, *next_param, *param, *param_bits, *param_const;
+ int i, j, p, b;
+
+ if (sizeof(definitions) / sizeof(definitions[0]) != _NUM_MPT_DEFINITIONS) {
+ fprintf(stderr, "definitions[] has different size than enum mpt1327_codeword_type, please fix!\n");
+ abort();
+ }
+ if (sizeof(mpt1327_parameter_names) / sizeof(mpt1327_parameter_names[0]) != _NUM_MPT_PARAMETERS) {
+ fprintf(stderr, "mpt1327_parameter_names[] has different size than enum mpt1352_parameters, please fix!\n");
+ abort();
+ }
+
+ /* parse all message definitions */
+ for (i = 0; i < _NUM_MPT_DEFINITIONS; i++) {
+ bits = mask = 0;
+ num_bits = 0;
+ param_text = next_param = strdup(definitions[i].def);
+ while ((param = strsep(&next_param, " "))) {
+ if (param[0] >= '0' && param[0] <= '9') {
+ /* param is a constant */
+ while (*param) {
+ if (*param < '0' || *param > '1') {
+ fprintf(stderr, "Constant '%s' does not consists of '0' or '1' only, please fix!\n", param);
+ abort();
+ }
+ _CHECK_MAX_BITS(num_bits, definitions[i].short_name);
+ if ((*param++ & 1))
+ bits |= 0x8000000000000000 >> num_bits;
+ mask |= 0x8000000000000000 >> num_bits;
+ params[num_bits] = 0;
+ num_bits++;
+ }
+ } else {
+ /* param is a parameter */
+ param_bits = strchr(param, ':');
+ if (!param_bits) {
+ fprintf(stderr, "Param '%s' does not have a ':' to define number of bits, please fix!\n", param);
+ abort();
+ }
+ *param_bits++ = '\0';
+ /* get parameter from param text */
+ for (p = 0; p < _NUM_MPT_PARAMETERS; p++) {
+ if (!strcmp(mpt1327_parameter_names[p].name, param))
+ break;
+ }
+ if (p == _NUM_MPT_PARAMETERS) {
+ fprintf(stderr, "Param '%s' is not found in list of parameter names, please fix!\n", param);
+ p = 0;
+ }
+ if (p > _NUM_MPT_PARAMETERS) {
+ fprintf(stderr, "There are more parameters than definitons, please fix!\n");
+ abort();
+ }
+ /* get constant for parameter, if given */
+ param_const = strchr(param_bits, '=');
+ if (param_const) {
+ *param_const++ = '\0';
+ if ((int)strlen(param_const) != atoi(param_bits)) {
+ fprintf(stderr, "Param '%s' has %s bits, but constant '%s' does not, please fix!\n", param, param_bits, param_const);
+ abort();
+ }
+ }
+ for (b = 0; b < atoi(param_bits); b++) {
+ _CHECK_MAX_BITS(num_bits, definitions[i].short_name);
+ if (param_const) {
+ if (param_const[b] < '0' || param_const[b] > '1') {
+ fprintf(stderr, "Param '%s' has a constant '%s', but must only consist of '0' or '1', please fix!\n", param, param_const);
+ abort();
+ }
+ if (param_const[b] == '1')
+ bits |= 0x8000000000000000 >> num_bits;
+ mask |= 0x8000000000000000 >> num_bits;
+ }
+ params[num_bits] = p;
+ num_bits++;
+ }
+ }
+ }
+ free(param_text);
+ if (num_bits != 64) {
+ fprintf(stderr, "Message '%s' (has %d bits) is not exactly 64 bits, please fix!\n", definitions[i].short_name, num_bits);
+ abort();
+ }
+#if 0
+ printf("Message definition for '%s'\n", definitions[i].short_name);
+ printf("%s\n", definitions[i].def);
+ for (b = 0; b < 64; b++)
+ printf("mask=%d data=%d name=%s\n", (mask >> (63 - b)) & 1, (bits >> (63 - b)) & 1, mpt1327_parameter_names[params[b]].name);
+#endif
+ /* check type */
+ if ((int)definitions[i].type != i) {
+ fprintf(stderr, "Message '%s' has type %d, but index is %d. Type and index must match, please fix!\n", definitions[i].short_name, definitions[i].type, i);
+ abort();
+ }
+ /* store codeword definition */
+ mpt1327_definitions[i].specific_only = definitions[i].specific_only;
+ mpt1327_definitions[i].dir = definitions[i].dir;
+ mpt1327_definitions[i].type = definitions[i].type;
+ mpt1327_definitions[i].short_name = definitions[i].short_name;
+ mpt1327_definitions[i].long_name = definitions[i].long_name;
+ mpt1327_definitions[i].bits = bits;
+ mpt1327_definitions[i].mask = mask;
+ memcpy(mpt1327_definitions[i].params, params, sizeof(params));
+ /* check for duplicate message types */
+ for (j = 0; j < i; j++)
+ if (mpt1327_definitions[j].type == definitions[i].type)
+ break;
+ if (j < i) {
+ fprintf(stderr, "Message '%s' is duplicated (index %d and %d have same message type), please fix!\n", definitions[i].short_name, j, i);
+ abort();
+ }
+ }
+}
+
+/* calculate check bits, ispired by olle@toolcrypt.org (snable) */
+uint16_t mpt1327_checkbits(uint64_t bits, uint16_t *parityp)
+{
+ uint16_t check = 0x0000, parity = 0;
+ int bit;
+ int b;
+
+ /* calculate check at upper 15 bits */
+ for (b = 0; b < 48; b++) {
+ bit = (bits >> (63 - b)) & 1;
+ parity ^= bit;
+ if (bit != (check >> 15))
+ check ^= 0x6815;
+ check <<= 1;
+ }
+
+ /* invert lowest check bit (of 15 upper bits) */
+ check ^= 0x0002;
+
+ /* finish parity and append as lest bit (bit 0) */
+ for (b = 1; b < 16; b++)
+ parity ^= (check >> b) & 1;
+ check ^= parity;
+
+ if (parityp)
+ *parityp = parity;
+ return check;
+}
+
+static void debug_codeword(const char *prefix, int i, uint64_t bits, int enc)
+{
+ uint64_t value;
+ char text[1024];
+ int column;
+ int b;
+
+ if (debuglevel > DEBUG_INFO)
+ return;
+
+ switch (mpt1327_definitions[i].type) {
+ case MPT_START_SYNC:
+ case MPT_CCSC:
+ case MPT_START_SYNT:
+ case MPT_ALH:
+ case MPT_ALHS:
+ case MPT_ALHD:
+ case MPT_ALHE:
+ case MPT_ALHR:
+ case MPT_ALHX:
+ case MPT_ALHF:
+ case MPT_BCAST0:
+ case MPT_BCAST1:
+ case MPT_BCAST2:
+ case MPT_BCAST3:
+ case MPT_BCAST4:
+ case MPT_BCAST5:
+ if (enc && debuglevel > DEBUG_DEBUG)
+ return;
+ default:
+ ;
+ }
+
+ PDEBUG(DFRAME, DEBUG_INFO, "%s Codeword %s: %s\n", prefix, mpt1327_definitions[i].short_name, mpt1327_definitions[i].long_name);
+ column = 0;
+ for (b = 0; b < 64; b++) {
+ /* if we have first parameter or we swith to next parameter */
+ if (b == 0 || mpt1327_definitions[i].params[b] != mpt1327_definitions[i].params[b - 1]) {
+ value = 0;
+ if (b != 0)
+ text[column++] = ' ';
+ if (mpt1327_definitions[i].params[b]) {
+ strcpy(text + column, mpt1327_parameter_names[mpt1327_definitions[i].params[b]].name);
+ column += strlen(mpt1327_parameter_names[mpt1327_definitions[i].params[b]].name);
+ text[column++] = '=';
+ }
+ }
+ value = (value << 1) | ((bits >> (63 - b)) & 1);
+ text[column++] = ((bits >> (63 - b)) & 1) + '0';
+#if 0
+ if (b == 63 || mpt1327_definitions[i].params[b] != mpt1327_definitions[i].params[b + 1]) {
+ sprintf(text + column, "(%" PRIu64 ")", value);
+ column += strlen(text + column);
+ }
+#endif
+ }
+ text[column] = '\0';
+ PDEBUG(DFRAME, DEBUG_INFO, "%s\n", text);
+}
+
+uint64_t mpt1327_encode_codeword(mpt1327_codeword_t *codeword)
+{
+ uint64_t params[_NUM_MPT_PARAMETERS];
+ uint64_t bits;
+ int i, b;
+
+ /* check all codeword definitions */
+ for (i = 0; i < _NUM_MPT_DEFINITIONS; i++) {
+ if (mpt1327_definitions[i].type == codeword->type)
+ break;
+ }
+ if (i == _NUM_MPT_DEFINITIONS) {
+ fprintf(stderr, "Codeword not found for type %d, please fix!\n", codeword->type);
+ abort();
+ }
+
+ /* fill parameters */
+ memcpy(params, codeword->params, sizeof(params));
+ bits = 0;
+ for (b = 63; b >= 0; b--) {
+ if ((params[mpt1327_definitions[i].params[b]] & 1))
+ bits |= (0x8000000000000000 >> b);
+ params[mpt1327_definitions[i].params[b]] >>= 1;
+ }
+
+ /* set constants */
+ bits = (bits & ~mpt1327_definitions[i].mask) | mpt1327_definitions[i].bits;
+
+ /* calculate MPT_CCS (See MTP1327 Appendix 3) */
+ if (codeword->type == MPT_CCSC) {
+ uint64_t ccs = 0xaaaac4d400000000 | ((codeword->params[MPT_SYS] & 0x7fff) << 17);
+ uint16_t parity;
+assumption_wrong:
+ ccs = (ccs & 0xffffffffffff0000) | mpt1327_checkbits(ccs, &parity);
+ if (parity == 0) {
+ ccs |= 0x10000;
+ goto assumption_wrong;
+ }
+ bits = (bits & 0xffff0000ffffffff) | (((ccs ^ 0x2) & 0x1fffe) << 31);
+ }
+
+ /* add parity, if not forced by definition */
+ if (!(mpt1327_definitions[i].mask & 0xffff))
+ bits = (bits & 0xffffffffffff0000) | mpt1327_checkbits(bits, NULL);
+
+ debug_codeword("Transmitting", i, bits, 1);
+
+ return bits;
+}
+
+int mpt1327_decode_codeword(mpt1327_codeword_t *codeword, int specific, enum mpt1327_codeword_dir dir, uint64_t bits)
+{
+ int i, b;
+
+ memset(codeword, 0, sizeof(*codeword));
+ codeword->dir = dir;
+
+ /* check all codeword definitions */
+ for (i = 0; i < _NUM_MPT_DEFINITIONS; i++) {
+ /* skip if direction does not match */
+ if (dir != mpt1327_definitions[i].dir && mpt1327_definitions[i].dir != MPT_BOTH)
+ continue;
+ if (specific >= 0) {
+ /* select where type matches */
+ if (mpt1327_definitions[i].type == (unsigned int)specific)
+ break;
+ } else {
+ /* ignore message definitions that require specifiying codeword type */
+ if (mpt1327_definitions[i].specific_only)
+ continue;
+ /* select where masked bits match */
+ if (mpt1327_definitions[i].bits == (bits & mpt1327_definitions[i].mask))
+ break;
+ }
+ }
+ if (i == _NUM_MPT_DEFINITIONS) {
+ char debug[256];
+ PDEBUG(DFRAME, DEBUG_NOTICE, "Received unknown codeword or loopback from transmitter side.\n");
+ for (b = 0; b < 64; b++)
+ debug[b] = ((bits >> (63 - b)) & 1) + '0';
+ debug[b] = '\0';
+ PDEBUG(DFRAME, DEBUG_DEBUG, "%s\n", debug);
+ return -EINVAL;
+ }
+ codeword->type = mpt1327_definitions[i].type;
+ codeword->short_name = mpt1327_definitions[i].short_name;
+ codeword->long_name = mpt1327_definitions[i].long_name;
+
+ /* fill parameters */
+ for (b = 0; b < 64; b++) {
+ codeword->params[mpt1327_definitions[i].params[b]] <<= 1;
+ if ((bits & (0x8000000000000000 >> b)))
+ codeword->params[mpt1327_definitions[i].params[b]] |= 1;
+ }
+
+ debug_codeword("Receiving", i, bits, 0);
+
+ return 0;
+}
+
diff --git a/src/mpt1327/message.h b/src/mpt1327/message.h
new file mode 100644
index 0000000..0cd9d19
--- /dev/null
+++ b/src/mpt1327/message.h
@@ -0,0 +1,227 @@
+
+#define IDENT_ALLI 8191 /* System-wide ident */
+#define IDENT_TSCI 8190 /* Ident of TSC */
+#define IDENT_IPFIXI 8189 /* Interprefix ident */
+#define IDENT_SDMI 8188 /* Short data message ident */
+#define IDENT_DIVERTI 8187 /* Divert ident */
+#define IDENT_INCI 8186 /* Include ident */
+#define IDENT_REGI 8185 /* Registration ident */
+#define IDENT_PSTNSI1 8121 /* Short-form PSTN idents */
+#define IDENT_NETSI1 8121 /* Short-form data Network idents */
+#define IDENT_DNI 8103 /* Data Network gateway ident */
+#define IDENT_PABXI 8102 /* PABX gateway ident */
+#define IDENT_PSTNGI 8101 /* General PSTN gateway ident */
+#define IDENT_DUMMYI 0 /* Dummy ident */
+
+#define OPER_PRESSEL_ON 0
+#define OPER_PRESSEL_OFF 1
+#define OPER_PERIODIC 2
+#define OPER_DISCONNECT 3
+#define OPER_SPARE 4
+#define OPER_RESERVED 5
+#define OPER_CLEAR 6
+#define OPER_DISABLE 7
+
+enum mpt1327_codeword_dir {
+ MPT_DOWN,
+ MPT_UP,
+ MPT_BOTH,
+};
+
+enum mpt1327_codeword_type {
+ MPT_FILLER = 0,
+ MPT_GTC,
+ MPT_ALH,
+ MPT_ALHS,
+ MPT_ALHD,
+ MPT_ALHE,
+ MPT_ALHR,
+ MPT_ALHX,
+ MPT_ALHF,
+ MPT_ACK,
+ MPT_ACKI,
+ MPT_ACKQ,
+ MPT_ACKX,
+ MPT_ACKV,
+ MPT_ACKE,
+ MPT_ACKT,
+ MPT_ACKB,
+ MPT_RQS,
+ MPT_RQSpare,
+ MPT_RQX,
+ MPT_RQT,
+ MPT_RQE,
+ MPT_RQR,
+ MPT_RQQ,
+ MPT_RQC,
+ MPT_AHY,
+ MPT_AHYSpare,
+ MPT_AHYX,
+ MPT_AHYP,
+ MPT_AHYQ,
+ MPT_AHYC,
+ MPT_MARK,
+ MPT_MAINT,
+ MPT_CLEAR,
+ MPT_MOVE,
+ MPT_BCAST0,
+ MPT_BCAST1,
+ MPT_BCAST2,
+ MPT_BCAST3,
+ MPT_BCAST4,
+ MPT_BCAST5,
+ MPT_SAMO,
+ MPT_SAMIU,
+ MPT_SAMIS,
+ MPT_HEAD,
+ MPT_RQD,
+ MPT_AHYD,
+ MPT_GTT,
+ MPT_DRUGI,
+ MPT_DACKD,
+ MPT_DACK_DAL,
+ MPT_DACK_DALG,
+ MPT_DACK_DALN,
+ MPT_DACK_GO,
+ MPT_DACKZ,
+ MPT_DAHY,
+ MPT_DAHYZ,
+ MPT_DAHYX,
+ MPT_RLA,
+ MPT_DRQG,
+ MPT_DRQZ,
+ MPT_DRQX,
+ MPT_SACK,
+ MPT_SITH_I,
+ MPT_SITH_G,
+ MPT_START_SYNC,
+ MPT_CCSC,
+ MPT_START_SYNT,
+ MPT_ACKT_DT1,
+ MPT_ACKT_DT2,
+ MPT_ACKT_DT3,
+ MPT_ACKT_DT4,
+ MPT_AHY_DT,
+ MPT_AHYQ_DT,
+ MPT_SAMIS_DT,
+ MPT_HEAD_DT,
+ MPT_AHYD_DT,
+ MPT_SACK_DT,
+ _NUM_MPT_DEFINITIONS
+};
+
+enum mpt1327_parameters {
+ MPT_CONSTANT = 0,
+ MPT_PFIX,
+ MPT_IDENT1,
+ MPT_D,
+ MPT_CHAN,
+ MPT_IDENT2,
+ MPT_N,
+ MPT_P,
+ MPT_CAT,
+ MPT_TYPE,
+ MPT_FUNC,
+ MPT_CHAN4,
+ MPT_WT,
+ MPT_RSVD,
+ MPT_M,
+ MPT_QUAL,
+ MPT_DT,
+ MPT_LEVEL,
+ MPT_EXT,
+ MPT_FLAG1,
+ MPT_FLAG2,
+ MPT_PARAMETERS,
+ MPT_SD,
+ MPT_DIV,
+ MPT_INFO,
+ MPT_STATUS,
+ MPT_SLOTS,
+ MPT_POINT,
+ MPT_CHECK,
+ MPT_E,
+ MPT_AD,
+ MPT_DESC,
+ MPT_A,
+ MPT_B,
+ MPT_SPARE,
+ MPT_REVS,
+ MPT_OPER,
+ MPT_SYS,
+ MPT_CONT,
+ MPT_SYSDEF,
+ MPT_PER,
+ MPT_IVAL,
+ MPT_PON,
+ MPT_ID,
+ MPT_ADJSITE,
+ MPT_SOL,
+ MPT_LEN,
+ MPT_PREFIX2,
+ MPT_KIND,
+ MPT_PORT,
+ MPT_FAD,
+ MPT_INTER,
+ MPT_HADT,
+ MPT_MODEM,
+ MPT_O_R,
+ MPT_RATE,
+ MPT_TRANS,
+ MPT_RNITEL,
+ MPT_TNITEL,
+ MPT_JOB,
+ MPT_REASON,
+ MPT_ATRANS,
+ MPT_EFLAGS,
+ MPT_TASK,
+ MPT_ONES,
+ MPT_ITENUM,
+ MPT_USERDATA,
+ MPT_I_G,
+ MPT_MORE,
+ MPT_LASTBIT,
+ MPT_FRAGL,
+ MPT_RTRANS,
+ MPT_W_F,
+ MPT_P_N,
+ MPT_DN,
+ MPT_SPRE,
+ MPT_SX,
+ MPT_CAUSE,
+ MPT_I_T,
+ MPT_RESP,
+ MPT_TOC,
+ MPT_CCS,
+ MPT_LET,
+ MPT_PREAMBLE,
+ MPT_PARAMETERS1,
+ MPT_PARAMETERS2,
+ MPT_BCD11,
+ MPT_RSA,
+ MPT_FCW,
+ MPT_SP,
+ MPT_EXCHANGE,
+ MPT_Number,
+ MPT_GF,
+ MPT_PFIXT,
+ MPT_IDENTT,
+ MPT_FORM,
+ MPT_PFIX2,
+ _NUM_MPT_PARAMETERS
+};
+
+extern char *mpt1327_bcd;
+
+typedef struct mpt1327_codeword {
+ enum mpt1327_codeword_dir dir;
+ enum mpt1327_codeword_type type;
+ const char *short_name, *long_name;
+ uint64_t params[_NUM_MPT_PARAMETERS];
+} mpt1327_codeword_t;
+
+void init_codeword(void);
+uint16_t mpt1327_checkbits(uint64_t bits, uint16_t *parityp);
+uint64_t mpt1327_encode_codeword(mpt1327_codeword_t *codeword);
+int mpt1327_decode_codeword(mpt1327_codeword_t *codeword, int specific, enum mpt1327_codeword_dir dir, uint64_t bits);
+
diff --git a/src/mpt1327/mpt1327.c b/src/mpt1327/mpt1327.c
new file mode 100755
index 0000000..8d44fee
--- /dev/null
+++ b/src/mpt1327/mpt1327.c
@@ -0,0 +1,1661 @@
+/* protocol handling
+ *
+ * (C) 2021 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * How code-word scheduling works on downlink (tx_sched):
+ *
+ * The DSP mode can be set to CONTROL or TRAFFIC, depending on the mode the
+ * channel is working. Depending on that, a function is called for every
+ * code-word to be transmitted, one to schedule code-words on control channel,
+ * one to schedule code-words on traffic channel.
+ *
+ * The scheduler uses a state that indicates what was last scheduled, i.e. what
+ * is currently transmitted. If nothing is scheduled yet, an IDLE state is set.
+ * When switching between CONTROL and TRAFFIC mode, the different states (for
+ * each mode) are handled as they would be IDLE state, so that no reset to IDLE
+ * state is required when changing DSP mode.
+ *
+ * An IDLE state results in a startup sequence on control channel (SYNC) or on
+ * traffic channel (SYNT), whenever a message must be scheduled. The message to
+ * be scheduled depends on the unit states. All units are queried for any
+ * message to be scheduled. If no message on control channel need to be
+ * scheduled, an ALH message is scheduled, so that random access is possible.
+ * On control channel the address conde-words alternate with CCSC code-word.
+ *
+ * To prevent random access when a unit is requested to transmit more than two
+ * code-word, a dummy frame counter is set. Then a dummy AHY message is
+ * scheduled, to prevent random access by other units in that slot.
+ */
+
+/*
+ * How code-word scheduling works on uplink (rx_sched):
+ *
+ * The DSP mode can be set to CONTROL or TRAFFIC, depending on the mode the
+ * channel is working. Depending on that, a function is called for every
+ * code-word received, one for code-words on control channel, one for
+ * code-words on traffic channel.
+ *
+ * Most messages have an address code-word only, so the message type is defined
+ * by the elements in the code-word. Additional data code-words (that may
+ * follow an address code-word) do not have a message type, because they are
+ * defined by the previous address code-word. If a message has additional data
+ * code-words, a data word counter is set, so that subsequent data code-words
+ * are parsed as defined by the address code-word. In case of a CRC error, the
+ * message resets into un-synced state, i.e. waiting for next sync + address
+ * code-word.
+ */
+
+#define CHAN mpt1327->sender.kanal
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <inttypes.h>
+#include "../libsample/sample.h"
+#include "../libdebug/debug.h"
+#include "../libtimer/timer.h"
+#include "../libmobile/call.h"
+#include "../libmobile/cause.h"
+#include "../libosmocc/message.h"
+#include "mpt1327.h"
+#include "dsp.h"
+#include "message.h"
+
+/* Timers and counters */
+#define RESPONSE_TIMEOUT 1.0
+#define REPEAT_GTC 1
+#define REPEAT_AHY 1
+#define REPEAT_AHYC 1
+#define REPEAT_AHYX 3
+#define REPEAT_CLEAR 3
+
+/* Sysdef
+ *
+ */
+
+static mpt1327_sysdef_t sysdef;
+
+void init_sysdef (uint16_t sys, int wt, int per, int pon, int timeout)
+{
+ memset(&sysdef, 0, sizeof(sysdef));
+
+ sysdef.sys = sys;
+ sysdef.wt = wt;
+ sysdef.per = per;
+ sysdef.pon = pon;
+ sysdef.timeout = timeout;
+ sysdef.framelength = 3;
+ sysdef.bcast_slots = 10; /* every seconds is good */
+}
+
+/*
+ * Units handling
+ */
+
+static mpt1327_unit_t *unit_list = NULL;
+
+#define UNIT_IDLE 0
+#define UNIT_REGISTER_ACK (1 << 0) /* need to ack registration */
+#define UNIT_DIVERSION_REJ (1 << 1) /* need to nack diversion */
+#define UNIT_CALLING_REJ (1 << 2) /* need to reject call */
+#define UNIT_CALLING_AHYC (1 << 3) /* need to request SAMIS */
+#define UNIT_CALLING_SAMIS (1 << 4) /* wait for SAMIS response */
+#define UNIT_CALLED_AHY (1 << 5) /* need to request ACK */
+#define UNIT_CALLED_AHYX (1 << 6) /* cancel call */
+#define UNIT_CALLED_ACK (1 << 7) /* wait foro AHY response */
+#define UNIT_GTC_P (1 << 8) /* need to assign channel (same prefix) */
+#define UNIT_GTC_A (1 << 9) /* need to assign channel (calling unit) */
+#define UNIT_GTC_B (1 << 10) /* need to assign channel (called unit) */
+#define UNIT_CALL (1 << 11) /* established call */
+#define UNIT_CALL_CLEAR (1 << 12) /* established call */
+#define UNIT_CANCEL_ACK (1 << 13) /* need to ack cancelation */
+
+const char *unit_state_name(uint64_t state)
+{
+ static char invalid[32];
+
+ switch (state) {
+ case UNIT_IDLE:
+ return "IDLE";
+ case UNIT_REGISTER_ACK:
+ return "REGISTER-ACK";
+ case UNIT_DIVERSION_REJ:
+ return "DIVERSION-REJ";
+ case UNIT_CALLING_REJ:
+ return "CALLING-REJ";
+ case UNIT_CALLING_AHYC:
+ return "CALLING-AHYC";
+ case UNIT_CALLING_SAMIS:
+ return "CALLING-SAMIS";
+ case UNIT_CALLED_AHY:
+ return "CALLED-AHY";
+ case UNIT_CALLED_AHYX:
+ return "CALLED-AHYX";
+ case UNIT_CALLED_ACK:
+ return "CALLED-ACK";
+ case UNIT_GTC_P:
+ return "GTC-BOTH";
+ case UNIT_GTC_A:
+ return "GTC-OTHER-UNIT";
+ case UNIT_GTC_B:
+ return "GTC-UNIT";
+ case UNIT_CALL:
+ return "CALL";
+ case UNIT_CALL_CLEAR:
+ return "CALL-CLEAR";
+ case UNIT_CANCEL_ACK:
+ return "CANCEL-ACK";
+ }
+
+ sprintf(invalid, "invalid(0x%" PRIx64 ")", state);
+ return invalid;
+}
+
+void unit_new_state(mpt1327_unit_t *unit, uint64_t new_state)
+{
+ PDEBUG(DMPT1327, DEBUG_DEBUG, "Radio Unit (Prefix:%d Ident:%d) state: %s -> %s\n", unit->prefix, unit->ident, unit_state_name(unit->state), unit_state_name(new_state));
+ unit->state = new_state;
+}
+
+static void unit_timeout(struct timer *timer);
+
+mpt1327_unit_t *get_unit(uint8_t prefix, uint16_t ident)
+{
+ mpt1327_unit_t **unitp;
+
+ for (unitp = &unit_list; *unitp; unitp = &((*unitp)->next)) {
+ if ((*unitp)->prefix == prefix
+ && (*unitp)->ident == ident)
+ break;
+ }
+
+ if (!(*unitp)) {
+ PDEBUG(DDB, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) added to database\n", prefix, ident);
+ *unitp = calloc(1, sizeof(mpt1327_unit_t));
+ timer_init(&(*unitp)->timer, unit_timeout, (*unitp));
+ (*unitp)->state = UNIT_IDLE;
+ (*unitp)->prefix = prefix;
+ (*unitp)->ident = ident;
+ }
+
+ return *unitp;
+}
+
+mpt1327_unit_t *find_unit_state(uint32_t state, mpt1327_t *tc)
+{
+ mpt1327_unit_t *unit;
+
+ for (unit = unit_list; unit; unit = unit->next) {
+ if (tc && unit->tc != tc)
+ continue;
+ if ((unit->state & state))
+ break;
+ }
+
+ return unit;
+}
+
+mpt1327_unit_t *find_unit_callref(uint32_t callref)
+{
+ mpt1327_unit_t *unit;
+
+ for (unit = unit_list; unit; unit = unit->next) {
+ if ((unit->callref & callref))
+ break;
+ }
+
+ return unit;
+}
+
+static void mpt1327_go_idle(mpt1327_t *mpt1327);
+static void mpt1327_release(mpt1327_unit_t *unit);
+
+/* Timeout handling */
+static void unit_timeout(struct timer *timer)
+{
+ mpt1327_unit_t *unit = (mpt1327_unit_t *)timer->priv;
+
+ // FIXME: do some retry
+ switch (unit->state) {
+ case UNIT_CALLING_SAMIS:
+ if (unit->repeat) {
+ --unit->repeat;
+ PDEBUG(DMPT1327, DEBUG_INFO, "Resend AHYC, because unit timed out.\n");
+ unit_new_state(unit, UNIT_CALLING_AHYC);
+ break;
+ }
+ PDEBUG(DMPT1327, DEBUG_INFO, "Unit failed to respond to AHYC, releasing...\n");
+ mpt1327_release(unit);
+ if (unit->callref) {
+ call_up_release(unit->callref, CAUSE_NORMAL);
+ unit->callref = 0;
+ }
+ break;
+ case UNIT_CALLED_ACK:
+ if (unit->repeat) {
+ --unit->repeat;
+ PDEBUG(DMPT1327, DEBUG_INFO, "Resend AHY, because unit timed out.\n");
+ unit_new_state(unit, UNIT_CALLED_AHY);
+ break;
+ }
+ PDEBUG(DMPT1327, DEBUG_INFO, "Unit failed to respond to AHY, releasing...\n");
+ mpt1327_release(unit);
+ if (unit->callref) {
+ call_up_release(unit->callref, CAUSE_NORMAL);
+ unit->callref = 0;
+ }
+ break;
+ case UNIT_CALL:
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "Release call, because unit timed out.\n");
+ mpt1327_release(unit);
+ if (unit->callref) {
+ call_up_release(unit->callref, CAUSE_NORMAL);
+ unit->callref = 0;
+ }
+ break;
+ default:
+ PDEBUG(DMPT1327, DEBUG_ERROR, "Unknown timeout at state 0x%" PRIx64 ", please fix!\n", unit->state);
+ break;
+ }
+
+}
+
+void flush_units(void)
+{
+ mpt1327_unit_t *next;
+
+ while (unit_list) {
+ next = unit_list->next;
+ timer_exit(&unit_list->timer);
+ free(unit_list);
+ unit_list = next;
+ }
+}
+
+void dump_units(void)
+{
+ mpt1327_unit_t *unit = unit_list;
+
+ PDEBUG(DDB, DEBUG_NOTICE, "Dump of Radio Unit list:\n");
+ if (!unit) {
+ PDEBUG(DDB, DEBUG_NOTICE, " - No Radio Unit seen yet!\n");
+ return;
+ }
+
+ while (unit) {
+ PDEBUG(DDB, DEBUG_NOTICE, " - Radio Unit (Prefix:%d Ident:%d) seen on this TSC.\n", unit->prefix, unit->ident);
+ unit = unit->next;
+ }
+}
+
+/*
+ * bands and channels
+ */
+
+static struct mpt1327_band_def {
+ const char *name;
+ const char *description;
+} mpt1327_band_def[] = {
+ { "MPT1343/1", "MPT1343 Sub Band 1"},
+ { "MPT1343/2", "MPT1343 Sub Band 2"},
+ { "Regionet43/1", "Regionet43 410-430 MHz (German band)" },
+ { "Regionet43/2", "Regionet43 445-448 MHz (rarely used in Germany)" },
+ { NULL, NULL }
+};
+
+const char *mpt1327_band_name(enum mpt1327_band band)
+{
+ return mpt1327_band_def[band].name;
+}
+
+int mpt1327_band_by_short_name(const char *name)
+{
+ int i;
+
+ for (i = 0; mpt1327_band_def[i].name; i++) {
+ if (!strcasecmp(mpt1327_band_def[i].name, name))
+ return i;
+ }
+
+ return -1;
+}
+
+void mpt1327_band_list(void)
+{
+ int i;
+
+ printf("Name\t\tDescription\n");
+ printf("------------------------------------------------------------------------\n");
+ for (i = 0; mpt1327_band_def[i].name; i++)
+ printf("%s\t%s\n", mpt1327_band_def[i].name, mpt1327_band_def[i].description);
+}
+
+/* convert channel to frequency */
+double mpt1327_channel2freq(enum mpt1327_band band, int channel, int uplink)
+{
+ double freq = 0, offset = 0; // make GCC happy
+ int channels = 0;
+
+ switch(band) {
+ case BAND_MPT1343_SUB1:
+ freq = 177.2125;
+ offset = 8.0; /* that's right! */
+ channel -= 58;
+ channels = 503;
+ break;
+ case BAND_MPT1343_SUB2:
+ freq = 201.2125;
+ offset = -8.0;
+ channel -= 58;
+ channels = 503;
+ break;
+ case BAND_REGIONET43_SUB1:
+ freq = 420.0125;
+ offset = -10.0;
+ channel -= 1;
+ channels = 799;
+ break;
+ case BAND_REGIONET43_SUB2:
+ freq = 445.0125;
+ offset = -5.0;
+ channel -= 1;
+ channels = 239;
+ break;
+ }
+
+ /* channel out of range */
+ if (channel < 0 || channel > channels)
+ return 0.0;
+
+ if (uplink == 2)
+ return offset * 1e6;
+
+ freq += channel * 0.0125;
+ if (uplink)
+ freq += offset;
+
+ return freq * 1e6;
+}
+
+/* convert channel to chan field */
+uint16_t mpt1327_channel2chan(enum mpt1327_band band, int channel)
+{
+ uint16_t chan = 0;
+
+ switch(band) {
+ case BAND_MPT1343_SUB1:
+ chan = channel - 58 + 513;
+ break;
+ case BAND_MPT1343_SUB2:
+ chan = channel - 58 + 1;
+ break;
+ case BAND_REGIONET43_SUB1:
+ chan = channel - 1 + 1;
+ break;
+ case BAND_REGIONET43_SUB2:
+ chan = channel - 1 + 1; // works with DETEWE
+ break;
+ }
+
+ return chan;
+}
+
+static struct mpt1327_channels {
+ enum mpt1327_chan_type chan_type;
+ const char *short_name;
+ const char *long_name;
+} mpt1327_channels[] = {
+ { CHAN_TYPE_CC, "CC", "control channel" },
+ { CHAN_TYPE_TC, "TC", "traffic channel" },
+ { CHAN_TYPE_CC_TC, "CC/TC","combined control & traffic channel" },
+ { 0, NULL, NULL }
+};
+
+void mpt1327_channel_list(void)
+{
+ int i;
+
+ printf("Type\t\tDescription\n");
+ printf("------------------------------------------------------------------------\n");
+ for (i = 0; mpt1327_channels[i].long_name; i++)
+ printf("%s%s\t%s\n", mpt1327_channels[i].short_name, (strlen(mpt1327_channels[i].short_name) >= 8) ? "" : "\t", mpt1327_channels[i].long_name);
+}
+
+int mpt1327_channel_by_short_name(const char *short_name)
+{
+ int i;
+
+ for (i = 0; mpt1327_channels[i].short_name; i++) {
+ if (!strcasecmp(mpt1327_channels[i].short_name, short_name)) {
+ PDEBUG(DMPT1327, DEBUG_INFO, "Selecting channel '%s' = %s\n", mpt1327_channels[i].short_name, mpt1327_channels[i].long_name);
+ return mpt1327_channels[i].chan_type;
+ }
+ }
+
+ return -1;
+}
+
+const char *chan_type_short_name(enum mpt1327_chan_type chan_type)
+{
+ int i;
+
+ for (i = 0; mpt1327_channels[i].short_name; i++) {
+ if (mpt1327_channels[i].chan_type == chan_type)
+ return mpt1327_channels[i].short_name;
+ }
+
+ return "invalid";
+}
+
+const char *chan_type_long_name(enum mpt1327_chan_type chan_type)
+{
+ int i;
+
+ for (i = 0; mpt1327_channels[i].long_name; i++) {
+ if (mpt1327_channels[i].chan_type == chan_type)
+ return mpt1327_channels[i].long_name;
+ }
+
+ return "invalid";
+}
+
+/*
+ * MPT processing
+ */
+
+static mpt1327_t *search_free_tc(void)
+{
+ sender_t *sender;
+ mpt1327_t *tc, *cc_tc = NULL;
+
+ for (sender = sender_head; sender; sender = sender->next) {
+ tc = (mpt1327_t *) sender;
+ if (tc->state != STATE_IDLE)
+ continue;
+ /* remember combined voice/control/paging channel as second alternative */
+ if (tc->chan_type == CHAN_TYPE_CC_TC)
+ cc_tc = tc;
+ if (tc->chan_type == CHAN_TYPE_TC)
+ return tc;
+ }
+
+ return cc_tc;
+
+}
+
+static mpt1327_t *search_cc(void)
+{
+ sender_t *sender;
+ mpt1327_t *cc = NULL;
+
+ for (sender = sender_head; sender; sender = sender->next) {
+ cc = (mpt1327_t *) sender;
+ /* remember combined voice/control/paging channel as second alternative */
+ if (cc->chan_type == CHAN_TYPE_CC_TC)
+ return cc;
+ if (cc->chan_type == CHAN_TYPE_CC)
+ return cc;
+ }
+
+ return NULL;
+
+}
+
+const char *mpt1327_state_name(enum mpt1327_state state)
+{
+ static char invalid[16];
+
+ switch (state) {
+ case STATE_NULL:
+ return "(NULL)";
+ case STATE_IDLE:
+ return "IDLE";
+ case STATE_BUSY:
+ return "BUSY";
+ }
+
+ sprintf(invalid, "invalid(%d)", state);
+ return invalid;
+}
+
+void mpt1327_display_status(void)
+{
+ sender_t *sender;
+ mpt1327_t *mpt1327;
+
+ display_status_start();
+ for (sender = sender_head; sender; sender = sender->next) {
+ mpt1327 = (mpt1327_t *) sender;
+ display_status_channel(mpt1327->sender.kanal, chan_type_short_name(mpt1327->chan_type), mpt1327_state_name(mpt1327->state));
+ if (mpt1327->unit) {
+ char unit_id[32];
+ sprintf(unit_id, "%d/%d", mpt1327->unit->prefix, mpt1327->unit->ident);
+ display_status_subscriber(unit_id, NULL);
+ }
+ }
+ display_status_end();
+}
+
+static void mpt1327_new_state(mpt1327_t *mpt1327, enum mpt1327_state new_state, mpt1327_unit_t *unit)
+{
+ if (mpt1327->state == new_state)
+ return;
+ PDEBUG_CHAN(DMPT1327, DEBUG_DEBUG, "State change: %s -> %s\n", mpt1327_state_name(mpt1327->state), mpt1327_state_name(new_state));
+
+ /* unlink unit, if linked */
+ if (mpt1327->unit) {
+ mpt1327->unit->tc = NULL;
+ mpt1327->unit = NULL;
+ }
+
+ /* link unit, if given */
+ if (unit) {
+ unit->tc = mpt1327;
+ mpt1327->unit = unit;
+ }
+
+ mpt1327->state = new_state;
+ mpt1327_display_status();
+}
+
+static void mpt1327_timeout(struct timer *timer);
+
+/* Create transceiver instance and link to a list. */
+int mpt1327_create(enum mpt1327_band band, const char *kanal, enum mpt1327_chan_type chan_type, const char *audiodev, int use_sdr, int samplerate, double rx_gain, double tx_gain, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback, double squelch_db)
+{
+ sender_t *sender;
+ mpt1327_t *mpt1327;
+ int rc;
+
+ /* check channel matching and set deviation factor */
+ if (mpt1327_channel2freq(band, atoi(kanal), 0) == 0.0)
+ return -EINVAL;
+
+ for (sender = sender_head; sender; sender = sender->next) {
+ mpt1327 = (mpt1327_t *)sender;
+ if ((mpt1327->chan_type == CHAN_TYPE_CC || mpt1327->chan_type == CHAN_TYPE_CC_TC)
+ && (chan_type == CHAN_TYPE_CC || chan_type == CHAN_TYPE_CC_TC)) {
+ PDEBUG(DCNETZ, DEBUG_NOTICE, "More than one control channel is not supported, please define other channels as traffic channels!\n");
+ return -EINVAL;
+ }
+ }
+ mpt1327 = calloc(1, sizeof(mpt1327_t));
+ if (!mpt1327) {
+ PDEBUG(DMPT1327, DEBUG_ERROR, "No memory!\n");
+ return -EIO;
+ }
+
+ PDEBUG(DMPT1327, DEBUG_DEBUG, "Creating 'MPT1327' instance for Channel %s on Band %s (sample rate %d).\n", kanal, mpt1327_band_def[band].name, samplerate);
+
+ /* init general part of transceiver */
+ rc = sender_create(&mpt1327->sender, kanal, mpt1327_channel2freq(band, atoi(kanal), 0), mpt1327_channel2freq(band, atoi(kanal), 1), audiodev, use_sdr, samplerate, rx_gain, tx_gain, 0, 0, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, PAGING_SIGNAL_NONE);
+ if (rc < 0) {
+ PDEBUG(DMPT1327, DEBUG_ERROR, "Failed to init 'Sender' processing!\n");
+ goto error;
+ }
+
+ /* init audio processing */
+ rc = dsp_init_sender(mpt1327, squelch_db);
+ if (rc < 0) {
+ PDEBUG(DANETZ, DEBUG_ERROR, "Failed to init signal processing!\n");
+ goto error;
+ }
+
+ /* timers */
+ timer_init(&mpt1327->timer, mpt1327_timeout, mpt1327);
+
+ mpt1327->band = band;
+ mpt1327->chan_type = chan_type;
+
+ /* only accept these valued */
+ if (sysdef.framelength != 1 && sysdef.framelength != 3 && sysdef.framelength != 6) {
+ PDEBUG(DMPT1327, DEBUG_ERROR, "Invalid frame length %d, please fix!\n", sysdef.framelength);
+ abort();
+ }
+ if (sysdef.wt != 5 && sysdef.wt != 10 && sysdef.wt != 15) {
+ PDEBUG(DMPT1327, DEBUG_ERROR, "Invalid WT value %d, please fix!\n", sysdef.wt);
+ abort();
+ }
+
+ /* go into idle state */
+ mpt1327_go_idle(mpt1327);
+
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "Created channel #%s of type '%s' = %s\n", kanal, chan_type_short_name(chan_type), chan_type_long_name(chan_type));
+
+ return 0;
+
+error:
+ mpt1327_destroy(&mpt1327->sender);
+
+ return rc;
+}
+
+void mpt1327_check_channels(void)
+{
+ sender_t *sender;
+ mpt1327_t *mpt1327;
+ int cc = 0, tc = 0;
+ int note = 0;
+
+ for (sender = sender_head; sender; sender = sender->next) {
+ mpt1327 = (mpt1327_t *) sender;
+ if (mpt1327->chan_type == CHAN_TYPE_CC)
+ cc = 1;
+ if (mpt1327->chan_type == CHAN_TYPE_TC)
+ tc = 1;
+ if (mpt1327->chan_type == CHAN_TYPE_CC_TC) {
+ cc = 1;
+ tc = 1;
+ }
+ }
+ if (cc && !tc) {
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "\n");
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "*** Selected channel(s) can be used for control only.\n");
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "*** No call is possible.\n");
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "*** Use at least one 'TC'!\n");
+ note = 1;
+ }
+ if (tc && !cc) {
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "\n");
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "*** Selected channel(s) can be used for traffic only.\n");
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "*** No call to the mobile phone is possible.\n");
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "*** Use one 'CC'!\n");
+ note = 1;
+ }
+ if (note)
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "\n");
+}
+
+/* Destroy transceiver instance and unlink from list. */
+void mpt1327_destroy(sender_t *sender)
+{
+ mpt1327_t *mpt1327 = (mpt1327_t *) sender;
+
+ PDEBUG(DMPT1327, DEBUG_DEBUG, "Destroying 'MPT1327' instance for channel = %s.\n", sender->kanal);
+
+ dsp_cleanup_sender(mpt1327);
+ timer_exit(&mpt1327->timer);
+ sender_destroy(&mpt1327->sender);
+ free(sender);
+}
+
+/* Abort connection towards mobile station changing to IDLE state */
+static void mpt1327_go_idle(mpt1327_t *mpt1327)
+{
+ timer_stop(&mpt1327->timer);
+ mpt1327->pressel_on = 0;
+
+ PDEBUG(DMPT1327, DEBUG_INFO, "Entering IDLE state on channel %s.\n", mpt1327->sender.kanal);
+ mpt1327_new_state(mpt1327, STATE_IDLE, NULL);
+ memset(&mpt1327->tx_sched, 0, sizeof(mpt1327->tx_sched));
+ switch (mpt1327->chan_type) {
+ case CHAN_TYPE_CC:
+ case CHAN_TYPE_CC_TC:
+ mpt1327->tx_sched.state = SCHED_STATE_CC_IDLE;
+ mpt1327_set_dsp_mode(mpt1327, DSP_MODE_CONTROL, 0);
+ break;
+ case CHAN_TYPE_TC:
+ mpt1327->tx_sched.state = SCHED_STATE_TC_IDLE;
+ mpt1327_set_dsp_mode(mpt1327, DSP_MODE_OFF, 0);
+ break;
+ }
+}
+
+static void mpt1327_release(mpt1327_unit_t *unit)
+{
+ timer_stop(&unit->timer);
+
+ if (unit->state == UNIT_CALL && unit->tc) {
+ /* release all units on traffic channel */
+ unit_new_state(unit, UNIT_CALL_CLEAR);
+ unit->repeat = REPEAT_CLEAR;
+ } else {
+ /* release unit on control channel */
+ unit_new_state(unit, UNIT_CALLED_AHYX);
+ unit->repeat = REPEAT_AHYX;
+ }
+}
+
+static int gtc_aloha_number(int length)
+{
+ switch (length) {
+ case 1:
+ return 1;
+ case 3:
+ return 2;
+ case 6:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+/* schedule message on control channel
+ *
+ * on IDLE state a STARTUP sequence is sent, followed by alternating CCSC and
+ * ADDR codewords. if a unit has nothing to send, ALH is transmitted. if a
+ * unit wants to send a message, the message is scheduled. this message can
+ * be repeated. afterwards, if no unit has something to send, the ALH is
+ * scheduled.
+ *
+ * a dummy slot is used to allow radio unit to allow multi slot response to a
+ * request from TSC.
+ */
+int mpt1327_send_codeword_control(mpt1327_t *mpt1327, mpt1327_codeword_t *codeword)
+{
+ mpt1327_unit_t *unit;
+
+ /* CC scheduler, the sched_state is what we have sent */
+ switch (mpt1327->tx_sched.state) {
+ case SCHED_STATE_CC_ADDR:
+ codeword->type = MPT_CCSC;
+ codeword->params[MPT_SYS] = sysdef.sys;
+ mpt1327->tx_sched.state = SCHED_STATE_CC_CCSC;
+ break;
+ case SCHED_STATE_CC_STARTUP:
+ case SCHED_STATE_CC_CCSC:
+ /* count slots for each broadcast */
+ if (mpt1327->tx_sched.bcast_count < sysdef.bcast_slots) {
+ mpt1327->tx_sched.bcast_count++;
+ }
+ /* count slots in frame */
+ if (!mpt1327->tx_sched.frame_length || mpt1327->tx_sched.frame_count == mpt1327->tx_sched.frame_length) {
+ mpt1327->tx_sched.frame_length = sysdef.framelength;
+ mpt1327->tx_sched.frame_count = 0;
+ }
+ /* send out a dummy slot to prevent random access from other units */
+ if (mpt1327->tx_sched.dummy_slot) {
+ mpt1327->tx_sched.dummy_slot--;
+ /* if this is a new frame, make it 1 slot long */
+ if (mpt1327->tx_sched.frame_count == 0)
+ mpt1327->tx_sched.frame_length = 1;
+ codeword->type = MPT_AHY;
+ codeword->params[MPT_PFIX] = 0x2a; /* just some alternating pattern (ignored) */
+ codeword->params[MPT_IDENT1] = IDENT_DUMMYI;
+ codeword->params[MPT_IDENT2] = IDENT_DUMMYI;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending dummy AHY, to prevent random access while receiving SAMIS\n");
+ } else {
+ unit = find_unit_state(UNIT_REGISTER_ACK | UNIT_DIVERSION_REJ | UNIT_CALLING_REJ | UNIT_CALLING_AHYC | UNIT_CALLED_AHY | UNIT_GTC_P | UNIT_GTC_A | UNIT_GTC_B | UNIT_CANCEL_ACK | UNIT_CALLED_AHYX, NULL);
+ if (!unit) {
+ /* if we reached the slot count for broadcast message
+ * AND the frame is not frame 0 (with given length, as long es frame length > 1)
+ * if the frame length is 1 (implies count == 0), then make this message single slot frame
+ */
+ if (mpt1327->tx_sched.bcast_count == sysdef.bcast_slots
+ && (mpt1327->tx_sched.frame_count > 0 || mpt1327->tx_sched.frame_length == 1)) {
+ mpt1327->tx_sched.bcast_count = 0;
+ /* if this is a new frame, make it 1 slot long */
+ if (mpt1327->tx_sched.frame_count == 0)
+ mpt1327->tx_sched.frame_length = 1;
+ codeword->type = MPT_BCAST2;
+ codeword->params[MPT_SYS] = sysdef.sys;
+ codeword->params[MPT_IVAL] = sysdef.per;
+ if (!sysdef.per)
+ codeword->params[MPT_PER] = 1;
+ if (!sysdef.pon)
+ codeword->params[MPT_PON] = 1;
+ } else {
+ codeword->type = MPT_ALH;
+ codeword->params[MPT_PFIX] = 0x2a; /* just some alternating pattern (ignored) */
+ codeword->params[MPT_IDENT1] = 0x1555; /* dito */
+ codeword->params[MPT_CHAN4] = mpt1327_channel2chan(mpt1327->band, atoi(mpt1327->sender.kanal)) & 0xf;
+ codeword->params[MPT_WT] = (sysdef.wt < 5) ? sysdef.wt : sysdef.wt / 5 + 4;
+ }
+ } else switch (unit->state) {
+ case UNIT_REGISTER_ACK: /* ack to register */
+ codeword->type = MPT_ACK;
+ codeword->params[MPT_PFIX] = unit->prefix;
+ codeword->params[MPT_IDENT1] = IDENT_REGI;
+ codeword->params[MPT_IDENT2] = unit->ident;
+ codeword->params[MPT_QUAL] = 0;
+ unit_new_state(unit, UNIT_IDLE);
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending acknowledge to Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
+ break;
+ case UNIT_DIVERSION_REJ: /* reject diversion */
+ codeword->type = MPT_ACKX;
+ codeword->params[MPT_PFIX] = unit->prefix;
+ codeword->params[MPT_IDENT1] = unit->called_ident;
+ codeword->params[MPT_IDENT2] = unit->ident;
+ codeword->params[MPT_QUAL] = 0;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending negative acknowledge to Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
+ if (unit->repeat) {
+ --unit->repeat;
+ break;
+ }
+ unit_new_state(unit, UNIT_IDLE);
+ if (unit->tc)
+ mpt1327_go_idle(unit->tc);
+ break;
+ case UNIT_CALLING_REJ: /* outgoing call rejected, no channel available */
+ codeword->type = MPT_ACKX;
+ codeword->params[MPT_PFIX] = unit->prefix;
+ codeword->params[MPT_IDENT1] = unit->called_ident;
+ codeword->params[MPT_IDENT2] = unit->ident;
+ codeword->params[MPT_QUAL] = 1;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending negative acknowledge to Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
+ if (unit->repeat) {
+ --unit->repeat;
+ break;
+ }
+ unit_new_state(unit, UNIT_IDLE);
+ if (unit->tc)
+ mpt1327_go_idle(unit->tc);
+ break;
+ case UNIT_CALLING_AHYC: /* request SAMIS for dialing data */
+ codeword->type = MPT_AHYC;
+ codeword->params[MPT_PFIX] = unit->prefix;
+ codeword->params[MPT_IDENT1] = unit->called_ident;
+ codeword->params[MPT_IDENT2] = unit->ident; /* implies Mode 1 */
+ switch (unit->called_type) {
+ case CALLED_TYPE_INTERPFX:
+ codeword->params[MPT_SLOTS] = 0x1;
+ codeword->params[MPT_DESC] = 0x0;
+ break;
+ case CALLED_TYPE_PSTN_LONG1:
+ codeword->params[MPT_SLOTS] = 0x1;
+ codeword->params[MPT_DESC] = 0x1;
+ break;
+ case CALLED_TYPE_PSTN_LONG2:
+ codeword->params[MPT_SLOTS] = 0x2;
+ codeword->params[MPT_DESC] = 0x1;
+ mpt1327->tx_sched.dummy_slot = 1;
+ break;
+ case CALLED_TYPE_PBX_LONG:
+ codeword->params[MPT_SLOTS] = 0x1;
+ codeword->params[MPT_DESC] = 0x2;
+ break;
+ default:
+ PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Want to send AHYC, but called_type not set correctly, please fix!\n");
+ abort();
+ }
+ unit_new_state(unit, UNIT_CALLING_SAMIS);
+ mpt1327->rx_sched.data_prefix = unit->prefix;
+ mpt1327->rx_sched.data_ident = unit->ident;
+ PDEBUG_CHAN(DMPT1327, DEBUG_DEBUG, "Starting timer, waiting for response\n");
+ timer_start(&unit->timer, RESPONSE_TIMEOUT);
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending AHYC, to request SAMIS from Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
+ break;
+ case UNIT_CALLED_AHY: /* call to unit and request ACK from unit */
+ codeword->type = MPT_AHY;
+ codeword->params[MPT_PFIX] = unit->prefix;
+ codeword->params[MPT_IDENT1] = unit->ident;
+ codeword->params[MPT_IDENT2] = IDENT_PABXI;
+ codeword->params[MPT_D] = 0; /* speech call */
+ codeword->params[MPT_POINT] = 0; /* demand ACK from ident1 */
+ codeword->params[MPT_CHECK] = 1; /* unit is in contact and accepts calls */
+ codeword->params[MPT_E] = 0; /* no emergency call */
+ codeword->params[MPT_AD] = 0; /* no appended data */
+ unit_new_state(unit, UNIT_CALLED_ACK);
+ PDEBUG_CHAN(DMPT1327, DEBUG_DEBUG, "Starting timer, waiting for response\n");
+ timer_start(&unit->timer, RESPONSE_TIMEOUT);
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending AHY, to request ACK from Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
+ break;
+ case UNIT_GTC_P: /* channel assignment to unit itself and called unit */
+ codeword->type = MPT_GTC;
+ codeword->params[MPT_PFIX] = unit->prefix;
+ codeword->params[MPT_IDENT1] = unit->called_ident;
+ codeword->params[MPT_IDENT2] = unit->ident;
+ codeword->params[MPT_CHAN] = mpt1327_channel2chan(unit->tc->band, atoi(unit->tc->sender.kanal));
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending channel assignment to calling and called Radio Units (Prefix:%d Ident:%d and Ident:%d)\n", unit->prefix, unit->ident, unit->called_ident);
+ if (unit->repeat) {
+ --unit->repeat;
+ break;
+ }
+ unit_new_state(unit, UNIT_CALL);
+ mpt1327_set_dsp_mode(unit->tc, DSP_MODE_TRAFFIC, 1);
+ if (sysdef.timeout)
+ timer_start(&unit->timer, sysdef.timeout);
+ break;
+ case UNIT_GTC_B: /* channel assignment to called unit */
+ /* NOTE GTC to called unit must be sent before GTC to calling unit (1.3.5.3) */
+ codeword->type = MPT_GTC;
+ codeword->params[MPT_PFIX] = unit->prefix;
+ codeword->params[MPT_IDENT1] = unit->called_ident;
+ codeword->params[MPT_IDENT2] = IDENT_DUMMYI;
+ codeword->params[MPT_CHAN] = mpt1327_channel2chan(unit->tc->band, atoi(unit->tc->sender.kanal));
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending channel assignment to called Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
+ if (unit->repeat) {
+ --unit->repeat;
+ break;
+ }
+ unit_new_state(unit, UNIT_GTC_A);
+ unit->repeat = REPEAT_GTC;
+ break;
+ case UNIT_GTC_A: /* channel assignment unit itself */
+ codeword->type = MPT_GTC;
+ codeword->params[MPT_PFIX] = unit->prefix;
+ codeword->params[MPT_IDENT1] = IDENT_DUMMYI;
+ codeword->params[MPT_IDENT2] = unit->ident;
+ codeword->params[MPT_CHAN] = mpt1327_channel2chan(unit->tc->band, atoi(unit->tc->sender.kanal));
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending channel assignment to calling Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
+ if (unit->repeat) {
+ --unit->repeat;
+ break;
+ }
+ unit_new_state(unit, UNIT_CALL);
+ mpt1327_set_dsp_mode(unit->tc, DSP_MODE_TRAFFIC, 1);
+ if (sysdef.timeout)
+ timer_start(&unit->timer, sysdef.timeout);
+ break;
+ case UNIT_CANCEL_ACK:
+ codeword->type = MPT_ACK;
+ codeword->params[MPT_PFIX] = unit->called_prefix;
+ codeword->params[MPT_IDENT1] = unit->called_ident;
+ codeword->params[MPT_IDENT2] = unit->ident;
+ codeword->params[MPT_QUAL] = 1;
+ unit_new_state(unit, UNIT_IDLE);
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending acknowledge to Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
+ break;
+ case UNIT_CALLED_AHYX: /* channel assignment unit itself */
+ codeword->type = MPT_AHYX;
+ codeword->params[MPT_PFIX] = unit->prefix;
+ codeword->params[MPT_IDENT1] = unit->ident;
+ codeword->params[MPT_IDENT2] = IDENT_PABXI;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending AHYX, to cancel call to Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
+ if (unit->repeat) {
+ --unit->repeat;
+ break;
+ }
+ unit_new_state(unit, UNIT_IDLE);
+ if (unit->tc)
+ mpt1327_go_idle(unit->tc);
+ break;
+ }
+ }
+ if (codeword->type == MPT_GTC)
+ codeword->params[MPT_N] = (mpt1327->tx_sched.frame_count == 0) ? gtc_aloha_number(mpt1327->tx_sched.frame_length) : 0;
+ else
+ codeword->params[MPT_N] = (mpt1327->tx_sched.frame_count == 0) ? mpt1327->tx_sched.frame_length : 0;
+ mpt1327->tx_sched.frame_count++;
+ mpt1327->tx_sched.state = SCHED_STATE_CC_ADDR;
+ break;
+ default:
+ /* on dirty state (e.g. changing from TC to CC), we start control channel framing */
+ codeword->type = MPT_START_SYNC;
+ mpt1327->tx_sched.frame_length = 0;
+ mpt1327->tx_sched.state = SCHED_STATE_CC_STARTUP;
+ break;
+ }
+
+ return 0;
+}
+
+/* schedule messages on traffic channel
+ *
+ * when the unit has a message to send, it, the repeat counter is decreased and
+ * a SYNC is sent the next request will send the an ADDR codeword. this will
+ * repeat until the repeat counter reaches 0.
+ */
+int mpt1327_send_codeword_traffic(mpt1327_t *mpt1327, mpt1327_codeword_t __attribute__((unused)) *codeword)
+{
+ mpt1327_unit_t *unit;
+ mpt1327_t *cc;
+
+ /* TC scheduler */
+ switch (mpt1327->tx_sched.state) {
+ case SCHED_STATE_TC_IDLE:
+ case SCHED_STATE_TC_ADDR:
+ /* on idle state or after sending address, we search for a unit that wants to send a message */
+ unit = find_unit_state(UNIT_CALL_CLEAR, mpt1327);
+ if (!unit) {
+ /* no message, so we have nothing to send */
+ mpt1327->tx_sched.state = SCHED_STATE_TC_IDLE;
+ return -1;
+ }
+ switch (unit->state) {
+ case UNIT_CALL_CLEAR: /* release channel */
+ if (!unit->repeat) {
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Done sending clear down on traffic channel, releasing\n");
+ unit_new_state(unit, UNIT_IDLE);
+ mpt1327_go_idle(mpt1327);
+ return -1;
+ }
+ --unit->repeat;
+ break;
+ }
+ codeword->type = MPT_START_SYNT;
+ mpt1327->tx_sched.state = SCHED_STATE_TC_SYNT;
+ break;
+ case SCHED_STATE_TC_SYNT:
+ /* after sending SYNT, we process message that unit wants to send */
+ unit = find_unit_state(UNIT_CALL_CLEAR, mpt1327);
+ if (!unit) {
+ mpt1327->tx_sched.state = SCHED_STATE_TC_IDLE;
+ return -1;
+ }
+ switch (unit->state) {
+ case UNIT_CALL_CLEAR: /* release channel */
+ codeword->type = MPT_CLEAR;
+ codeword->params[MPT_CHAN] = mpt1327_channel2chan(mpt1327->band, atoi(mpt1327->sender.kanal));
+ cc = search_cc();
+ if (cc)
+ codeword->params[MPT_CONT] = mpt1327_channel2chan(cc->band, atoi(cc->sender.kanal));
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Sending clear down on traffic channel\n");
+ }
+ mpt1327->tx_sched.state = SCHED_STATE_TC_ADDR;
+ break;
+ default:
+ /* on dirty state (e.g. changing from CC to TC), we enter idle state */
+ mpt1327->tx_sched.state = SCHED_STATE_TC_IDLE;
+ return -1;
+ }
+
+ return 0;
+}
+
+int mpt1327_send_codeword(mpt1327_t *mpt1327, uint64_t *bits)
+{
+ mpt1327_codeword_t codeword;
+ int rc = -1;
+
+ memset(&codeword, 0, sizeof(codeword));
+
+ switch (mpt1327->dsp_mode) {
+ case DSP_MODE_CONTROL:
+ rc = mpt1327_send_codeword_control(mpt1327, &codeword);
+ break;
+ case DSP_MODE_TRAFFIC:
+ rc = mpt1327_send_codeword_traffic(mpt1327, &codeword);
+ break;
+ default:
+ ;
+ }
+
+ if (rc < 0)
+ return 0;
+
+ *bits = mpt1327_encode_codeword(&codeword);
+ return 64;
+}
+
+static void out_setup(mpt1327_unit_t *unit, uint8_t network_type, int network_id)
+{
+ char caller_id[32], id[16];
+
+ /* setup call */
+ PDEBUG(DMPT1327, DEBUG_INFO, "Setup call to network.\n");
+ sprintf(caller_id, "%03d%04d", unit->prefix, unit->ident);
+ if (network_id)
+ sprintf(id, "%d", network_id);
+ else
+ id[0] = '\0';
+ unit->callref = call_up_setup(caller_id, unit->called_number, network_type, id);
+}
+
+static void _cancel_pending_call(mpt1327_t *mpt1327, mpt1327_unit_t *unit)
+{
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "We are already in a call, the phone might have restarted, so we free old channel first.\n");
+ mpt1327_go_idle(unit->tc);
+ timer_stop(&unit->timer);
+ if (unit->callref) {
+ call_up_release(unit->callref, CAUSE_NORMAL);
+ unit->callref = 0;
+ }
+}
+
+void mpt1327_receive_codeword_control(mpt1327_t *mpt1327, mpt1327_codeword_t *codeword)
+{
+ mpt1327_unit_t *unit;
+ mpt1327_t *tc;
+
+ switch (codeword->type) {
+ case MPT_RQR: /* register */
+ mpt1327_reset_sync(mpt1327); /* message complete */
+ unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT1]);
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) registers\n", unit->prefix, unit->ident);
+ if (unit->tc)
+ _cancel_pending_call(mpt1327, unit);
+ unit_new_state(unit, UNIT_REGISTER_ACK);
+ break;
+ case MPT_RQT: /* diversion */
+ mpt1327_reset_sync(mpt1327); /* message complete */
+ unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT2]);
+ unit->called_ident = codeword->params[MPT_IDENT1];
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) requests diversion\n", unit->prefix, unit->ident);
+ if (unit->tc)
+ _cancel_pending_call(mpt1327, unit);
+ PDEBUG_CHAN(DMPT1327, DEBUG_NOTICE, "Diversion not supported by TSC, rejecting...\n");
+ unit_new_state(unit, UNIT_DIVERSION_REJ);
+ break;
+ case MPT_RQS: /* simple call */
+ case MPT_RQE: /* emergency call */
+ mpt1327_reset_sync(mpt1327); /* message complete */
+ unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT2]);
+ unit->called_ident = codeword->params[MPT_IDENT1];
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) calls Ident:%d%s\n", unit->prefix, unit->ident, unit->called_ident, (codeword->type == MPT_RQE) ? " (emergency)" : "");
+ if (unit->tc)
+ _cancel_pending_call(mpt1327, unit);
+ tc = search_free_tc();
+ if (!tc) {
+ unit_new_state(unit, UNIT_CALLING_REJ);
+ PDEBUG_CHAN(DMPT1327, DEBUG_NOTICE, "No free Traffic Channel, call is rejected.\n");
+ break;
+ }
+ if (codeword->params[MPT_EXT]) {
+ int exchange;
+ unit->called_type = CALLED_TYPE_PBX_SHORT;
+ sprintf(unit->called_number, "%d", unit->called_ident);
+ exchange = ((codeword->params[MPT_FLAG1] << 1) | codeword->params[MPT_FLAG2]) + 1;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to PBX exchange %d, Number %s\n", exchange, unit->called_number);
+ unit_new_state(unit, UNIT_GTC_A);
+ unit->repeat = REPEAT_GTC;
+ out_setup(unit, OSMO_CC_NETWORK_MPT1327_PBX, exchange);
+ } else if (unit->called_ident >= IDENT_PSTNSI1 && unit->called_ident < IDENT_PSTNSI1 + 15) {
+ unit->called_type = CALLED_TYPE_PSTN_PRE;
+ sprintf(unit->called_number, "%d", unit->called_ident - IDENT_PSTNSI1 + 1);
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to PSTN with pre-arranged Number %s\n", unit->called_number);
+ unit_new_state(unit, UNIT_GTC_A);
+ unit->repeat = REPEAT_GTC;
+ out_setup(unit, OSMO_CC_NETWORK_MPT1327_PSTN, 0);
+ } else switch (unit->called_ident) {
+ case IDENT_IPFIXI:
+ unit->called_type = CALLED_TYPE_INTERPFX;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to Unit/Group %d with different Prefix\n", unit->called_ident);
+ unit_new_state(unit, UNIT_CALLING_AHYC);
+ unit->repeat = REPEAT_AHYC;
+ break;
+ case IDENT_ALLI:
+ unit->called_type = CALLED_TYPE_SYSTEM;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> System wide Call\n");
+ unit_new_state(unit, UNIT_GTC_P);
+ unit->repeat = REPEAT_GTC;
+ break;
+ case IDENT_PSTNGI:
+ if (codeword->params[MPT_FLAG1]) {
+ unit->called_type = CALLED_TYPE_PSTN_LONG2;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to PSTN with long Number (10..31 Digits)\n");
+ } else {
+ unit->called_type = CALLED_TYPE_PSTN_LONG1;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to PSTN with long Number (1..9 Digits)\n");
+ }
+ unit_new_state(unit, UNIT_CALLING_AHYC);
+ unit->repeat = REPEAT_AHYC;
+ break;
+ case IDENT_PABXI:
+ unit->called_type = CALLED_TYPE_PBX_LONG;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to PBX (long number)\n");
+ unit_new_state(unit, UNIT_CALLING_AHYC);
+ unit->repeat = REPEAT_AHYC;
+ break;
+ default:
+ unit->called_type = CALLED_TYPE_UNIT;
+ unit->called_prefix = unit->prefix;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, " -> Call to Unit/Group %d (same Prefix)\n", unit->called_ident);
+ unit_new_state(unit, UNIT_GTC_P);
+ unit->repeat = REPEAT_GTC;
+ }
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Allocating Traffic Channel %s\n", tc->sender.kanal);
+ mpt1327_new_state(tc, STATE_BUSY, unit);
+ break;
+ case MPT_SAMIS: /* SAMIS response */
+ unit = get_unit(mpt1327->rx_sched.data_prefix, mpt1327->rx_sched.data_ident);
+ if (unit->state != UNIT_CALLING_SAMIS) {
+ PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Radio Unit (Prefix:%d Ident:%d) sends SAMIS, but not requested\n", unit->prefix, unit->ident);
+ break;
+ }
+ switch (unit->called_type) {
+ case CALLED_TYPE_INTERPFX:
+ if (codeword->params[MPT_DESC] != 0x0) {
+ PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Expecting DESC=%d from Radio Unit, but got DESC=%d, dropping!\n", 0x0, (int)codeword->params[MPT_DESC]);
+ return;
+ }
+ unit->called_prefix = codeword->params[MPT_PARAMETERS1] >> 13;
+ unit->called_ident = codeword->params[MPT_PARAMETERS1] & 0x1fff;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) calls Prefix:%d Ident:%d\n", unit->prefix, unit->ident, unit->called_prefix, unit->called_ident);
+ unit_new_state(unit, UNIT_GTC_B);
+ unit->repeat = REPEAT_GTC;
+ break;
+ case CALLED_TYPE_PSTN_LONG1:
+ case CALLED_TYPE_PSTN_LONG2:
+ if (codeword->params[MPT_DESC] != 0x1) {
+ PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Expecting DESC=%d from Radio Unit, but got DESC=%d, dropping!\n", 0x1, (int)codeword->params[MPT_DESC]);
+ return;
+ }
+ unit->called_number[0] = '0';
+ unit->called_number[1] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 16) & 0xf];
+ unit->called_number[2] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 12) & 0xf];
+ unit->called_number[3] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 8) & 0xf];
+ unit->called_number[4] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 4) & 0xf];
+ unit->called_number[5] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 0) & 0xf];
+ unit->called_number[6] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 12) & 0xf];
+ unit->called_number[7] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 8) & 0xf];
+ unit->called_number[8] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 4) & 0xf];
+ unit->called_number[9] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 0) & 0xf];
+ unit->called_number[10] = '\0';
+ /* schedule reception of one or two words */
+ mpt1327->rx_sched.data_num = codeword->params[MPT_PARAMETERS2] >> 16;
+ mpt1327->rx_sched.data_count = 0;
+ mpt1327->rx_sched.data_word = MPT_SAMIS_DT;
+ if (mpt1327->rx_sched.data_num == 0) {
+ timer_stop(&unit->timer);
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) calls Number %s\n", unit->prefix, unit->ident, unit->called_number);
+ out_setup(unit, OSMO_CC_NETWORK_MPT1327_PSTN, 0);
+ unit_new_state(unit, UNIT_GTC_A);
+ unit->repeat = REPEAT_GTC;
+ }
+ break;
+ case CALLED_TYPE_PBX_LONG:
+ if (codeword->params[MPT_DESC] != 0x2) {
+ PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Expecting DESC=%d from Radio Unit, but got DESC=%d, dropping!\n", 0x2, (int)codeword->params[MPT_DESC]);
+ return;
+ }
+ unit->called_number[0] = '0';
+ unit->called_number[1] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 16) & 0xf];
+ unit->called_number[2] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 12) & 0xf];
+ unit->called_number[3] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 8) & 0xf];
+ unit->called_number[4] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 4) & 0xf];
+ unit->called_number[5] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS1] >> 0) & 0xf];
+ unit->called_number[6] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 12) & 0xf];
+ unit->called_number[7] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 8) & 0xf];
+ unit->called_number[8] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 4) & 0xf];
+ unit->called_number[9] = mpt1327_bcd[(codeword->params[MPT_PARAMETERS2] >> 0) & 0xf];
+ unit->called_number[10] = '\0';
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) calls Number %s\n", unit->prefix, unit->ident, unit->called_number);
+ timer_stop(&unit->timer);
+ out_setup(unit, OSMO_CC_NETWORK_MPT1327_PBX, 0);
+ unit_new_state(unit, UNIT_GTC_A);
+ unit->repeat = REPEAT_GTC;
+ break;
+ default:
+ PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Want to receive SAMIS, but called_type not set correctly, please fix!\n");
+ abort();
+ }
+ break;
+ case MPT_SAMIS_DT:
+ unit = get_unit(mpt1327->rx_sched.data_prefix, mpt1327->rx_sched.data_ident);
+ if (unit->state != UNIT_CALLING_SAMIS) {
+ PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Radio Unit (Prefix:%d Ident:%d) sends SAMIS, but not requested\n", unit->prefix, unit->ident);
+ break;
+ }
+ if (mpt1327->rx_sched.data_count == 1) {
+ unit->called_number[10] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 40) & 0xf];
+ unit->called_number[11] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 36) & 0xf];
+ unit->called_number[12] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 32) & 0xf];
+ unit->called_number[13] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 28) & 0xf];
+ unit->called_number[14] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 24) & 0xf];
+ unit->called_number[15] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 20) & 0xf];
+ unit->called_number[16] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 16) & 0xf];
+ unit->called_number[17] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 12) & 0xf];
+ unit->called_number[18] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 8) & 0xf];
+ unit->called_number[19] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 4) & 0xf];
+ unit->called_number[20] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 0) & 0xf];
+ unit->called_number[21] = '\0';
+ if (mpt1327->rx_sched.data_num == 1) {
+ timer_stop(&unit->timer);
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) calls Number %s\n", unit->prefix, unit->ident, unit->called_number);
+ out_setup(unit, OSMO_CC_NETWORK_MPT1327_PSTN, 0);
+ unit_new_state(unit, UNIT_GTC_A);
+ unit->repeat = REPEAT_GTC;
+ }
+ } else {
+ unit->called_number[21] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 40) & 0xf];
+ unit->called_number[22] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 36) & 0xf];
+ unit->called_number[23] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 32) & 0xf];
+ unit->called_number[24] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 28) & 0xf];
+ unit->called_number[25] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 24) & 0xf];
+ unit->called_number[26] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 20) & 0xf];
+ unit->called_number[27] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 16) & 0xf];
+ unit->called_number[28] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 12) & 0xf];
+ unit->called_number[29] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 8) & 0xf];
+ unit->called_number[30] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 4) & 0xf];
+ unit->called_number[31] = mpt1327_bcd[(codeword->params[MPT_BCD11] >> 0) & 0xf];
+ unit->called_number[32] = '\0';
+ mpt1327->rx_sched.data_num = 0; /* just in case it is more than 2 data words */
+ timer_stop(&unit->timer);
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) calls Number %s\n", unit->prefix, unit->ident, unit->called_number);
+ out_setup(unit, OSMO_CC_NETWORK_MPT1327_PSTN, 0);
+ unit_new_state(unit, UNIT_GTC_A);
+ unit->repeat = REPEAT_GTC;
+ }
+ break;
+ case MPT_RQX: /* call cancel */
+ unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT2]);
+ unit->called_ident = codeword->params[MPT_IDENT1];
+ timer_stop(&unit->timer);
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) cancels call to %d\n", unit->prefix, unit->ident, unit->called_ident);
+ unit_new_state(unit, UNIT_CANCEL_ACK);
+ if (unit->tc) {
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Free Traffic Channel %s, because unit cancels on control channel\n", unit->tc->sender.kanal);
+ mpt1327_go_idle(unit->tc);
+ }
+ break;
+ case MPT_ACKI: /* ack from unit (not ready, wait for RQQ) */
+ unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT1]);
+ timer_stop(&unit->timer);
+ if (unit->state == UNIT_CALLED_ACK) {
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) acknowledges call (not yet ready, waiting for RQQ\n", unit->prefix, unit->ident);
+ if (unit->callref)
+ call_up_alerting(unit->callref);
+ break;
+ }
+ PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Radio Unit (Prefix:%d Ident:%d) acknowledges, no call\n", unit->prefix, unit->ident);
+ break;
+ case MPT_ACK: /* ack from unit */
+ unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT1]);
+ timer_stop(&unit->timer);
+ if (unit->state == UNIT_CALLED_ACK) {
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) acknowledges call\n", unit->prefix, unit->ident);
+answer:
+ if (unit->callref) {
+ char connected_id[32];
+ sprintf(connected_id, "%03d%04d", unit->prefix, unit->ident);
+ call_up_answer(unit->callref, connected_id);
+ }
+ unit_new_state(unit, UNIT_GTC_A);
+ unit->repeat = REPEAT_GTC;
+ break;
+ }
+ PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Radio Unit (Prefix:%d Ident:%d) acknowledges, no call\n", unit->prefix, unit->ident);
+ break;
+ case MPT_RQQ: /* status from radio */
+ unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT2]);
+ timer_stop(&unit->timer);
+ PDEBUG_CHAN(DMPT1327, DEBUG_ERROR, "Radio Unit (Prefix:%d Ident:%d) sends RRQ with STATUS=%d\n", unit->prefix, unit->ident, (int)codeword->params[MPT_STATUS]);
+ switch (codeword->params[MPT_STATUS]) {
+ case 0x00:
+ if (unit->state == UNIT_CALLED_ACK) {
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) answers call\n", unit->prefix, unit->ident);
+ goto answer;
+ }
+ break;
+ case 0x1f:
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) rejects call, releasing\n", unit->prefix, unit->ident);
+ mpt1327_release(unit);
+ if (unit->callref) {
+ call_up_release(unit->callref, CAUSE_NORMAL);
+ unit->callref = 0;
+ }
+ break;
+ }
+ break;
+ case MPT_ALH: /* control channel Aloha for loopback mode */
+ case MPT_ALHS:
+ case MPT_ALHD:
+ case MPT_ALHE:
+ case MPT_ALHR:
+ case MPT_ALHX:
+ case MPT_ALHF:
+ /* schedule reception of CCSC word */
+ mpt1327->rx_sched.data_num = 1;
+ mpt1327->rx_sched.data_count = 0;
+ mpt1327->rx_sched.data_word = MPT_CCSC;
+ break;
+ default:
+ if (mpt1327->sender.loopback)
+ return;
+ PDEBUG_CHAN(DMPT1327, DEBUG_NOTICE, "Received unsupported codeword '%s' = '%s' on control channel\n", codeword->short_name, codeword->long_name);
+ }
+}
+
+void mpt1327_receive_codeword_traffic(mpt1327_t *mpt1327, mpt1327_codeword_t *codeword)
+{
+ mpt1327_unit_t *unit;
+
+ switch (codeword->type) {
+ case MPT_MAINT: /* maintenance message */
+ unit = get_unit(codeword->params[MPT_PFIX], codeword->params[MPT_IDENT1]);
+ if (codeword->params[MPT_CHAN] != mpt1327_channel2chan(mpt1327->band, atoi(mpt1327->sender.kanal))) {
+ PDEBUG_CHAN(DMPT1327, DEBUG_NOTICE, "Radio Unit (Prefix:%d Ident:%d) sends maintenance message on wrong channel %d, ignoring!\n", unit->prefix, unit->ident, (int)codeword->params[MPT_CHAN]);
+ return;
+ }
+ if (!unit->tc) {
+ PDEBUG_CHAN(DMPT1327, DEBUG_NOTICE, "Radio Unit (Prefix:%d Ident:%d) sends maintenance, but it has no channel assigned, ignoring!\n", unit->prefix, unit->ident);
+ return;
+ }
+ switch (codeword->params[MPT_OPER]) {
+ case OPER_PRESSEL_ON:
+ if (sysdef.timeout)
+ timer_start(&unit->timer, sysdef.timeout);
+ mpt1327->pressel_on = 1;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) starts transmission\n", unit->prefix, unit->ident);
+ break;
+ case OPER_PRESSEL_OFF:
+ if (sysdef.timeout)
+ timer_start(&unit->timer, sysdef.timeout);
+ mpt1327->pressel_on = 0;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) stops transmission\n", unit->prefix, unit->ident);
+ break;
+ case OPER_DISCONNECT:
+ /* ignore while we send clear message */
+ if (unit->state == UNIT_CALL_CLEAR)
+ return;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) disconnects from channel\n", unit->prefix, unit->ident);
+ if (unit->state == UNIT_CALL) {
+ timer_stop(&unit->timer);
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Free Traffic Channel %s, because the initiator goes on-hook\n", unit->tc->sender.kanal);
+ mpt1327_go_idle(unit->tc);
+ if (unit->callref) {
+ call_up_release(unit->callref, CAUSE_NORMAL);
+ unit->callref = 0;
+ }
+ }
+ unit_new_state(unit, UNIT_IDLE);
+ break;
+ case OPER_PERIODIC:
+ if (sysdef.timeout)
+ timer_start(&unit->timer, sysdef.timeout);
+ mpt1327->pressel_on = 1;
+ PDEBUG_CHAN(DMPT1327, DEBUG_INFO, "Radio Unit (Prefix:%d Ident:%d) sends periodic message\n", unit->prefix, unit->ident);
+ break;
+ }
+ break;
+ default:
+ if (mpt1327->sender.loopback)
+ return;
+ PDEBUG_CHAN(DMPT1327, DEBUG_NOTICE, "Received unsupported codeword '%s' = '%s' on traffic channel\n", codeword->short_name, codeword->long_name);
+ }
+}
+
+void mpt1327_receive_codeword(mpt1327_t *mpt1327, uint64_t bits, double quality, double level)
+{
+ mpt1327_codeword_t codeword;
+ int rc;
+
+ PDEBUG_CHAN(DDSP, DEBUG_INFO, "RX Level: %.0f%% Quality=%.0f%%\n", level * 100.0, quality * 100.0);
+
+ rc = mpt1327_decode_codeword(&codeword, (mpt1327->rx_sched.data_num) ? mpt1327->rx_sched.data_word : -1, (mpt1327->sender.loopback) ? MPT_DOWN : MPT_UP, bits);
+ if (rc < 0) {
+ mpt1327->rx_sched.data_num = 0;
+ mpt1327_reset_sync(mpt1327); /* message complete */
+ return;
+ }
+
+ /* count if we have data words */
+ if (mpt1327->rx_sched.data_num)
+ mpt1327->rx_sched.data_count++;
+
+ switch (mpt1327->dsp_mode) {
+ case DSP_MODE_CONTROL:
+ mpt1327_receive_codeword_control(mpt1327, &codeword);
+ break;
+ case DSP_MODE_TRAFFIC:
+ mpt1327_receive_codeword_traffic(mpt1327, &codeword);
+ break;
+ default:
+ ;
+ }
+
+ /* if all data words are received */
+ if (mpt1327->rx_sched.data_num && mpt1327->rx_sched.data_count == mpt1327->rx_sched.data_num)
+ mpt1327->rx_sched.data_num = 0;
+
+ /* reset receiver unless there is a pending data word to be received */
+ if (mpt1327->rx_sched.data_num == 0)
+ mpt1327_reset_sync(mpt1327); /* message complete */
+}
+
+void mpt1327_signal_indication(mpt1327_t *mpt1327)
+{
+ /* restart timer, if enabled */
+ if (mpt1327->unit && mpt1327->unit->state == UNIT_CALL) {
+ if (sysdef.timeout)
+ timer_start(&mpt1327->unit->timer, sysdef.timeout);
+ }
+}
+
+/* Timeout handling */
+static void mpt1327_timeout(struct timer *timer)
+{
+ mpt1327_t *mpt1327 = (mpt1327_t *)timer->priv;
+
+ switch (mpt1327->state) {
+ default:
+ break;
+ }
+}
+
+/*
+ * call control (from upper layer)
+ */
+
+/* Call control starts call towards mobile station. */
+int call_down_setup(int callref, const char __attribute__((unused)) *caller_id, enum number_type __attribute__((unused)) caller_type, const char *dialing)
+{
+ mpt1327_unit_t *unit;
+ mpt1327_t *tc;
+ uint8_t prefix;
+ uint16_t ident;
+ int i;
+
+ /* 1. check if number is invalid, return INVALNUMBER */
+ if (strlen(dialing) != 7) {
+inval:
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing call to invalid number '%s', rejecting!\n", dialing);
+ return -CAUSE_INVALNUMBER;
+ }
+ for (i = 0; i < 7; i++) {
+ if (dialing[i] < '0' || dialing[i] > '9')
+ goto inval;
+ }
+ prefix = (dialing[0] - '0') * 100 + (dialing[1] - '0') * 10 + (dialing[2] - '0');
+ if (prefix > 127) {
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing call to invalid Prefix '%03d' in number '%s', rejecting! (Prefix must be 000..127)\n", prefix, dialing);
+ return -CAUSE_INVALNUMBER;
+ }
+ ident = atoi(dialing + 3);
+ if (ident > 8100 || ident < 1) {
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing call to invalid Ident '%04d' in number '%s', rejecting! (Ident must be 0001..8100)\n", ident, dialing);
+ return -CAUSE_INVALNUMBER;
+ }
+
+ /* 2. check if given number is already in a call, return BUSY */
+ unit = get_unit(prefix, ident);
+ if (unit->state != UNIT_IDLE) {
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing call to busy Radio Unit, rejecting!\n");
+ return -CAUSE_BUSY;
+ }
+
+ /* 3. check if all channels are busy, return NOCHANNEL */
+ tc = search_free_tc();
+ if (!tc) {
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing call, but no free channel, rejecting!\n");
+ return -CAUSE_NOCHANNEL;
+ }
+
+ PDEBUG(DMPT1327, DEBUG_INFO, "Outgoing call to Radio Unit (Prefix:%d Ident:%d)\n", unit->prefix, unit->ident);
+
+ /* 4. trying to reach radio unit */
+ unit->callref = callref;
+ mpt1327_new_state(tc, STATE_BUSY, unit);
+ unit_new_state(unit, UNIT_CALLED_AHY);
+ unit->repeat = REPEAT_AHY;
+ unit->called_prefix = unit->prefix;
+ unit->called_ident = unit->ident;
+
+ return 0;
+}
+
+void call_down_answer(int __attribute__((unused)) callref)
+{
+}
+
+/* Call control sends disconnect (with tones).
+ * An active call stays active, so tones and annoucements can be received
+ * by mobile station.
+ */
+void call_down_disconnect(int callref, int cause)
+{
+ mpt1327_unit_t *unit;
+
+ PDEBUG(DMPT1327, DEBUG_INFO, "Call has been disconnected by network.\n");
+
+ unit = find_unit_callref(callref);
+ if (!unit) {
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing disconnect, but no unit for callref!\n");
+ call_up_release(callref, CAUSE_INVALCALLREF);
+ return;
+ }
+
+ /* Release when not active */
+ if (unit->state == UNIT_CALL)
+ return;
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing disconnect, but no call, releasing!\n");
+ mpt1327_release(unit);
+ unit->callref = 0;
+
+ call_up_release(callref, cause);
+}
+
+/* Call control releases call toward mobile station. */
+void call_down_release(int callref, __attribute__((unused)) int cause)
+{
+ mpt1327_unit_t *unit;
+
+ PDEBUG(DMPT1327, DEBUG_INFO, "Call has been released by network, releasing call.\n");
+
+ unit = find_unit_callref(callref);
+ if (!unit) {
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing release, but no unit for callref!\n");
+ /* don't send release, because caller already released */
+ return;
+ }
+
+ PDEBUG(DMPT1327, DEBUG_NOTICE, "Outgoing release, releasing!\n");
+ mpt1327_release(unit);
+ unit->callref = 0;
+}
+
+/* Receive audio from call instance. */
+void call_down_audio(int callref, sample_t *samples, int count)
+{
+ mpt1327_unit_t *unit;
+
+ unit = find_unit_callref(callref);
+ if (!unit)
+ return;
+ if (!unit->tc)
+ return;
+
+ if (unit->tc->state == STATE_BUSY && unit->tc->dsp_mode == DSP_MODE_TRAFFIC) {
+ sample_t up[(int)((double)count * unit->tc->sender.srstate.factor + 0.5) + 10];
+ count = samplerate_upsample(&unit->tc->sender.srstate, samples, count, up);
+ jitter_save(&unit->tc->sender.dejitter, up, count);
+ }
+}
+
+void dump_info(void)
+{
+ dump_units();
+}
+
+void call_down_clock(void) {}
+
diff --git a/src/mpt1327/mpt1327.h b/src/mpt1327/mpt1327.h
new file mode 100755
index 0000000..7878727
--- /dev/null
+++ b/src/mpt1327/mpt1327.h
@@ -0,0 +1,155 @@
+#include "../libsquelch/squelch.h"
+#include "../libfsk/fsk.h"
+#include "../libmobile/sender.h"
+
+enum mpt1327_band {
+ BAND_MPT1343_SUB1 = 0,
+ BAND_MPT1343_SUB2,
+ BAND_REGIONET43_SUB1,
+ BAND_REGIONET43_SUB2,
+};
+
+enum mpt1327_chan_type {
+ CHAN_TYPE_CC, /* control channel */
+ CHAN_TYPE_TC, /* traffic channel */
+ CHAN_TYPE_CC_TC, /* combined CC + TC */
+};
+
+enum mpt1327_state {
+ STATE_NULL = 0,
+ STATE_IDLE,
+ STATE_BUSY,
+};
+
+enum dsp_mode {
+ DSP_MODE_OFF = 0, /* no transmission, no reception */
+ DSP_MODE_CONTROL, /* send/receive codewords */
+ DSP_MODE_TRAFFIC, /* send/receive codewords and audio */
+};
+
+enum mpt1327_sched_state {
+ /* states on control channel */
+ SCHED_STATE_CC_IDLE,
+ SCHED_STATE_CC_STARTUP,
+ SCHED_STATE_CC_CCSC,
+ SCHED_STATE_CC_ADDR,
+ SCHED_STATE_CC_DATA,
+ /* states on traffic channel */
+ SCHED_STATE_TC_IDLE,
+ SCHED_STATE_TC_SYNT,
+ SCHED_STATE_TC_ADDR,
+ SCHED_STATE_TC_DATA,
+};
+
+enum mpt1327_called_type {
+ CALLED_TYPE_UNIT, /* call to radio unit / line unit / group */
+ CALLED_TYPE_INTERPFX, /* same as above with different prefix */
+ CALLED_TYPE_SYSTEM, /* system wide call */
+ CALLED_TYPE_PBX_SHORT, /* call to short PBX extenstion */
+ CALLED_TYPE_PBX_LONG, /* call to long PBX extenstion */
+ CALLED_TYPE_PSTN_PRE, /* call to PSTN with prearranged number */
+ CALLED_TYPE_PSTN_LONG1, /* call to PSTN with 1..9 digits */
+ CALLED_TYPE_PSTN_LONG2, /* call to PSTN with 10..31 digits */
+};
+
+typedef struct mpt1327_sysdef {
+ uint16_t sys; /* system idenity */
+ int wt;
+ int per;
+ int pon;
+ double timeout;
+ int framelength;
+ int bcast_slots;
+} mpt1327_sysdef_t;
+
+typedef struct mpt1327_tx_sched {
+ enum mpt1327_sched_state state; /* what was currently scheduled */
+ int frame_length; /* number of slots in frame */
+ int frame_count; /* current slot number */
+ int dummy_slot; /* set, if next slot uses a dummy AHY */
+ int bcast_count; /* counts slots until sending broadcast */
+} mpt1327_tx_sched_t;
+
+typedef struct mpt1327_rx_sched {
+ int data_num; /* set if N data words are awaited */
+ int data_count; /* count data words */
+ int data_word; /* what data word to parse */
+ uint8_t data_prefix; /* unit that requires that data word */
+ uint16_t data_ident;
+} mpt1327_rx_sched_t;
+
+struct mpt1327;
+
+typedef struct mpt1327_unit {
+ struct mpt1327_unit *next;
+ uint64_t state;
+ int repeat; /* number of repeating messages / retries after timeout */
+ struct timer timer; /* timeout waiting for unit response */
+ struct mpt1327 *tc; /* link to transceiver */
+ uint8_t prefix; /* unit's prefix */
+ uint16_t ident; /* unit's ident */
+ uint8_t called_prefix;
+ uint16_t called_ident;
+ enum mpt1327_called_type called_type;
+ char called_number[33]; /* 0+number+'\0' */
+ uint32_t callref; /* PBX/PSTN link to call control */
+} mpt1327_unit_t;
+
+typedef struct mpt1327 {
+ sender_t sender;
+ enum mpt1327_band band;
+ enum mpt1327_chan_type chan_type;
+
+ /* sender's states */
+ enum mpt1327_state state; /* current sender's state */
+ struct timer timer; /* inactivity timer to clear channel */
+ mpt1327_unit_t *unit; /* link to unit */
+
+ /* display measurements */
+ dispmeasparam_t *dmp_frame_level;
+ dispmeasparam_t *dmp_frame_quality;
+ dispmeasparam_t *dmp_super_level;
+ dispmeasparam_t *dmp_super_quality;
+
+ /* scheduler states */
+ mpt1327_tx_sched_t tx_sched; /* downlink scheduler states, see above */
+ mpt1327_rx_sched_t rx_sched; /* uplink scheduler states, see above */
+
+ /* dsp states */
+ int repeater; /* in repeater mode the received audio is repeated */
+ jitter_t repeater_dejitter; /* forwarding audio */
+ int pressel_on; /* set if somebody transmitting on TC */
+ enum dsp_mode dsp_mode; /* current mode: audio, durable tone 0 or 1, paging */
+ fsk_mod_t fsk_mod; /* fsk processing */
+ fsk_demod_t fsk_demod;
+ uint16_t sync_word; /* current sync word for channel */
+ uint16_t rx_sync; /* shift register to detect sync */
+ int rx_in_sync; /* if we are in sync and receive bits */
+ int rx_mute; /* set, if currently receiving a message */
+ uint64_t rx_bits; /* receive frame (one extra byte to terminate string) */
+ int rx_count; /* next bit to receive */
+ double rx_level[256]; /* level infos */
+ double rx_quality[256]; /* quality infos */
+ uint64_t tx_bits; /* carries bits of one frame to transmit */
+ int tx_bit_num; /* number of bits to tansmit, or 0, if no transmission */
+ int tx_count; /* next bit to transmit */
+ squelch_t squelch; /* squelch detection process */
+} mpt1327_t;
+
+void init_sysdef (uint16_t sys, int wt, int per, int pon, int timeout);
+void flush_units(void);
+double mpt1327_channel2freq(enum mpt1327_band band, int channel, int uplink);
+const char *mpt1327_band_name(enum mpt1327_band band);
+void mpt1327_band_list(void);
+int mpt1327_band_by_short_name(const char *short_name);
+void mpt1327_channel_list(void);
+int mpt1327_channel_by_short_name(const char *short_name);
+const char *chan_type_short_name(enum mpt1327_chan_type chan_type);
+const char *chan_type_long_name(enum mpt1327_chan_type chan_type);
+int mpt1327_create(enum mpt1327_band band, const char *kanal, enum mpt1327_chan_type chan_type, const char *audiodev, int use_sdr, int samplerate, double rx_gain, double tx_gain, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback, double squelch_db);
+void mpt1327_check_channels(void);
+void mpt1327_destroy(sender_t *sender);
+void mpt1327_receive_codeword(mpt1327_t *mpt1327, uint64_t bits, double quality, double level);
+int mpt1327_send_codeword(mpt1327_t *mpt1327, uint64_t *bits);
+void mpt1327_signal_indication(mpt1327_t *mpt1327);
+