From 8e67c3fda026519abb73f925371a87d596571639 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 23 Oct 2022 21:42:38 +0200 Subject: [PATCH] Golay/GSC paging support --- .gitignore | 1 + README | 1 + configure.ac | 1 + docs/index.html | 3 +- src/Makefile.am | 1 + src/golay/Makefile.am | 50 +++ src/golay/dsp.c | 209 +++++++++++ src/golay/dsp.h | 4 + src/golay/golay.c | 823 ++++++++++++++++++++++++++++++++++++++++++ src/golay/golay.h | 54 +++ src/golay/image.c | 6 + src/golay/main.c | 302 ++++++++++++++++ src/libdebug/debug.c | 1 + src/libdebug/debug.h | 75 ++-- 14 files changed, 1493 insertions(+), 38 deletions(-) create mode 100644 src/golay/Makefile.am create mode 100644 src/golay/dsp.c create mode 100644 src/golay/dsp.h create mode 100644 src/golay/golay.c create mode 100644 src/golay/golay.h create mode 100644 src/golay/image.c create mode 100644 src/golay/main.c diff --git a/.gitignore b/.gitignore index 7207dfd..ad59228 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ src/mpt1327/mpt1327 src/jolly/jollycom src/eurosignal/eurosignal src/pocsag/pocsag +src/golay/golay src/fuenf/5-ton-folge src/tv/osmotv src/radio/osmoradio diff --git a/README b/README index 4a0b331..bc0815b 100644 --- a/README +++ b/README @@ -27,6 +27,7 @@ Additionally the following communication services are implemented: * Analog Modem Emulation 'Datenklo' (AM7911) * German classic 'Zeitansage' (talking clock) * POCSAG transmitter / receiver + * Golay/GSC transmitter / receiver * DCF77 time signal transmitter and receiver * C-Netz SIM emulator * C-Netz magnetic card emulator diff --git a/configure.ac b/configure.ac index 8dea9de..d11f741 100644 --- a/configure.ac +++ b/configure.ac @@ -104,6 +104,7 @@ AC_OUTPUT( src/jolly/Makefile src/eurosignal/Makefile src/pocsag/Makefile + src/golay/Makefile src/fuenf/Makefile src/tv/Makefile src/radio/Makefile diff --git a/docs/index.html b/docs/index.html index 5e96f7a..9f1be76 100644 --- a/docs/index.html +++ b/docs/index.html @@ -129,7 +129,8 @@ Additional features:
  • C-Netz Magnetic Card
  • Zeitansage (German talking clock)
  • C-Netz FuVSt (MSC to control a real base station)
  • -
  • POCSAG
  • +
  • POCSAG (paging system)
  • +
  • Golay / GSC (paging system)
  • 5-Ton-Ruf (firefighter's pagers and siren control)
  • diff --git a/src/Makefile.am b/src/Makefile.am index dafe363..d81dbd7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -54,6 +54,7 @@ SUBDIRS += \ jolly \ eurosignal \ pocsag \ + golay \ fuenf \ tv \ radio \ diff --git a/src/golay/Makefile.am b/src/golay/Makefile.am new file mode 100644 index 0000000..d7cc8fe --- /dev/null +++ b/src/golay/Makefile.am @@ -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 + diff --git a/src/golay/dsp.c b/src/golay/dsp.c new file mode 100644 index 0000000..24d9ac7 --- /dev/null +++ b/src/golay/dsp.c @@ -0,0 +1,209 @@ +/* GSC signal processing + * + * (C) 2022 by Andreas Eversberg + * 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 . + */ + +#define CHAN gsc->sender.kanal + +#include +#include +#include +#include +#include +#include +#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; +} + + diff --git a/src/golay/dsp.h b/src/golay/dsp.h new file mode 100644 index 0000000..0d0e143 --- /dev/null +++ b/src/golay/dsp.h @@ -0,0 +1,4 @@ + +int dsp_init_sender(gsc_t *gsc, int samplerate, double deviation, double polarity); +void dsp_cleanup_sender(gsc_t *gsc); + diff --git a/src/golay/golay.c b/src/golay/golay.c new file mode 100644 index 0000000..3f1d8e8 --- /dev/null +++ b/src/golay/golay.c @@ -0,0 +1,823 @@ +/* Golay/GSC transcoding (encoding only - maybe) + * + * (C) 2022 by Andreas Eversberg + * All Rights Reserved + * + * Inspired by GSC code written by Brandon Creighton . + * + * Inspired by GOLAY code written by Robert Morelos-Zaragoza + * . + * + * 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 . + */ + +/* Golay code was is use since 1973, the GSC extension was used after 1982. + */ + +#include +#include +#include +#include +#include +#include +#include +#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 (" ", 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) {} + diff --git a/src/golay/golay.h b/src/golay/golay.h new file mode 100644 index 0000000..24e201f --- /dev/null +++ b/src/golay/golay.h @@ -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); + diff --git a/src/golay/image.c b/src/golay/image.c new file mode 100644 index 0000000..9fff620 --- /dev/null +++ b/src/golay/image.c @@ -0,0 +1,6 @@ +#include + +const char *aaimage[] = { + NULL +}; + diff --git a/src/golay/main.c b/src/golay/main.c new file mode 100644 index 0000000..09c8dad --- /dev/null +++ b/src/golay/main.c @@ -0,0 +1,302 @@ +/* Golay/GSC pager main + * + * (C) 2022 by Andreas Eversberg + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 "); + /* - - */ + 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 | \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 \"
    [,message]\" to it, to send a default message.\n"); + printf(" Write \"
    ,n,message\" to it, to send a numeric message.\n"); + printf(" Write \"
    ,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 ' 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; +} + diff --git a/src/libdebug/debug.c b/src/libdebug/debug.c index bf23035..f7b91e0 100755 --- a/src/libdebug/debug.c +++ b/src/libdebug/debug.c @@ -57,6 +57,7 @@ struct debug_cat { { "jollycom", "\033[1;34m" }, { "eurosignal", "\033[1;34m" }, { "pocsag", "\033[1;34m" }, + { "golay", "\033[1;34m" }, { "5-ton-folge", "\033[1;34m" }, { "frame", "\033[0;36m" }, { "call", "\033[0;37m" }, diff --git a/src/libdebug/debug.h b/src/libdebug/debug.h index ba55fdb..5942651 100644 --- a/src/libdebug/debug.h +++ b/src/libdebug/debug.h @@ -19,43 +19,44 @@ #define DJOLLY 12 #define DEURO 13 #define DPOCSAG 14 -#define DFUENF 15 -#define DFRAME 16 -#define DCALL 17 -#define DCC 18 -#define DDB 19 -#define DTRANS 20 -#define DDMS 21 -#define DSMS 22 -#define DSDR 23 -#define DUHD 24 -#define DSOAPY 25 -#define DWAVE 26 -#define DRADIO 27 -#define DAM791X 28 -#define DUART 29 -#define DDEVICE 30 -#define DDATENKLO 31 -#define DZEIT 32 -#define DSIM1 33 -#define DSIM2 34 -#define DSIMI 35 -#define DSIM7 36 -#define DMTP2 37 -#define DMTP3 38 -#define DMUP 39 -#define DROUTER 40 -#define DSTDERR 41 -#define DSS5 42 -#define DISDN 43 -#define DMISDN 44 -#define DDSS1 45 -#define DSIP 46 -#define DTEL 47 -#define DUK0 48 -#define DPH 49 -#define DDCF77 50 -#define DJITTER 51 +#define DGOLAY 15 +#define DFUENF 16 +#define DFRAME 17 +#define DCALL 18 +#define DCC 19 +#define DDB 20 +#define DTRANS 21 +#define DDMS 22 +#define DSMS 23 +#define DSDR 24 +#define DUHD 25 +#define DSOAPY 26 +#define DWAVE 27 +#define DRADIO 28 +#define DAM791X 29 +#define DUART 30 +#define DDEVICE 31 +#define DDATENKLO 32 +#define DZEIT 33 +#define DSIM1 34 +#define DSIM2 35 +#define DSIMI 36 +#define DSIM7 37 +#define DMTP2 38 +#define DMTP3 39 +#define DMUP 40 +#define DROUTER 41 +#define DSTDERR 42 +#define DSS5 43 +#define DISDN 44 +#define DMISDN 45 +#define DDSS1 46 +#define DSIP 47 +#define DTEL 48 +#define DUK0 49 +#define DPH 50 +#define DDCF77 51 +#define DJITTER 52 //NOTE: increment mask array, if 127 is exceeded void lock_debug(void);