Golay/GSC paging support
This commit is contained in:
parent
ed0f8694f7
commit
8e67c3fda0
|
@ -71,6 +71,7 @@ src/mpt1327/mpt1327
|
||||||
src/jolly/jollycom
|
src/jolly/jollycom
|
||||||
src/eurosignal/eurosignal
|
src/eurosignal/eurosignal
|
||||||
src/pocsag/pocsag
|
src/pocsag/pocsag
|
||||||
|
src/golay/golay
|
||||||
src/fuenf/5-ton-folge
|
src/fuenf/5-ton-folge
|
||||||
src/tv/osmotv
|
src/tv/osmotv
|
||||||
src/radio/osmoradio
|
src/radio/osmoradio
|
||||||
|
|
1
README
1
README
|
@ -27,6 +27,7 @@ Additionally the following communication services are implemented:
|
||||||
* Analog Modem Emulation 'Datenklo' (AM7911)
|
* Analog Modem Emulation 'Datenklo' (AM7911)
|
||||||
* German classic 'Zeitansage' (talking clock)
|
* German classic 'Zeitansage' (talking clock)
|
||||||
* POCSAG transmitter / receiver
|
* POCSAG transmitter / receiver
|
||||||
|
* Golay/GSC transmitter / receiver
|
||||||
* DCF77 time signal transmitter and receiver
|
* DCF77 time signal transmitter and receiver
|
||||||
* C-Netz SIM emulator
|
* C-Netz SIM emulator
|
||||||
* C-Netz magnetic card emulator
|
* C-Netz magnetic card emulator
|
||||||
|
|
|
@ -104,6 +104,7 @@ AC_OUTPUT(
|
||||||
src/jolly/Makefile
|
src/jolly/Makefile
|
||||||
src/eurosignal/Makefile
|
src/eurosignal/Makefile
|
||||||
src/pocsag/Makefile
|
src/pocsag/Makefile
|
||||||
|
src/golay/Makefile
|
||||||
src/fuenf/Makefile
|
src/fuenf/Makefile
|
||||||
src/tv/Makefile
|
src/tv/Makefile
|
||||||
src/radio/Makefile
|
src/radio/Makefile
|
||||||
|
|
|
@ -129,7 +129,8 @@ Additional features:
|
||||||
<li><a href="magnetic.html">C-Netz Magnetic Card</a></li>
|
<li><a href="magnetic.html">C-Netz Magnetic Card</a></li>
|
||||||
<li>Zeitansage (German talking clock)</li>
|
<li>Zeitansage (German talking clock)</li>
|
||||||
<li>C-Netz FuVSt (MSC to control a real base station)</li>
|
<li>C-Netz FuVSt (MSC to control a real base station)</li>
|
||||||
<li>POCSAG</li>
|
<li>POCSAG (paging system)</li>
|
||||||
|
<li>Golay / GSC (paging system)</li>
|
||||||
<li>5-Ton-Ruf (firefighter's pagers and siren control)</li>
|
<li>5-Ton-Ruf (firefighter's pagers and siren control)</li>
|
||||||
</ul>
|
</ul>
|
||||||
</td></tr></table></center>
|
</td></tr></table></center>
|
||||||
|
|
|
@ -54,6 +54,7 @@ SUBDIRS += \
|
||||||
jolly \
|
jolly \
|
||||||
eurosignal \
|
eurosignal \
|
||||||
pocsag \
|
pocsag \
|
||||||
|
golay \
|
||||||
fuenf \
|
fuenf \
|
||||||
tv \
|
tv \
|
||||||
radio \
|
radio \
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||||
|
|
||||||
|
bin_PROGRAMS = \
|
||||||
|
golay
|
||||||
|
|
||||||
|
golay_SOURCES = \
|
||||||
|
golay.c \
|
||||||
|
dsp.c \
|
||||||
|
image.c \
|
||||||
|
main.c
|
||||||
|
golay_LDADD = \
|
||||||
|
$(COMMON_LA) \
|
||||||
|
../amps/libusatone.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/libtimer/libtimer.a \
|
||||||
|
$(top_builddir)/src/libsamplerate/libsamplerate.a \
|
||||||
|
$(top_builddir)/src/libemphasis/libemphasis.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 \
|
||||||
|
$(top_builddir)/src/libaaimage/libaaimage.a \
|
||||||
|
-lm
|
||||||
|
|
||||||
|
if HAVE_ALSA
|
||||||
|
golay_LDADD += \
|
||||||
|
$(top_builddir)/src/libsound/libsound.a \
|
||||||
|
$(ALSA_LIBS)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HAVE_SDR
|
||||||
|
golay_LDADD += \
|
||||||
|
$(top_builddir)/src/libsdr/libsdr.a \
|
||||||
|
$(top_builddir)/src/libam/libam.a \
|
||||||
|
$(top_builddir)/src/libfft/libfft.a \
|
||||||
|
$(UHD_LIBS) \
|
||||||
|
$(SOAPY_LIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if HAVE_ALSA
|
||||||
|
AM_CPPFLAGS += -DHAVE_ALSA
|
||||||
|
endif
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
/* GSC signal processing
|
||||||
|
*
|
||||||
|
* (C) 2022 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 gsc->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 "../libdebug/debug.h"
|
||||||
|
#include "golay.h"
|
||||||
|
#include "dsp.h"
|
||||||
|
|
||||||
|
#define MAX_DISPLAY 1.4 /* something above speech level, no emphasis */
|
||||||
|
|
||||||
|
static void dsp_init_ramp(gsc_t *gsc)
|
||||||
|
{
|
||||||
|
double c;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Generating cosine shaped ramp table.\n");
|
||||||
|
for (i = 0; i < 256; i++) {
|
||||||
|
/* This is mathematically incorrect... */
|
||||||
|
if (i < 64)
|
||||||
|
c = 1.0;
|
||||||
|
else if (i >= 192)
|
||||||
|
c = -1.0;
|
||||||
|
else
|
||||||
|
c = cos((double)(i - 64) / 128.0 * M_PI);
|
||||||
|
gsc->fsk_ramp_down[i] = c * gsc->fsk_deviation * gsc->fsk_polarity;
|
||||||
|
gsc->fsk_ramp_up[i] = -gsc->fsk_ramp_down[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Init transceiver instance. */
|
||||||
|
int dsp_init_sender(gsc_t *gsc, int samplerate, double deviation, double polarity)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Init DSP for transceiver.\n");
|
||||||
|
|
||||||
|
/* set modulation parameters */
|
||||||
|
// NOTE: baudrate equals modulation, because we have a raised cosine ramp of beta = 0.5
|
||||||
|
sender_set_fm(&gsc->sender, deviation, 600.0, deviation, MAX_DISPLAY);
|
||||||
|
|
||||||
|
gsc->fsk_bitduration = (double)samplerate / 600.0;
|
||||||
|
gsc->fsk_bitstep = 1.0 / gsc->fsk_bitduration;
|
||||||
|
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Use %.4f samples for one bit duration @ %d.\n", gsc->fsk_bitduration, gsc->sender.samplerate);
|
||||||
|
|
||||||
|
gsc->fsk_tx_buffer_size = gsc->fsk_bitduration + 10; /* 1 bit, add some extra to prevent short buffer due to rounding */
|
||||||
|
gsc->fsk_tx_buffer = calloc(sizeof(sample_t), gsc->fsk_tx_buffer_size);
|
||||||
|
if (!gsc->fsk_tx_buffer) {
|
||||||
|
PDEBUG_CHAN(DDSP, DEBUG_ERROR, "No memory!\n");
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create deviation and ramp */
|
||||||
|
gsc->fsk_deviation = 1.0; // equals what we st at sender_set_fm()
|
||||||
|
gsc->fsk_polarity = polarity;
|
||||||
|
dsp_init_ramp(gsc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
dsp_cleanup_sender(gsc);
|
||||||
|
|
||||||
|
return -rc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cleanup transceiver instance. */
|
||||||
|
void dsp_cleanup_sender(gsc_t *gsc)
|
||||||
|
{
|
||||||
|
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Cleanup DSP for transceiver.\n");
|
||||||
|
|
||||||
|
if (gsc->fsk_tx_buffer) {
|
||||||
|
free(gsc->fsk_tx_buffer);
|
||||||
|
gsc->fsk_tx_buffer = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* encode one bit into samples
|
||||||
|
* input: bit
|
||||||
|
* output: samples
|
||||||
|
* return number of samples */
|
||||||
|
static int fsk_bit_encode(gsc_t *gsc, uint8_t bit)
|
||||||
|
{
|
||||||
|
/* alloc samples, add 1 in case there is a rest */
|
||||||
|
sample_t *spl;
|
||||||
|
double phase, bitstep, devpol;
|
||||||
|
int count;
|
||||||
|
uint8_t lastbit;
|
||||||
|
|
||||||
|
devpol = gsc->fsk_deviation * gsc->fsk_polarity;
|
||||||
|
spl = gsc->fsk_tx_buffer;
|
||||||
|
phase = gsc->fsk_tx_phase;
|
||||||
|
lastbit = gsc->fsk_tx_lastbit;
|
||||||
|
bitstep = gsc->fsk_bitstep * 256.0;
|
||||||
|
|
||||||
|
if (lastbit) {
|
||||||
|
if (bit) {
|
||||||
|
/* stay up */
|
||||||
|
do {
|
||||||
|
*spl++ = devpol;
|
||||||
|
phase += bitstep;
|
||||||
|
} while (phase < 256.0);
|
||||||
|
phase -= 256.0;
|
||||||
|
} else {
|
||||||
|
/* ramp down */
|
||||||
|
do {
|
||||||
|
*spl++ = gsc->fsk_ramp_down[(uint8_t)phase];
|
||||||
|
phase += bitstep;
|
||||||
|
} while (phase < 256.0);
|
||||||
|
phase -= 256.0;
|
||||||
|
lastbit = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (bit) {
|
||||||
|
/* ramp up */
|
||||||
|
do {
|
||||||
|
*spl++ = gsc->fsk_ramp_up[(uint8_t)phase];
|
||||||
|
phase += bitstep;
|
||||||
|
} while (phase < 256.0);
|
||||||
|
phase -= 256.0;
|
||||||
|
lastbit = 1;
|
||||||
|
} else {
|
||||||
|
/* stay down */
|
||||||
|
do {
|
||||||
|
*spl++ = -devpol;
|
||||||
|
phase += bitstep;
|
||||||
|
} while (phase < 256.0);
|
||||||
|
phase -= 256.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* depending on the number of samples, return the number */
|
||||||
|
count = ((uintptr_t)spl - (uintptr_t)gsc->fsk_tx_buffer) / sizeof(*spl);
|
||||||
|
|
||||||
|
gsc->fsk_tx_phase = phase;
|
||||||
|
gsc->fsk_tx_lastbit = lastbit;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process received audio stream from radio unit. */
|
||||||
|
void sender_receive(sender_t __attribute__((unused)) *sender, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) length, double __attribute__((unused)) rf_level_db)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Provide stream of audio toward radio unit */
|
||||||
|
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
||||||
|
{
|
||||||
|
gsc_t *gsc = (gsc_t *) sender;
|
||||||
|
|
||||||
|
again:
|
||||||
|
/* get word */
|
||||||
|
if (!gsc->fsk_tx_buffer_length) {
|
||||||
|
int8_t bit = get_bit(gsc);
|
||||||
|
|
||||||
|
/* no message, power is off */
|
||||||
|
if (bit < 0) {
|
||||||
|
memset(samples, 0, sizeof(samples) * length);
|
||||||
|
memset(power, 0, length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* encode */
|
||||||
|
gsc->fsk_tx_buffer_length = fsk_bit_encode(gsc, bit);
|
||||||
|
gsc->fsk_tx_buffer_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send encoded bit until end of source or destination buffer is reached */
|
||||||
|
while (length) {
|
||||||
|
*power++ = 1;
|
||||||
|
*samples++ = gsc->fsk_tx_buffer[gsc->fsk_tx_buffer_pos++];
|
||||||
|
length--;
|
||||||
|
if (gsc->fsk_tx_buffer_pos == gsc->fsk_tx_buffer_length) {
|
||||||
|
gsc->fsk_tx_buffer_length = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do again, if destination buffer is not yet full */
|
||||||
|
if (length)
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
int dsp_init_sender(gsc_t *gsc, int samplerate, double deviation, double polarity);
|
||||||
|
void dsp_cleanup_sender(gsc_t *gsc);
|
||||||
|
|
|
@ -0,0 +1,823 @@
|
||||||
|
/* Golay/GSC transcoding (encoding only - maybe)
|
||||||
|
*
|
||||||
|
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* Inspired by GSC code written by Brandon Creighton <cstone@pobox.com>.
|
||||||
|
*
|
||||||
|
* Inspired by GOLAY code written by Robert Morelos-Zaragoza
|
||||||
|
* <robert@spectra.eng.hawaii.edu>.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Golay code was is use since 1973, the GSC extension was used after 1982.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include "../libsample/sample.h"
|
||||||
|
#include "../libdebug/debug.h"
|
||||||
|
#include "../libmobile/call.h"
|
||||||
|
#include "../libmobile/main_mobile.h"
|
||||||
|
#include "../libmobile/cause.h"
|
||||||
|
#include "golay.h"
|
||||||
|
#include "dsp.h"
|
||||||
|
|
||||||
|
/* Create transceiver instance and link to a list. */
|
||||||
|
int golay_create(const char *kanal, double frequency, const char *device, int use_sdr, int samplerate, double rx_gain, double tx_gain, double deviation, double polarity, const char *message, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback)
|
||||||
|
{
|
||||||
|
gsc_t *gsc;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
gsc = calloc(1, sizeof(*gsc));
|
||||||
|
if (!gsc) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_ERROR, "No memory!\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, "Creating 'GOLAY' instance for frequency = %s (sample rate %d).\n", kanal, samplerate);
|
||||||
|
|
||||||
|
/* init general part of transceiver */
|
||||||
|
rc = sender_create(&gsc->sender, kanal, frequency, frequency, device, 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(DGOLAY, DEBUG_ERROR, "Failed to init transceiver process!\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* init audio processing */
|
||||||
|
rc = dsp_init_sender(gsc, samplerate, deviation, polarity);
|
||||||
|
if (rc < 0) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_ERROR, "Failed to init audio processing!\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
gsc->tx = 1;
|
||||||
|
gsc->default_message = message;
|
||||||
|
|
||||||
|
PDEBUG(DGOLAY, DEBUG_NOTICE, "Created transmitter for frequency %s\n", kanal);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
golay_destroy(&gsc->sender);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void golay_msg_destroy(gsc_t *gsc, gsc_msg_t *msg);
|
||||||
|
|
||||||
|
/* Destroy transceiver instance and unlink from list. */
|
||||||
|
void golay_destroy(sender_t *sender)
|
||||||
|
{
|
||||||
|
gsc_t *gsc = (gsc_t *) sender;
|
||||||
|
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, "Destroying 'GOLAY' instance for frequency = %s.\n", sender->kanal);
|
||||||
|
|
||||||
|
while (gsc->msg_list)
|
||||||
|
golay_msg_destroy(gsc, gsc->msg_list);
|
||||||
|
dsp_cleanup_sender(gsc);
|
||||||
|
sender_destroy(&gsc->sender);
|
||||||
|
free(gsc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create message and add to queue */
|
||||||
|
static gsc_msg_t *golay_msg_create(gsc_t *gsc, const char *address, const char *text, int force_type)
|
||||||
|
{
|
||||||
|
gsc_msg_t *msg, **msgp;
|
||||||
|
|
||||||
|
PDEBUG(DGOLAY, DEBUG_INFO, "Creating msg instance to page address '%s'.\n", address);
|
||||||
|
|
||||||
|
/* create */
|
||||||
|
msg = calloc(1, sizeof(*msg));
|
||||||
|
if (!msg) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_ERROR, "No mem!\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (strlen(address) != sizeof(msg->address) - 1) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_NOTICE, "Address has incorrect length, cannot page!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (strlen(text) > sizeof(msg->data) - 1) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_NOTICE, "Given test is too long, cannot page!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* init */
|
||||||
|
strcpy(msg->address, address);
|
||||||
|
msg->force_type = force_type;
|
||||||
|
strcpy(msg->data, text);
|
||||||
|
|
||||||
|
/* link */
|
||||||
|
msgp = &gsc->msg_list;
|
||||||
|
while ((*msgp))
|
||||||
|
msgp = &(*msgp)->next;
|
||||||
|
(*msgp) = msg;
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove and destroy msg from queue */
|
||||||
|
static void golay_msg_destroy(gsc_t *gsc, gsc_msg_t *msg)
|
||||||
|
{
|
||||||
|
gsc_msg_t **msgp;
|
||||||
|
|
||||||
|
/* unlink */
|
||||||
|
msgp = &gsc->msg_list;
|
||||||
|
while ((*msgp) != msg)
|
||||||
|
msgp = &(*msgp)->next;
|
||||||
|
(*msgp) = msg->next;
|
||||||
|
|
||||||
|
/* destroy */
|
||||||
|
free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* uncomment this for showing encoder tables ("<parity> <information>", LSB is the right most bit) */
|
||||||
|
//#define DEBUG_TABLE
|
||||||
|
|
||||||
|
static uint32_t golay_table[4096];
|
||||||
|
|
||||||
|
#define X22 0x00400000
|
||||||
|
#define X11 0x00000800
|
||||||
|
#define MASK12 0xfffff800
|
||||||
|
#define GEN_GOL 0x00000c75
|
||||||
|
|
||||||
|
/* generate golay encoding table. the redundancy is shifted 12 bits */
|
||||||
|
void init_golay(void)
|
||||||
|
{
|
||||||
|
uint32_t syndrome, aux;
|
||||||
|
int data;
|
||||||
|
|
||||||
|
for (data = 0; data < 4096; data++) {
|
||||||
|
syndrome = data << 11;
|
||||||
|
/* calculate syndrome */
|
||||||
|
aux = X22;
|
||||||
|
if (syndrome >= X11) {
|
||||||
|
while (syndrome & MASK12) {
|
||||||
|
while (!(aux & syndrome))
|
||||||
|
aux = aux >> 1;
|
||||||
|
syndrome ^= (aux / X11) * GEN_GOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
golay_table[data] = data | (syndrome << 12);
|
||||||
|
#ifdef DEBUG_TABLE
|
||||||
|
printf("Golay %4d: ", data);
|
||||||
|
for (int i = 22; i >= 0; i--) {
|
||||||
|
if (i == 11)
|
||||||
|
printf(" ");
|
||||||
|
printf("%d", (golay_table[data] >> i) & 1);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t bch_table[128];
|
||||||
|
|
||||||
|
#define X14 0x4000
|
||||||
|
#define X8 0x0100
|
||||||
|
#define MASK7 0xff00
|
||||||
|
#define GEN_BCH 0x00000117
|
||||||
|
|
||||||
|
/* generate bch encoding table. the redundancy is shifted 7 bits */
|
||||||
|
void init_bch(void)
|
||||||
|
{
|
||||||
|
uint16_t syndrome, aux;
|
||||||
|
int data;
|
||||||
|
|
||||||
|
for (data = 0; data < 128; data++) {
|
||||||
|
syndrome = data << 8;
|
||||||
|
/* calculate syndrome */
|
||||||
|
aux = X14;
|
||||||
|
if (syndrome >= X8) {
|
||||||
|
while (syndrome & MASK7) {
|
||||||
|
while (!(aux & syndrome))
|
||||||
|
aux = aux >> 1;
|
||||||
|
syndrome ^= (aux / X8) * GEN_BCH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bch_table[data] = data | (syndrome << 7);
|
||||||
|
#ifdef DEBUG_TABLE
|
||||||
|
printf("BCH %3d: ", data);
|
||||||
|
for (int i = 14; i >= 0; i--) {
|
||||||
|
if (i == 6)
|
||||||
|
printf(" ");
|
||||||
|
printf("%d", (bch_table[data] >> i) & 1);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t calc_golay(uint16_t data)
|
||||||
|
{
|
||||||
|
return golay_table[data & 0xfff];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t calc_bch(uint16_t data)
|
||||||
|
{
|
||||||
|
return bch_table[data & 0x7f];
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint16_t preamble_values[] = {
|
||||||
|
2030, 1628, 3198, 647, 191, 3315, 1949, 2540, 1560, 2335,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint32_t start_code = 713;
|
||||||
|
|
||||||
|
/* Rep. 900-2 Table VI */
|
||||||
|
static const uint16_t word1s[50] = {
|
||||||
|
721, 2731, 2952, 1387, 1578, 1708, 2650, 1747, 2580, 1376,
|
||||||
|
2692, 696, 1667, 3800, 3552, 3424, 1384, 3595, 876, 3124,
|
||||||
|
2285, 2608, 899, 3684, 3129, 2124, 1287, 2616, 1647, 3216,
|
||||||
|
375, 1232, 2824, 1840, 408, 3127, 3387, 882, 3468, 3267,
|
||||||
|
1575, 3463, 3152, 2572, 1252, 2592, 1552, 835, 1440, 160,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Rep. 900-2 Table VII (left column) */
|
||||||
|
static char encode_alpha(char c)
|
||||||
|
{
|
||||||
|
if (c >= 'a' && c <= 'z')
|
||||||
|
c = c - 'a' + 'A';
|
||||||
|
switch (c) {
|
||||||
|
case 0x0a:
|
||||||
|
case 0x0d:
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> CR/LF character.\n");
|
||||||
|
c = 0x3c;
|
||||||
|
break;
|
||||||
|
case '{':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
|
||||||
|
c = 0x3b;
|
||||||
|
break;
|
||||||
|
case '}':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
|
||||||
|
c = 0x3d;
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
|
||||||
|
c = 0x20;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (c < 0x20 || c > 0x5d) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> ' ' character.\n");
|
||||||
|
c = 0x20;
|
||||||
|
} else {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
|
||||||
|
c = c - 0x20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rep. 900-2 Table VII (right columns) */
|
||||||
|
static char encode_numeric(char c)
|
||||||
|
{
|
||||||
|
switch (c) {
|
||||||
|
case 'u':
|
||||||
|
case 'U':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'U' character.\n");
|
||||||
|
c = 0xb;
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
|
||||||
|
c = 0xc;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
|
||||||
|
c = 0xd;
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
case '*':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '*' character.\n");
|
||||||
|
c = 0xe;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
case 'A':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'A' character.\n");
|
||||||
|
c = 0xf0;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
case 'B':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'B' character.\n");
|
||||||
|
c = 0xf1;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
case 'C':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'C' character.\n");
|
||||||
|
c = 0xf2;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
case 'D':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'D' character.\n");
|
||||||
|
c = 0xf3;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'E' character.\n");
|
||||||
|
c = 0xf4;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
case 'F':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'F' character.\n");
|
||||||
|
c = 0xf6;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
case 'G':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'G' character.\n");
|
||||||
|
c = 0xf7;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
case 'H':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'H' character.\n");
|
||||||
|
c = 0xf8;
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
case 'J':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'J' character.\n");
|
||||||
|
c = 0xf9;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
case 'L':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'L' character.\n");
|
||||||
|
c = 0xfb;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
case 'N':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'N' character.\n");
|
||||||
|
c = 0xfc;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
case 'P':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'P' character.\n");
|
||||||
|
c = 0xfd;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
case 'R':
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> 'r' character.\n");
|
||||||
|
c = 0xfe;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> '%c' character.\n", c);
|
||||||
|
c = c - '0';
|
||||||
|
} else {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, " -> ' ' character.\n");
|
||||||
|
c = 0xc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int encode_address(const char *code, int *preamble, uint16_t *word1, uint16_t *word2)
|
||||||
|
{
|
||||||
|
static const uint16_t illegal_low[16] = { 0, 25, 51, 103, 206, 340, 363, 412, 445, 530, 642, 726, 782, 810, 825, 877 };
|
||||||
|
static const uint16_t illegal_high[7] = { 0, 292, 425, 584, 631, 841, 851 };
|
||||||
|
int idx, g0, g1, a0, a1, a2, ap0, ap, ap1, ap2, ap3, b1b0, b3b2, g1g0, a2a1a0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 7; i++) {
|
||||||
|
if (code[i] < '0' || code[i] > '9')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (code[i]) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_NOTICE, "Invalid functional address character. Only 0..9 are allowed.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = code[0] - '0';
|
||||||
|
g1 = code[1] - '0';
|
||||||
|
g0 = code[2] - '0';
|
||||||
|
a2 = code[3] - '0';
|
||||||
|
a1 = code[4] - '0';
|
||||||
|
a0 = code[5] - '0';
|
||||||
|
|
||||||
|
*preamble = (idx + g0) % 10;
|
||||||
|
|
||||||
|
ap = a2 * 200 + a1 * 20 + a0 * 2;
|
||||||
|
ap3 = ap / 1000;
|
||||||
|
ap2 = (ap / 100) % 10;
|
||||||
|
ap1 = (ap / 10) % 10;
|
||||||
|
ap0 = ap % 10;
|
||||||
|
|
||||||
|
b1b0 = (ap1 * 10 + ap0) / 2;
|
||||||
|
b3b2 = (ap3 * 10 + ap2);
|
||||||
|
|
||||||
|
g1g0 = (g1 * 10 + g0);
|
||||||
|
if (g1g0 >= 50) {
|
||||||
|
*word1 = word1s[g1g0 - 50];
|
||||||
|
*word2 = b3b2 * 100 + b1b0 + 50;
|
||||||
|
} else {
|
||||||
|
*word1 = word1s[g1g0];
|
||||||
|
*word2 = b3b2 * 100 + b1b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a2a1a0 = a2 * 100 + a1 * 10 + a0;
|
||||||
|
if (g1g0 < 50) {
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
if (a2a1a0 == illegal_low[i])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i < 16) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_NOTICE, "Functional address has invlid value '%03d' for last three characters.\n", a2a1a0);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < 7; i++) {
|
||||||
|
if (a2a1a0 == illegal_high[i])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i < 7) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_NOTICE, "Functional address has invlid value '%03d' for last three characters.\n", a2a1a0);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void queue_reset(gsc_t *gsc)
|
||||||
|
{
|
||||||
|
gsc->bit_index = 0;
|
||||||
|
gsc->bit_num = 0;
|
||||||
|
gsc->bit_overflow = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void queue_bit(gsc_t *gsc, int bit)
|
||||||
|
{
|
||||||
|
if (gsc->bit_num == sizeof(gsc->bit))
|
||||||
|
gsc->bit_overflow = 1;
|
||||||
|
if (gsc->bit_overflow) {
|
||||||
|
gsc->bit_num++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gsc->bit[gsc->bit_num++] = bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void queue_dup(gsc_t *gsc, uint32_t data, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
queue_bit(gsc, (data >> i) & 1);
|
||||||
|
queue_bit(gsc, (data >> i) & 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void queue_comma(gsc_t *gsc, int bits, uint8_t polarity)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < bits; i++) {
|
||||||
|
queue_bit(gsc, polarity);
|
||||||
|
polarity = !polarity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int queue_batch(gsc_t *gsc, const char *address, int force_type, const char *message)
|
||||||
|
{
|
||||||
|
int type;
|
||||||
|
int preamble;
|
||||||
|
uint16_t word1, word2;
|
||||||
|
uint8_t function;
|
||||||
|
uint32_t golay;
|
||||||
|
uint16_t bch[8];
|
||||||
|
uint8_t msg[12], digit, shifted, contbit, checksum;
|
||||||
|
int i, j, k;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
queue_reset(gsc);
|
||||||
|
|
||||||
|
/* check address length */
|
||||||
|
if (!address || strlen(address) != 7) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_NOTICE, "Invalid functional address '%s' size. Only 7 digits are allowed.\n", address);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* calculate address */
|
||||||
|
rc = encode_address(address, &preamble, &word1, &word2);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* calculate function */
|
||||||
|
switch (address[6]) {
|
||||||
|
case '1': type = TYPE_VOICE; function = 0; break;
|
||||||
|
case '2': type = TYPE_VOICE; function = 1; break;
|
||||||
|
case '3': type = TYPE_VOICE; function = 2; break;
|
||||||
|
case '4': type = TYPE_VOICE; function = 3; break;
|
||||||
|
case '5': type = TYPE_ALPHA; function = 0; break;
|
||||||
|
case '6': type = TYPE_ALPHA; function = 1; break;
|
||||||
|
case '7': type = TYPE_ALPHA; function = 2; break;
|
||||||
|
case '8': type = TYPE_ALPHA; function = 3; break;
|
||||||
|
case '9': type = TYPE_TONE; function = 0; break;
|
||||||
|
case '0': type = TYPE_TONE; function = 1; break;
|
||||||
|
default:
|
||||||
|
PDEBUG(DGOLAY, DEBUG_NOTICE, "Illegal function suffix '%c' in last address digit.\n", address[6]);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* override type */
|
||||||
|
if (force_type >= 0) {
|
||||||
|
type = force_type;
|
||||||
|
PDEBUG(DGOLAY, DEBUG_INFO, "Overriding message type as defined by sender.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == TYPE_ALPHA || type == TYPE_NUMERIC)
|
||||||
|
PDEBUG(DGOLAY, DEBUG_INFO, "Coding text message for functional address '%s' and message '%s'.\n", address, message);
|
||||||
|
else
|
||||||
|
PDEBUG(DGOLAY, DEBUG_INFO, "Coding tone only message for functional address %s.\n", address);
|
||||||
|
|
||||||
|
/* encode preamble and store */
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, "Encoding preamble '%d'.\n", preamble);
|
||||||
|
golay = calc_golay(preamble_values[preamble]);
|
||||||
|
queue_comma(gsc, 28, golay & 1);
|
||||||
|
for (i = 0; i < 18; i++) {
|
||||||
|
queue_dup(gsc, golay, 23);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* encode start code and store */
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, "Encoding start code.\n");
|
||||||
|
golay = calc_golay(start_code);
|
||||||
|
queue_comma(gsc, 28, golay & 1);
|
||||||
|
queue_dup(gsc, golay, 23);
|
||||||
|
golay ^= 0x7fffff;
|
||||||
|
queue_bit(gsc, (golay & 1) ^ 1);
|
||||||
|
queue_dup(gsc, golay, 23);
|
||||||
|
|
||||||
|
/* encode address and store */
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, "Encoding address words '%d' and '%d'.\n", word1, word2);
|
||||||
|
golay = calc_golay(word1);
|
||||||
|
if (function & 0x2)
|
||||||
|
golay ^= 0x7fffff;
|
||||||
|
queue_comma(gsc, 28, golay & 1);
|
||||||
|
queue_dup(gsc, golay, 23);
|
||||||
|
golay = calc_golay(word2);
|
||||||
|
if (function & 0x1)
|
||||||
|
golay ^= 0x7fffff;
|
||||||
|
queue_bit(gsc, (golay & 1) ^ 1);
|
||||||
|
queue_dup(gsc, golay, 23);
|
||||||
|
|
||||||
|
/* encode message */
|
||||||
|
if (type == TYPE_ALPHA) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, "Encoding %d alphanumeric digits.\n", (int)strlen(message));
|
||||||
|
for (i = 0; *message; i++) {
|
||||||
|
if (i == MAX_ADB) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_NOTICE, "Message overflows %d characters, cropping message.\n", MAX_ADB * 8);
|
||||||
|
}
|
||||||
|
for (j = 0; *message && j < 8; j++) {
|
||||||
|
msg[j] = encode_alpha(*message++);
|
||||||
|
}
|
||||||
|
/* fill empty characters with NULL */
|
||||||
|
while (j < 8)
|
||||||
|
msg[j++] = 0x3e;
|
||||||
|
/* 8 characters + continue-bit */
|
||||||
|
bch[0] = calc_bch((msg[0] | (msg[1] << 6)) & 0x7f);
|
||||||
|
bch[1] = calc_bch(((msg[1] >> 1) | (msg[2] << 5)) & 0x7f);
|
||||||
|
bch[2] = calc_bch(((msg[2] >> 2) | (msg[3] << 4)) & 0x7f);
|
||||||
|
bch[3] = calc_bch(((msg[3] >> 3) | (msg[4] << 3)) & 0x7f);
|
||||||
|
bch[4] = calc_bch(((msg[4] >> 4) | (msg[5] << 2)) & 0x7f);
|
||||||
|
bch[5] = calc_bch(((msg[5] >> 5) | (msg[6] << 1)) & 0x7f);
|
||||||
|
if (*message && i < MAX_ADB)
|
||||||
|
contbit = 1;
|
||||||
|
else
|
||||||
|
contbit = 0;
|
||||||
|
bch[6] = calc_bch((contbit << 6) | msg[7]);
|
||||||
|
/* checksum */
|
||||||
|
checksum = bch[0] + bch[1] + bch[2] + bch[3] + bch[4] + bch[5] + bch[6];
|
||||||
|
bch[7] = calc_bch(checksum & 0x7f);
|
||||||
|
/* store comma bit */
|
||||||
|
queue_bit(gsc, (bch[0] & 1) ^ 1); // inverted first bit
|
||||||
|
/* store interleaved bits */
|
||||||
|
for (j = 0; j < 15; j++) {
|
||||||
|
for (k = 0; k < 8; k++)
|
||||||
|
queue_bit(gsc, (bch[k] >> j) & 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type == TYPE_NUMERIC) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, "Encoding %d numeric digits.\n", (int)strlen(message));
|
||||||
|
shifted = 0;
|
||||||
|
for (i = 0; *message; i++) {
|
||||||
|
if (i == MAX_NDB) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_NOTICE, "Message overflows %d characters, cropping message.\n", MAX_NDB * 12);
|
||||||
|
}
|
||||||
|
for (j = 0; *message && j < 12; j++) {
|
||||||
|
/* get next digit or shifted digit */
|
||||||
|
if (shifted) {
|
||||||
|
digit = shifted & 0xf;
|
||||||
|
shifted = 0;
|
||||||
|
} else
|
||||||
|
digit = encode_numeric(*message);
|
||||||
|
/* if digit is extended, use the shifted code and later the digit itself */
|
||||||
|
if (digit > 0xf) {
|
||||||
|
shifted = digit;
|
||||||
|
msg[j] = digit >> 4;
|
||||||
|
} else {
|
||||||
|
msg[j] = digit;
|
||||||
|
message++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* fill empty digits with NULL */
|
||||||
|
while (j < 12)
|
||||||
|
msg[j++] = 0xa;
|
||||||
|
/* 8 digits + continue-bit */
|
||||||
|
bch[0] = calc_bch((msg[0] | (msg[1] << 4)) & 0x7f);
|
||||||
|
bch[1] = calc_bch(((msg[1] >> 3) | (msg[2] << 1) | (msg[3] << 5)) & 0x7f);
|
||||||
|
bch[2] = calc_bch(((msg[3] >> 2) | (msg[4] << 2) | (msg[5] << 6)) & 0x7f);
|
||||||
|
bch[3] = calc_bch(((msg[5] >> 1) | (msg[6] << 3)) & 0x7f);
|
||||||
|
bch[4] = calc_bch((msg[7] | (msg[8] << 4)) & 0x7f);
|
||||||
|
bch[5] = calc_bch(((msg[8] >> 3) | (msg[9] << 1) | (msg[10] << 5)) & 0x7f);
|
||||||
|
if (*message && i < MAX_NDB)
|
||||||
|
contbit = 1;
|
||||||
|
else
|
||||||
|
contbit = 0;
|
||||||
|
bch[6] = calc_bch((contbit << 6) | (msg[10] >> 2) | (msg[11] << 2));
|
||||||
|
/* checksum */
|
||||||
|
checksum = bch[0] + bch[1] + bch[2] + bch[3] + bch[4] + bch[5] + bch[6];
|
||||||
|
bch[7] = calc_bch(checksum & 0x7f);
|
||||||
|
/* store comma bit */
|
||||||
|
queue_bit(gsc, (bch[0] & 1) ^ 1); // inverted first bit
|
||||||
|
/* store interleaved bits */
|
||||||
|
for (j = 0; j < 15; j++) {
|
||||||
|
for (k = 0; k < 8; k++)
|
||||||
|
queue_bit(gsc, (bch[k] >> j) & 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* encode comma after message and store */
|
||||||
|
PDEBUG(DGOLAY, DEBUG_DEBUG, "Encoding 'comma' sequence after message.\n");
|
||||||
|
queue_comma(gsc, 121 * 8, 1);
|
||||||
|
|
||||||
|
/* check overflow */
|
||||||
|
if (gsc->bit_overflow) {
|
||||||
|
PDEBUG(DGOLAY, DEBUG_ERROR, "Bit stream (%d bits) overflows bit buffer size (%d bits), please fix!\n", gsc->bit_num, (int)sizeof(gsc->bit));
|
||||||
|
return -EOVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get next bit
|
||||||
|
*
|
||||||
|
* if there is no message, return -1, so that the transmitter is turned off.
|
||||||
|
*
|
||||||
|
* if there is a message, return next bit to be transmitted.
|
||||||
|
*
|
||||||
|
* if there is a message in the queue, encode message and return its first bit.
|
||||||
|
*/
|
||||||
|
int8_t get_bit(gsc_t *gsc)
|
||||||
|
{
|
||||||
|
gsc_msg_t *msg;
|
||||||
|
uint8_t bit;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* if currently transmiting message, send next bit */
|
||||||
|
if (gsc->bit_num) {
|
||||||
|
bit = gsc->bit[gsc->bit_index++];
|
||||||
|
if (gsc->bit_index == gsc->bit_num) {
|
||||||
|
queue_reset(gsc);
|
||||||
|
PDEBUG(DGOLAY, DEBUG_INFO, "Done transmitting message.\n");
|
||||||
|
}
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_msg:
|
||||||
|
msg = gsc->msg_list;
|
||||||
|
|
||||||
|
/* no message pending, turn transmitter off */
|
||||||
|
if (!msg)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* encode first message in queue */
|
||||||
|
rc = queue_batch(gsc, msg->address, msg->force_type, msg->data);
|
||||||
|
if (rc >= 0)
|
||||||
|
PDEBUG(DGOLAY, DEBUG_INFO, "Transmitting message to address '%s'.\n", msg->address);
|
||||||
|
golay_msg_destroy(gsc, msg);
|
||||||
|
if (rc < 0)
|
||||||
|
goto next_msg;
|
||||||
|
|
||||||
|
/* return first bit */
|
||||||
|
bit = gsc->bit[gsc->bit_index++];
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void golay_msg_send(const char *text)
|
||||||
|
{
|
||||||
|
char buffer[strlen(text) + 1], *p = buffer, *address_string, *message;
|
||||||
|
gsc_t *gsc;
|
||||||
|
int force_type = -1;
|
||||||
|
|
||||||
|
strcpy(buffer, text);
|
||||||
|
address_string = strsep(&p, ",");
|
||||||
|
message = p;
|
||||||
|
if (message) {
|
||||||
|
if (message[0] == 'a' && message[1] == ',') {
|
||||||
|
force_type = TYPE_ALPHA;
|
||||||
|
message += 2;
|
||||||
|
} else
|
||||||
|
if (message[0] == 'n' && message[1] == ',') {
|
||||||
|
force_type = TYPE_NUMERIC;
|
||||||
|
message += 2;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
message = "";
|
||||||
|
|
||||||
|
gsc = (gsc_t *) sender_head;
|
||||||
|
golay_msg_create(gsc, address_string, message, force_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_down_clock(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call control starts call towards paging network. */
|
||||||
|
int call_down_setup(int __attribute__((unused)) callref, const char *caller_id, enum number_type __attribute__((unused)) caller_type, const char *dialing)
|
||||||
|
{
|
||||||
|
char channel = '\0';
|
||||||
|
sender_t *sender;
|
||||||
|
gsc_t *gsc;
|
||||||
|
const char *address;
|
||||||
|
const char *message;
|
||||||
|
gsc_msg_t *msg;
|
||||||
|
|
||||||
|
/* find transmitter */
|
||||||
|
for (sender = sender_head; sender; sender = sender->next) {
|
||||||
|
/* skip channels that are different than requested */
|
||||||
|
if (channel && sender->kanal[0] != channel)
|
||||||
|
continue;
|
||||||
|
gsc = (gsc_t *) sender;
|
||||||
|
/* check if base station cannot transmit */
|
||||||
|
if (!gsc->tx)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!sender) {
|
||||||
|
if (channel)
|
||||||
|
PDEBUG(DGOLAY, DEBUG_NOTICE, "Cannot page, because given station not available, rejecting!\n");
|
||||||
|
else
|
||||||
|
PDEBUG(DGOLAY, DEBUG_NOTICE, "Cannot page, no trasmitting station available, rejecting!\n");
|
||||||
|
return -CAUSE_NOCHANNEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get address */
|
||||||
|
address = dialing;
|
||||||
|
|
||||||
|
/* get message */
|
||||||
|
if (caller_id[0])
|
||||||
|
message = caller_id;
|
||||||
|
else
|
||||||
|
message = gsc->default_message;
|
||||||
|
|
||||||
|
/* create call process to page station */
|
||||||
|
msg = golay_msg_create(gsc, address, message, -1);
|
||||||
|
if (!msg)
|
||||||
|
return -CAUSE_INVALNUMBER;
|
||||||
|
return -CAUSE_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_down_answer(int __attribute__((unused)) callref)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _release(int __attribute__((unused)) callref, int __attribute__((unused)) cause)
|
||||||
|
{
|
||||||
|
PDEBUG(DGOLAY, DEBUG_INFO, "Call has been disconnected by network.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void call_down_disconnect(int callref, int cause)
|
||||||
|
{
|
||||||
|
_release(callref, cause);
|
||||||
|
|
||||||
|
call_up_release(callref, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call control releases call toward mobile station. */
|
||||||
|
void call_down_release(int callref, int cause)
|
||||||
|
{
|
||||||
|
_release(callref, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Receive audio from call instance. */
|
||||||
|
void call_down_audio(int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_info(void) {}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
#include "../libmobile/sender.h"
|
||||||
|
|
||||||
|
#define TYPE_TONE 0 /* TONE only */
|
||||||
|
#define TYPE_VOICE 1 /* TONE + VOICE */
|
||||||
|
#define TYPE_ALPHA 2 /* TONE + DATA */
|
||||||
|
#define TYPE_NUMERIC 3 /* TONE + DATA */
|
||||||
|
#define MAX_ADB 10 /* 80 characters */
|
||||||
|
#define MAX_NDB 2 /* 24 digits */
|
||||||
|
|
||||||
|
/* instance of outgoing message */
|
||||||
|
typedef struct gsc_msg {
|
||||||
|
struct gsc_msg *next;
|
||||||
|
char address[8]; /* 7 digits + EOL */
|
||||||
|
int force_type; /* override type from address digit 7 */
|
||||||
|
char data[256]; /* message to be transmitted */
|
||||||
|
} gsc_msg_t;
|
||||||
|
|
||||||
|
typedef struct gsc {
|
||||||
|
sender_t sender;
|
||||||
|
int tx;
|
||||||
|
|
||||||
|
gsc_msg_t *msg_list; /* queue of messages */
|
||||||
|
const char *default_message;
|
||||||
|
|
||||||
|
/* current trasmitting message */
|
||||||
|
uint8_t bit[4096];
|
||||||
|
int bit_num;
|
||||||
|
int bit_index; /* when playing out */
|
||||||
|
int bit_overflow;
|
||||||
|
|
||||||
|
/* dsp states */
|
||||||
|
double fsk_deviation; /* deviation of FSK signal on sound card */
|
||||||
|
double fsk_polarity; /* polarity of FSK signal (-1.0 = bit '1' is down) */
|
||||||
|
sample_t fsk_ramp_up[256]; /* samples of upward ramp shape */
|
||||||
|
sample_t fsk_ramp_down[256]; /* samples of downward ramp shape */
|
||||||
|
double fsk_bitduration; /* duration of a bit in samples */
|
||||||
|
double fsk_bitstep; /* fraction of a bit each sample */
|
||||||
|
sample_t *fsk_tx_buffer; /* tx buffer for one data block */
|
||||||
|
int fsk_tx_buffer_size; /* size of tx buffer (in samples) */
|
||||||
|
int fsk_tx_buffer_length; /* usage of buffer (in samples) */
|
||||||
|
int fsk_tx_buffer_pos; /* current position sending buffer */
|
||||||
|
double fsk_tx_phase; /* current bit position */
|
||||||
|
uint8_t fsk_tx_lastbit; /* last bit of last message, to correctly ramp */
|
||||||
|
} gsc_t;
|
||||||
|
|
||||||
|
int golay_create(const char *kanal, double frequency, const char *device, int use_sdr, int samplerate, double rx_gain, double tx_gain, double deviation, double polarity, const char *message, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback);
|
||||||
|
void golay_destroy(sender_t *sender);
|
||||||
|
|
||||||
|
void init_golay(void);
|
||||||
|
void init_bch(void);
|
||||||
|
|
||||||
|
int8_t get_bit(gsc_t *gsc);
|
||||||
|
void golay_msg_send(const char *buffer);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
const char *aaimage[] = {
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,302 @@
|
||||||
|
/* Golay/GSC pager main
|
||||||
|
*
|
||||||
|
* (C) 2022 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 <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "../libsample/sample.h"
|
||||||
|
#include "../libdebug/debug.h"
|
||||||
|
#include "../libmobile/call.h"
|
||||||
|
#include "../libmobile/main_mobile.h"
|
||||||
|
#include "../liboptions/options.h"
|
||||||
|
#include "../libfm/fm.h"
|
||||||
|
#include "../amps/tones.h"
|
||||||
|
#include "../amps/noanswer.h"
|
||||||
|
#include "../amps/outoforder.h"
|
||||||
|
#include "../amps/invalidnumber.h"
|
||||||
|
#include "../amps/congestion.h"
|
||||||
|
#include "golay.h"
|
||||||
|
|
||||||
|
#define MSG_SEND "/tmp/golay_msg_send"
|
||||||
|
#define MSG_RECEIVED "/tmp/golay_msg_received"
|
||||||
|
static int msg_send_fd = -1;
|
||||||
|
|
||||||
|
static int tx = 0; /* we transmit */
|
||||||
|
static int rx = 0; /* we receive */
|
||||||
|
static double deviation = 4500; /* WB confirmed by an email: POCSAG and GSC have same deviation of +-4.5 kHz. */
|
||||||
|
static int deviation_given = 0;
|
||||||
|
static double polarity = 1;
|
||||||
|
static int polarity_given = 0;
|
||||||
|
static const char *message = "1234";
|
||||||
|
|
||||||
|
void print_help(const char *arg0)
|
||||||
|
{
|
||||||
|
main_mobile_print_help(arg0, "| -k 462.900 | -k <MHz> ");
|
||||||
|
/* - - */
|
||||||
|
printf(" -T --tx\n");
|
||||||
|
printf(" Transmit GSC signal on given channel, to page a receiver. (default)\n");
|
||||||
|
printf(" -R --rx\n");
|
||||||
|
printf(" Receive GSC signal on given channel, so we are the receiver.\n");
|
||||||
|
printf(" If none of the options -T nor -R is given, only transmitter is enabled.\n");
|
||||||
|
printf(" -D --deviation wide | 4.5 | narrow | 1.0 | <other KHz>\n"); /* NB confirmed by IQ data from signal-id-wiki */
|
||||||
|
printf(" Choose deviation of FFSK signal (default %.0f KHz).\n", deviation / 1000.0);
|
||||||
|
printf(" -P --polarity -1 | nagative | 1 | positive\n");
|
||||||
|
printf(" Choose polarity of FFSK signal. 'positive' means that a binary 1 uses\n");
|
||||||
|
printf(" positive and a binary 0 negative deviation. (default %s KHz).\n", (polarity < 0) ? "negative" : "positive");
|
||||||
|
printf(" -M --message \"...\"\n");
|
||||||
|
printf(" Send this message, if no caller ID was given or if built-in console\n");
|
||||||
|
printf(" is used. (default \"%s\").\n", message);
|
||||||
|
printf("\n");
|
||||||
|
printf("File: %s\n", MSG_SEND);
|
||||||
|
printf(" Write \"<address>[,message]\" to it, to send a default message.\n");
|
||||||
|
printf(" Write \"<address>,n,message\" to it, to send a numeric message.\n");
|
||||||
|
printf(" Write \"<address>,a,message\" to it, to send an alphanumeric message.\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("By default, an alphanumic message is sent, if last digit of the functional\n");
|
||||||
|
printf("address is 5..8. Otherwise a tone only message is sent.\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("A numeric message can have up to 24 digits, they are: 0123456789U-* and space\n");
|
||||||
|
printf("Also 'shifted' digits can be sent using two digits, they are: ABCDEFGHJLNPR\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("An aplhanumeric message can have up to 80 digits, sent upper case only.\n");
|
||||||
|
main_mobile_print_station_id();
|
||||||
|
main_mobile_print_hotkeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_options(void)
|
||||||
|
{
|
||||||
|
main_mobile_add_options();
|
||||||
|
option_add('T', "tx", 0);
|
||||||
|
option_add('R', "rx", 0);
|
||||||
|
option_add('D', "deviation", 1);
|
||||||
|
option_add('P', "polarity", 1);
|
||||||
|
option_add('M', "message", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_options(int short_option, int argi, char **argv)
|
||||||
|
{
|
||||||
|
switch (short_option) {
|
||||||
|
case 'T':
|
||||||
|
tx = 1;
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
rx = 1;
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
if (argv[argi][0] == 'n' || argv[argi][0] == 'N')
|
||||||
|
deviation = 1000.0;
|
||||||
|
else if (argv[argi][0] == 'w' || argv[argi][0] == 'W')
|
||||||
|
deviation = 4500.0;
|
||||||
|
else
|
||||||
|
deviation = atof(argv[argi]) * 1000.0;
|
||||||
|
if (deviation < 1000.0) {
|
||||||
|
fprintf(stderr, "Given deviation is too low, use higher deviation.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (deviation > 10000.0) {
|
||||||
|
fprintf(stderr, "Given deviation is too high, use lower deviation.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
deviation_given = 1;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
if (argv[argi][0] == 'n' || argv[argi][0] == 'N')
|
||||||
|
polarity = -1.0;
|
||||||
|
else if (argv[argi][0] == 'p' || argv[argi][0] == 'P')
|
||||||
|
polarity = 1.0;
|
||||||
|
else if (atoi(argv[argi]) == -1)
|
||||||
|
polarity = -1.0;
|
||||||
|
else if (atoi(argv[argi]) == 1)
|
||||||
|
polarity = 1.0;
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "Given polarity is not positive nor negative, use '-h' for help.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
polarity_given = 1;
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
message = options_strdup(argv[argi++]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return main_mobile_handle_options(short_option, argi, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void myhandler(void)
|
||||||
|
{
|
||||||
|
static char buffer[256];
|
||||||
|
static int pos = 0, rc, i;
|
||||||
|
int space = sizeof(buffer) - pos;
|
||||||
|
|
||||||
|
rc = read(msg_send_fd, buffer + pos, space);
|
||||||
|
if (rc > 0) {
|
||||||
|
pos += rc;
|
||||||
|
if (pos == space) {
|
||||||
|
fprintf(stderr, "Message buffer overflow!\n");
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
/* check for end of line */
|
||||||
|
for (i = 0; i < pos; i++) {
|
||||||
|
if (buffer[i] == '\r' || buffer[i] == '\n')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* send msg */
|
||||||
|
if (i < pos) {
|
||||||
|
buffer[i] = '\0';
|
||||||
|
pos = 0;
|
||||||
|
if (tx)
|
||||||
|
golay_msg_send(buffer);
|
||||||
|
else
|
||||||
|
PDEBUG(DGOLAY, DEBUG_ERROR, "Failed to send message, transmitter is not enabled!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct number_lengths number_lengths[] = {
|
||||||
|
{ 7, "functional address" },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int rc, argi;
|
||||||
|
const char *station_id = "";
|
||||||
|
int i;
|
||||||
|
double frequency;
|
||||||
|
|
||||||
|
/* GSC does not use emphasis, so disable it */
|
||||||
|
uses_emphasis = 0;
|
||||||
|
|
||||||
|
/* init common tones */
|
||||||
|
init_tones();
|
||||||
|
init_outoforder();
|
||||||
|
init_noanswer();
|
||||||
|
init_invalidnumber();
|
||||||
|
init_congestion();
|
||||||
|
|
||||||
|
/* init coding tables */
|
||||||
|
init_golay();
|
||||||
|
init_bch();
|
||||||
|
|
||||||
|
/* init mobile interface */
|
||||||
|
main_mobile_init("0123456789", number_lengths, NULL, NULL);
|
||||||
|
|
||||||
|
/* handle options / config file */
|
||||||
|
add_options();
|
||||||
|
rc = options_config_file(argc, argv, "~/.osmocom/analog/golay.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];
|
||||||
|
rc = main_mobile_number_ask(station_id, "functional address");
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!num_kanal) {
|
||||||
|
printf("No channel is specified, Use '-k <MHz>' to define frequency.\n\n");
|
||||||
|
print_help(argv[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (use_sdr) {
|
||||||
|
/* set device */
|
||||||
|
for (i = 0; i < num_kanal; i++)
|
||||||
|
dsp_device[i] = "sdr";
|
||||||
|
num_device = num_kanal;
|
||||||
|
}
|
||||||
|
if (num_kanal == 1 && num_device == 0)
|
||||||
|
num_device = 1; /* use default */
|
||||||
|
if (num_kanal != num_device) {
|
||||||
|
fprintf(stderr, "You need to specify as many sound devices as you have channels.\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TX is default */
|
||||||
|
if (!tx && !rx)
|
||||||
|
tx = 1;
|
||||||
|
|
||||||
|
if (rx) {
|
||||||
|
fprintf(stderr, "Sorry, but RX is not yet supported and maybe never will.\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TX & RX if loopback */
|
||||||
|
if (loopback)
|
||||||
|
tx = rx = 1;
|
||||||
|
|
||||||
|
/* create pipe for message sendy */
|
||||||
|
unlink(MSG_SEND);
|
||||||
|
rc = mkfifo(MSG_SEND, 0666);
|
||||||
|
if (rc < 0) {
|
||||||
|
fprintf(stderr, "Failed to create mwaaage send FIFO '%s'!\n", MSG_SEND);
|
||||||
|
goto fail;
|
||||||
|
} else {
|
||||||
|
msg_send_fd = open(MSG_SEND, O_RDONLY | O_NONBLOCK);
|
||||||
|
if (msg_send_fd < 0) {
|
||||||
|
fprintf(stderr, "Failed to open mwaaage send FIFO! '%s'\n", MSG_SEND);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* inits */
|
||||||
|
fm_init(fast_math);
|
||||||
|
|
||||||
|
/* create transceiver instance */
|
||||||
|
for (i = 0; i < num_kanal; i++) {
|
||||||
|
frequency = atof(kanal[i]) * 1e6;
|
||||||
|
rc = golay_create(kanal[i], frequency, dsp_device[i], use_sdr, dsp_samplerate, rx_gain, tx_gain, deviation, polarity, message, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback);
|
||||||
|
if (rc < 0) {
|
||||||
|
fprintf(stderr, "Failed to create \"Sender\" instance. Quitting!\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
printf("Base station ready, please tune transmitter (or receiver) to %.4f MHz\n", frequency / 1e6);
|
||||||
|
}
|
||||||
|
|
||||||
|
main_mobile_loop("golay", &quit, myhandler, station_id);
|
||||||
|
|
||||||
|
fail:
|
||||||
|
/* pipe */
|
||||||
|
if (msg_send_fd > 0)
|
||||||
|
close(msg_send_fd);
|
||||||
|
unlink(MSG_SEND);
|
||||||
|
|
||||||
|
/* destroy transceiver instance */
|
||||||
|
while(sender_head)
|
||||||
|
golay_destroy(sender_head);
|
||||||
|
|
||||||
|
/* exits */
|
||||||
|
fm_exit();
|
||||||
|
|
||||||
|
options_free();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ struct debug_cat {
|
||||||
{ "jollycom", "\033[1;34m" },
|
{ "jollycom", "\033[1;34m" },
|
||||||
{ "eurosignal", "\033[1;34m" },
|
{ "eurosignal", "\033[1;34m" },
|
||||||
{ "pocsag", "\033[1;34m" },
|
{ "pocsag", "\033[1;34m" },
|
||||||
|
{ "golay", "\033[1;34m" },
|
||||||
{ "5-ton-folge", "\033[1;34m" },
|
{ "5-ton-folge", "\033[1;34m" },
|
||||||
{ "frame", "\033[0;36m" },
|
{ "frame", "\033[0;36m" },
|
||||||
{ "call", "\033[0;37m" },
|
{ "call", "\033[0;37m" },
|
||||||
|
|
|
@ -19,43 +19,44 @@
|
||||||
#define DJOLLY 12
|
#define DJOLLY 12
|
||||||
#define DEURO 13
|
#define DEURO 13
|
||||||
#define DPOCSAG 14
|
#define DPOCSAG 14
|
||||||
#define DFUENF 15
|
#define DGOLAY 15
|
||||||
#define DFRAME 16
|
#define DFUENF 16
|
||||||
#define DCALL 17
|
#define DFRAME 17
|
||||||
#define DCC 18
|
#define DCALL 18
|
||||||
#define DDB 19
|
#define DCC 19
|
||||||
#define DTRANS 20
|
#define DDB 20
|
||||||
#define DDMS 21
|
#define DTRANS 21
|
||||||
#define DSMS 22
|
#define DDMS 22
|
||||||
#define DSDR 23
|
#define DSMS 23
|
||||||
#define DUHD 24
|
#define DSDR 24
|
||||||
#define DSOAPY 25
|
#define DUHD 25
|
||||||
#define DWAVE 26
|
#define DSOAPY 26
|
||||||
#define DRADIO 27
|
#define DWAVE 27
|
||||||
#define DAM791X 28
|
#define DRADIO 28
|
||||||
#define DUART 29
|
#define DAM791X 29
|
||||||
#define DDEVICE 30
|
#define DUART 30
|
||||||
#define DDATENKLO 31
|
#define DDEVICE 31
|
||||||
#define DZEIT 32
|
#define DDATENKLO 32
|
||||||
#define DSIM1 33
|
#define DZEIT 33
|
||||||
#define DSIM2 34
|
#define DSIM1 34
|
||||||
#define DSIMI 35
|
#define DSIM2 35
|
||||||
#define DSIM7 36
|
#define DSIMI 36
|
||||||
#define DMTP2 37
|
#define DSIM7 37
|
||||||
#define DMTP3 38
|
#define DMTP2 38
|
||||||
#define DMUP 39
|
#define DMTP3 39
|
||||||
#define DROUTER 40
|
#define DMUP 40
|
||||||
#define DSTDERR 41
|
#define DROUTER 41
|
||||||
#define DSS5 42
|
#define DSTDERR 42
|
||||||
#define DISDN 43
|
#define DSS5 43
|
||||||
#define DMISDN 44
|
#define DISDN 44
|
||||||
#define DDSS1 45
|
#define DMISDN 45
|
||||||
#define DSIP 46
|
#define DDSS1 46
|
||||||
#define DTEL 47
|
#define DSIP 47
|
||||||
#define DUK0 48
|
#define DTEL 48
|
||||||
#define DPH 49
|
#define DUK0 49
|
||||||
#define DDCF77 50
|
#define DPH 50
|
||||||
#define DJITTER 51
|
#define DDCF77 51
|
||||||
|
#define DJITTER 52
|
||||||
//NOTE: increment mask array, if 127 is exceeded
|
//NOTE: increment mask array, if 127 is exceeded
|
||||||
|
|
||||||
void lock_debug(void);
|
void lock_debug(void);
|
||||||
|
|
Loading…
Reference in New Issue