aboutsummaryrefslogtreecommitdiffstats
path: root/src/bnetz
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2018-02-15 07:39:45 +0100
committerAndreas Eversberg <jolly@eversberg.eu>2018-02-15 08:00:20 +0100
commit56f07a473d403a84afcf1e639d499eeacda0952a (patch)
tree2b34324128acf1926b9cf9e1ff93123605f5919a /src/bnetz
parenta5aee82e5383e3106924170e60fe10d5bb8f9c73 (diff)
B-Netz: Add dial sequence generator to emulate call setup from mobile phone
Diffstat (limited to 'src/bnetz')
-rw-r--r--src/bnetz/Makefile.am26
-rw-r--r--src/bnetz/dialer.c416
2 files changed, 441 insertions, 1 deletions
diff --git a/src/bnetz/Makefile.am b/src/bnetz/Makefile.am
index e00c026..bc562e9 100644
--- a/src/bnetz/Makefile.am
+++ b/src/bnetz/Makefile.am
@@ -1,7 +1,8 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
bin_PROGRAMS = \
- bnetz
+ bnetz \
+ bnetz-dialer
bnetz_SOURCES = \
bnetz.c \
@@ -30,10 +31,29 @@ bnetz_LDADD = \
$(top_builddir)/src/libsample/libsample.a \
-lm
+bnetz_dialer_SOURCES = \
+ telegramm.c \
+ dialer.c
+bnetz_dialer_LDADD = \
+ $(COMMON_LA) \
+ $(top_builddir)/src/libdebug/libdebug.a \
+ $(top_builddir)/src/libfsk/libfsk.a \
+ $(top_builddir)/src/libfm/libfm.a \
+ $(top_builddir)/src/libfilter/libfilter.a \
+ $(top_builddir)/src/libwave/libwave.a \
+ $(top_builddir)/src/libmncc/libmncc.a \
+ $(top_builddir)/src/libsample/libsample.a \
+ $(ALSA_LIBS)
+ -lm
+
if HAVE_ALSA
bnetz_LDADD += \
$(top_builddir)/src/libsound/libsound.a \
$(ALSA_LIBS)
+
+bnetz_dialer_LDADD += \
+ $(top_builddir)/src/libsound/libsound.a \
+ $(ALSA_LIBS)
endif
if HAVE_SDR
@@ -44,3 +64,7 @@ bnetz_LDADD += \
$(SOAPY_LIBS)
endif
+if HAVE_ALSA
+AM_CPPFLAGS += -DHAVE_ALSA
+endif
+
diff --git a/src/bnetz/dialer.c b/src/bnetz/dialer.c
new file mode 100644
index 0000000..a3a292d
--- /dev/null
+++ b/src/bnetz/dialer.c
@@ -0,0 +1,416 @@
+/* B-Netz dialer
+ *
+ * (C) 2018 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 <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "../libsample/sample.h"
+#include "../libfsk/fsk.h"
+#include "../libwave/wave.h"
+#include "../libdebug/debug.h"
+#ifdef HAVE_ALSA
+#include "../libsound/sound.h"
+#endif
+#include "telegramm.h"
+
+#define MAX_PAUSE 0.5 /* pause before and after dialing sequence */
+#define F0 2070.0
+#define F1 1950.0
+#define BIT_RATE 100.0
+
+/* presets */
+char start_digit = 's';
+const char *station_id = "50993";
+const char *dialing;
+const char *audiodev = "hw:0,0";
+int samplerate = 48000;
+const char *write_tx_wave = NULL;
+int latency = 50;
+
+/* states */
+enum tx_mode {
+ TX_MODE_SILENCE,
+ TX_MODE_FSK,
+ TX_MODE_DONE,
+} tx_mode = TX_MODE_SILENCE;
+int tx_silence_count = 0;
+char funkwahl[128];
+int digit_pos = 0;
+const char *tx_telegramm = NULL;
+int tx_telegramm_pos = 0;
+int latspl;
+
+/* instances */
+fsk_t fsk;
+#ifdef HAVE_ALSA
+void *audio = NULL;
+#endif
+wave_rec_t wave_tx_rec;
+
+/* dummy functions */
+int num_kanal = 1; /* only one channel used for debugging */
+void display_status_limit_scroll() {}
+void *get_sender_by_empfangsfrequenz() { return NULL; }
+void display_measurements_add() {}
+void display_measurements_update() {}
+
+#define OPT_METERING 1000
+#define OPT_COIN_BOX 1001
+
+static void print_help(const char *arg0)
+{
+ printf("Usage: %s [options] <number>\n\n", arg0);
+ /* - - */
+ printf("This program generates a dialing sequence to make a call via B-Netz base\n");
+ printf("station using an amateur radio transceiver.\n");
+ printf("Also it can write an audio file (wave) to be fed into B-Netz base station for\n");
+ printf("showing how it simulates a B-Netz phone doing an outgoing call.\n\n");
+ printf(" -h --help\n");
+ printf(" This help\n");
+ printf(" -i --station-id <station ID>\n");
+ printf(" 5 Digits of ID of mobile station (default = '%s')\n", station_id);
+#ifdef HAVE_ALSA
+ printf(" -a --audio-device hw:<card>,<device>\n");
+ printf(" Sound card and device number (default = '%s')\n", audiodev);
+#endif
+ printf(" Don't set it for SDR!\n");
+ printf(" -s --samplerate <rate>\n");
+ printf(" Sample rate of sound device (default = '%d')\n", samplerate);
+ printf(" -w --write-tx-wave <file>\n");
+ printf(" Write audio to given wave file also.\n");
+ printf(" -g --gebuehenimpuls\n");
+ printf(" -g --metering\n");
+ printf(" Indicate to base station that we have a charge meter on board.\n");
+ printf(" This will allow the base station to send billing tones during call.\n");
+ printf(" -m --muenztelefon\n");
+ printf(" -m --coin-box\n");
+ printf(" Indicate to base station that we are a pay phone. ('Muenztelefon')\n");
+}
+
+static int handle_options(int argc, char **argv)
+{
+ const char *optstring;
+ int skip_args = 0;
+
+ static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"station-id", 1, 0, 'i'},
+ {"audio-device", 1, 0, 'a'},
+ {"samplerate", 1, 0, 's'},
+ {"write-tx-wave", 1, 0, 'w'},
+ {"gebuehrenimpuls", 0, 0, 'g'},
+ {"metering", 0, 0, OPT_METERING},
+ {"muenztelefon", 0, 0, 'm'},
+ {"coin-box", 0, 0, OPT_COIN_BOX},
+ {0, 0, 0, 0},
+ };
+
+ optstring = "hi:a:s:w:gm";
+
+ while (1) {
+ int option_index = 0, c;
+
+ c = getopt_long(argc, argv, optstring, long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_help(argv[0]);
+ exit(0);
+ case 'i':
+ station_id = strdup(optarg);
+ skip_args += 2;
+ break;
+ case 'a':
+ audiodev = strdup(optarg);
+ skip_args += 2;
+ break;
+ case 's':
+ samplerate = atoi(optarg);
+ skip_args += 2;
+ break;
+ case 'w':
+ write_tx_wave = strdup(optarg);
+ skip_args += 2;
+ break;
+ case 'g':
+ case OPT_METERING:
+ start_digit = 'S';
+ skip_args += 1;
+ break;
+ case 'm':
+ case OPT_COIN_BOX:
+ start_digit = 'M';
+ skip_args += 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return skip_args;
+}
+
+
+/* process next fsk bit.
+ * if the dial string terminats, change to SILENCE mode
+ */
+static int fsk_send_bit(void __attribute__((unused)) *inst)
+{
+ struct impulstelegramm *impulstelegramm;
+
+ if (!tx_telegramm || tx_telegramm_pos == 16) {
+ switch (funkwahl[digit_pos]) {
+ case '\0':
+ PDEBUG(DBNETZ, DEBUG_INFO, "Done sending dialing sequence\n");
+ tx_mode = TX_MODE_SILENCE;
+ tx_silence_count = 0;
+ return -1;
+ case 'w':
+ if (!tx_telegramm)
+ PDEBUG(DBNETZ, DEBUG_INFO, "Sending channel allocation tone ('Kanalbelegung')\n");
+ tx_telegramm = "0000000000000000";
+ tx_telegramm_pos = 0;
+ digit_pos++;
+ break;
+ default:
+ switch (funkwahl[digit_pos]) {
+ case 's':
+ PDEBUG(DBNETZ, DEBUG_INFO, "Sending start digit (no charging meater on board)\n");
+ break;
+ case 'S':
+ PDEBUG(DBNETZ, DEBUG_INFO, "Sending start digit (with charging meater on board)\n");
+ break;
+ case 'M':
+ PDEBUG(DBNETZ, DEBUG_INFO, "Sending start digit (Phone is a coin box.)\n");
+ break;
+ case 'e':
+ PDEBUG(DBNETZ, DEBUG_INFO, "Sending stop digit\n");
+ break;
+ default:
+ PDEBUG(DBNETZ, DEBUG_INFO, "Sending digit '%c'\n", funkwahl[digit_pos]);
+ }
+ impulstelegramm = bnetz_digit2telegramm(funkwahl[digit_pos]);
+ if (!impulstelegramm) {
+ PDEBUG(DBNETZ, DEBUG_ERROR, "Illegal digit '%c', please fix!\n", funkwahl[digit_pos]);
+ abort();
+ }
+ tx_telegramm = impulstelegramm->sequence;
+ tx_telegramm_pos = 0;
+ digit_pos++;
+ }
+ }
+
+ return tx_telegramm[tx_telegramm_pos++];
+}
+
+/* encode audio */
+static void encode_audio(sample_t *samples, uint8_t *power, int length)
+{
+ int count;
+
+ memset(power, 1, length);
+
+again:
+ switch (tx_mode) {
+ case TX_MODE_SILENCE:
+ memset(samples, 0, length * sizeof(*samples));
+ tx_silence_count += length;
+ if (tx_silence_count >= (int)((double)samplerate * MAX_PAUSE)) {
+ if (funkwahl[digit_pos])
+ tx_mode = TX_MODE_FSK;
+ else
+ tx_mode = TX_MODE_DONE;
+ }
+ break;
+ case TX_MODE_FSK:
+ /* send FSK until it stops, then fill with silence */
+ count = fsk_send(&fsk, samples, length, 0);
+ samples += count;
+ length -= count;
+ if (length)
+ goto again;
+ break;
+ default:
+ break;
+ }
+}
+
+/* loop that gets audio from encoder and fowards it to sound card.
+ * alternatively a sound file is written.
+ */
+static void process_signal(void)
+{
+ sample_t buff[latspl], *samples[1] = { buff };
+ uint8_t pbuff[latspl], *power[1] = { pbuff };
+ int count;
+ int __attribute__((unused)) rc;
+
+ while (tx_mode != TX_MODE_DONE) {
+#ifdef HAVE_ALSA
+ count = sound_get_tosend(audio, latspl);
+#else
+ count = samplerate / 1000;
+#endif
+ if (count < 0) {
+ PDEBUG(DDSP, DEBUG_ERROR, "Failed to get number of samples in buffer (rc = %d)!\n", count);
+ break;
+ }
+
+ /* get fsk / silence */
+ encode_audio(samples[0], power[0], count);
+
+ /* write wave, if open */
+ if (wave_tx_rec.fp)
+ wave_write(&wave_tx_rec, samples, count);
+
+#ifdef HAVE_ALSA
+ /* write audio */
+ rc = sound_write(audio, samples, power, count, NULL, NULL, 1);
+ if (rc < 0) {
+ PDEBUG(DDSP, DEBUG_ERROR, "Failed to write TX data to audio device (rc = %d)\n", rc);
+ break;
+ }
+#endif
+
+ /* sleep a while */
+ usleep(1000);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ const char *arg0 = argv[0];
+ int skip_args;
+ int i;
+ int rc;
+
+ /* init */
+ bnetz_init_telegramm();
+ memset(&fsk, 0, sizeof(fsk));
+
+ /* latency of send buffer in samples */
+ latspl = samplerate * latency / 1000;
+
+ skip_args = handle_options(argc, argv);
+ argc -= skip_args;
+ argv += skip_args;
+
+ if (argc <= 1) {
+ printf("No phone number given!\n\n");
+ print_help(arg0);
+ goto exit;
+ }
+
+ /* check for valid station ID */
+ if (strlen(station_id) != 5) {
+ printf("Given station ID '%s' has too many digits!\n", station_id);
+ goto exit;
+ }
+ for (i = 0; station_id[i]; i++) {
+ if (station_id[i] < '0' || station_id[i] > '9') {
+ printf("Given station ID '%s' has invalid digits!\n", station_id);
+ goto exit;
+ }
+ }
+
+ /* check for valid phone number */
+ dialing = argv[1];
+ if (strlen(dialing) < 4) {
+ printf("Given phone number '%s' has too few digits! (less than minimum of 4 digits)\n", dialing);
+ goto exit;
+ }
+ if (strlen(dialing) > 14) {
+ printf("Given phone number '%s' has too many digits! (more than allowed 14 digits)\n", dialing);
+ goto exit;
+ }
+ for (i = 0; dialing[i]; i++) {
+ if (dialing[i] < '0' || dialing[i] > '9') {
+ printf("Given phone number '%s' has invalid digits!\n", dialing);
+ goto exit;
+ }
+ }
+ if (dialing[0] != '0') {
+ printf("Given phone number '%s' does not start with 0!\n", dialing);
+ goto exit;
+ }
+
+ /* dial string: 640 ms pause, 640 ms 2070 HZ, 2 * {start, station ID, number, stop}, 640 ms pause */
+ sprintf(funkwahl, "wwww%c%s%se%c%s%se", start_digit, station_id, dialing + 1, start_digit, station_id, dialing + 1);
+
+ /* init fsk */
+ if (fsk_init(&fsk, NULL, fsk_send_bit, NULL, samplerate, BIT_RATE, F0, F1, 1.0, 0, 0) < 0) {
+ PDEBUG(DDSP, DEBUG_ERROR, "FSK init failed!\n");
+ goto exit;
+ }
+
+#ifdef HAVE_ALSA
+ /* init sound */
+ audio = sound_open(audiodev, NULL, NULL, 1, 0.0, samplerate, latspl, 1.0, 4000.0);
+ if (!audio) {
+ PDEBUG(DBNETZ, DEBUG_ERROR, "No sound device!\n");
+ goto exit;
+ }
+#endif
+
+ /* open wave */
+ if (write_tx_wave) {
+ rc = wave_create_record(&wave_tx_rec, write_tx_wave, samplerate, 1, 1.0);
+ if (rc < 0) {
+ PDEBUG(DBNETZ, DEBUG_ERROR, "Failed to create WAVE recoding instance!\n");
+ goto exit;
+ }
+ }
+#ifndef HAVE_ALSA
+ else {
+ PDEBUG(DBNETZ, DEBUG_ERROR, "No sound support compiled in, so you need to write to a wave file. See help!\n");
+ goto exit;
+ }
+#endif
+
+#ifdef HAVE_ALSA
+ /* start sound */
+ sound_start(audio);
+#endif
+
+ PDEBUG(DBNETZ, DEBUG_ERROR, "Start audio after pause...\n");
+
+ process_signal();
+
+exit:
+ /* close wave */
+ wave_destroy_record(&wave_tx_rec);
+
+#ifdef HAVE_ALSA
+ /* exit sound */
+ if (audio)
+ sound_close(audio);
+#endif
+
+ /* exit fsk */
+ fsk_cleanup(&fsk);
+
+ return 0;
+}
+