Add support for "Eurosignal", a classic paging service

This commit is contained in:
Andreas Eversberg 2019-11-29 16:19:56 +01:00
parent 4af2dca10d
commit 90c47658c1
21 changed files with 9517 additions and 17 deletions

1
.gitignore vendored
View File

@ -63,6 +63,7 @@ src/r2000/radiocom2000
src/imts/imts
src/imts/imts-dialer
src/jolly/jollycom
src/eurosignal/eurosignal
src/tv/osmotv
src/radio/osmoradio
src/datenklo/datenklo

9
README
View File

@ -14,8 +14,15 @@ generated simultaniously using SDR. Currently supported networks:
* JTACS (Japanese version of TACS)
* Radiocom 2000 (French network)
* IMTS / MTS ((Improved) Mobile Telephone Service)
* Eurosignal (ERuRD paging service)
* JollyCom (Unofficial network, invented by the author)
Additionally the following communication services are implemented:
* TV Transmitter with test Images
* Radio transmitter / receiver
* Analog Modem Emulation (AM7911)
USE AT YOUR OWN RISK!
@ -33,7 +40,7 @@ Hans Wigger for providing valuable informations about A-Netz and B-Netz and
helping me to get an A-Netz phone.
Stephan Hessberger for his work on oebl.de, that provides valuable informations
about A-Netz, B-Netz and C-Netz.
about A-Netz, B-Netz, C-Netz, Eurosignal and much more.
Friedhelm Hillebrand for providing complete specification of the mobile phone
of C-Netz.

View File

@ -94,6 +94,7 @@ AC_OUTPUT(
src/r2000/Makefile
src/imts/Makefile
src/jolly/Makefile
src/eurosignal/Makefile
src/tv/Makefile
src/radio/Makefile
src/datenklo/Makefile

View File

@ -48,6 +48,7 @@ SUBDIRS += \
r2000 \
imts \
jolly \
eurosignal \
tv \
radio

View File

@ -0,0 +1,53 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
bin_PROGRAMS = \
eurosignal
eurosignal_SOURCES = \
eurosignal.c \
dsp.c \
image.c \
es_mitte.c \
es_ges.c \
es_teilges.c \
es_kaudn.c \
main.c
eurosignal_LDADD = \
$(COMMON_LA) \
../anetz/libgermanton.a \
$(top_builddir)/src/liboptions/liboptions.a \
$(top_builddir)/src/libdebug/libdebug.a \
$(top_builddir)/src/libmobile/libmobile.a \
$(top_builddir)/src/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/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 \
-lm
if HAVE_ALSA
eurosignal_LDADD += \
$(top_builddir)/src/libsound/libsound.a \
$(ALSA_LIBS)
endif
if HAVE_SDR
eurosignal_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

326
src/eurosignal/dsp.c Normal file
View File

@ -0,0 +1,326 @@
/* Eurosignal signal processing
*
* (C) 2019 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 euro->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 "eurosignal.h"
#include "dsp.h"
#define PI 3.1415927
/* signaling */
#define MAX_DEVIATION 5000.0 /* FIXME */
#define MAX_MODULATION 2000.0
#define TONE_DEVIATION 5000.0 /* FIXME */
#define TONE_INDEX 0.92
#define MAX_DISPLAY 1.4 /* something above tone level */
#define DIGIT_DURATION 0.1 /* duration of digit */
#define PAUSE_DURATION 0.22 /* duration of pause */
#define FREQUENCY_MIN 313.3
#define FREQUENCY_MAX 1153.1
#define FREQUENCY_TOL 15.0 /* tolerance of frequency */
#define DIGIT_DETECT 200 /* time for a tone to sustain (in samples) */
#define TIMEOUT_DETECT 4000 /* time for a tone to sustain (in samples) */
static struct dsp_digits {
char digit;
char *name;
double frequency;
double phaseshift65536;
} dsp_digits[] = {
{ 'I', "Idle", 1153.1, 0 },
{ 'R', "Repeat", 1062.9, 0 },
{ '0', "Digit 0", 979.8, 0 },
{ '1', "Digit 1", 903.1, 0 },
{ '2', "Digit 2", 832.5, 0 },
{ '3', "Digit 3", 767.4, 0 },
{ '4', "Digit 4", 707.4, 0 },
{ '5', "Digit 5", 652.0, 0 },
{ '6', "Digit 6", 601.0, 0 },
{ '7', "Digit 7", 554.0, 0 },
{ '8', "Digit 8", 510.7, 0 },
{ '9', "Digit 9", 470.8, 0 },
{ 'A', "Digit 10", 433.9, 0 },
{ 'B', "Digit 11", 400.0, 0 },
{ 'C', "Digit 12", 368.7, 0 },
{ 'D', "Digit 13", 339.9, 0 },
{ 'E', "Digit 14", 313.3, 0 },
{ '\0', NULL, 0.0, 0 },
};
static const char *digit_to_name(char digit)
{
int i;
for (i = 0; dsp_digits[i].digit; i++) {
if (dsp_digits[i].digit == digit)
return dsp_digits[i].name;
}
return "<none>";
}
static double digit_to_phaseshift65536(char digit)
{
int i;
for (i = 0; dsp_digits[i].digit; i++) {
if (dsp_digits[i].digit == digit)
return dsp_digits[i].phaseshift65536;
}
return 0.0;
}
static double dsp_tone[65536];
/* global init for FSK */
void dsp_init(int samplerate)
{
int i;
PDEBUG(DDSP, DEBUG_DEBUG, "Generating phase shiftings for tones.\n");
for (i = 0; dsp_digits[i].digit; i++)
dsp_digits[i].phaseshift65536 = 65536.0 / ((double)samplerate / dsp_digits[i].frequency);
PDEBUG(DDSP, DEBUG_DEBUG, "Generating sine table for tones.\n");
for (i = 0; i < 65536; i++)
dsp_tone[i] = sin((double)i / 65536.0 * 2.0 * PI);
}
/* Init transceiver instance. */
int dsp_init_sender(euro_t *euro, int samplerate, int fm)
{
int rc = 0;
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Init DSP for 'Sender'.\n");
/* set modulation parameters */
if (fm)
sender_set_fm(&euro->sender, MAX_DEVIATION, MAX_MODULATION, TONE_DEVIATION, MAX_DISPLAY);
else
sender_set_am(&euro->sender, MAX_MODULATION, 1.0, MAX_DISPLAY, TONE_INDEX);
euro->sample_duration = 1.0 / (double)samplerate;
/* initial phase shift */
euro->tx_phaseshift65536 = digit_to_phaseshift65536('I');
/* init demodulator */
rc = fm_demod_init(&euro->rx_demod, 8000, (FREQUENCY_MIN + FREQUENCY_MAX) / 2.0, FREQUENCY_MAX - FREQUENCY_MIN);
if (rc)
goto error;
/* use fourth order (2 iter) filter, since it is as fast as second order (1 iter) filter */
iir_lowpass_init(&euro->rx_lp, 25.0, 8000, 2);
euro->dmp_tone_level = display_measurements_add(&euro->sender.dispmeas, "Tone Level", "%.1f %%", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 150.0, 100.0);
//euro->dmp_tone_quality = display_measurements_add(&euro->sender.dispmeas, "Tone Quality", "%.1f %%", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 100.0, 100.0);
return 0;
error:
dsp_cleanup_sender(euro);
return -rc;
}
/* Cleanup transceiver instance. */
void dsp_cleanup_sender(euro_t *euro)
{
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Cleanup DSP for 'Sender'.\n");
/* cleanup demodulator */
fm_demod_exit(&euro->rx_demod);
}
//#define DEBUG
static void tone_decode(euro_t *euro, sample_t *samples, int length)
{
sample_t frequency[length], f;
sample_t I[length], Q[length];
int i, d;
char digit;
/* tone demodulation */
fm_demodulate_real(&euro->rx_demod, frequency, length, samples, I, Q);
/* reduce bandwidth of tone detector */
iir_process(&euro->rx_lp, frequency, length);
/* detect tone */
for (i = 0; i < length; i++) {
/* get frequency */
f = frequency[i] + (FREQUENCY_MIN + FREQUENCY_MAX) / 2.0;
#ifdef DEBUG
if (i == 0) printf("%s %.5f ", debug_amplitude(frequency[i] / (FREQUENCY_MAX - FREQUENCY_MIN) * 2.0), f);
#endif
for (d = 0; dsp_digits[d].digit; d++) {
if (f >= dsp_digits[d].frequency - FREQUENCY_TOL && f <= dsp_digits[d].frequency + FREQUENCY_TOL)
break;
}
#ifdef DEBUG
if (i == 0) printf("%c\n", dsp_digits[d].digit);
#endif
/* change detection and collect digits */
digit = dsp_digits[d].digit;
if (digit != euro->rx_digit_last) {
euro->rx_digit_last = digit;
euro->rx_digit_count = 0;
}
euro->rx_digit_count++;
switch (digit) {
case 'I':
/* pause tone */
if (euro->rx_digit_count == DIGIT_DETECT) {
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Detected Idle tone, starting.\n");
euro->rx_digit_receiving = 1;
euro->rx_digit_index = 0;
euro->rx_timeout_count = 0;
}
break;
case '\0':
/* we are not yet receiving digits */
if (!euro->rx_digit_receiving)
break;
if (euro->rx_digit_count == DIGIT_DETECT) {
/* out of range tone */
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Detected tone out of range, aborting.\n");
euro->rx_digit_receiving = 0;
}
break;
default:
/* we are not yet receiving digits */
if (!euro->rx_digit_receiving)
break;
/* got digit */
if (euro->rx_digit_count == DIGIT_DETECT) {
double level;
level = sqrt(I[i] * I[i] + Q[i] * Q[i]) * 2;
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Detected digit '%s' (level = %.0f%%)\n", digit_to_name(digit), level * 100.0);
display_measurements_update(euro->dmp_tone_level, level * 100.0, 0.0);
euro->rx_digits[euro->rx_digit_index] = digit;
euro->rx_digit_index++;
euro->rx_timeout_count = 0;
if (euro->rx_digit_index == 6 || euro->rx_digits[0] == 'R') {
euro->rx_digits[euro->rx_digit_index] = '\0';
euro_receive_id(euro, euro->rx_digits);
euro->rx_digit_receiving = 0;
}
break;
}
}
/* abort if tone sustains too long or next tone will not become steady */
if (euro->rx_digit_receiving && euro->rx_digit_index) {
euro->rx_timeout_count++;
if (euro->rx_timeout_count == TIMEOUT_DETECT) {
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Timeout receiving, aborting.\n");
euro->rx_digit_receiving = 0;
}
}
euro->rx_digit_last = digit;
}
}
/* Process received audio stream from radio unit. */
void sender_receive(sender_t *sender, sample_t *samples, int length, double __attribute__((unused)) rf_level_db)
{
euro_t *euro = (euro_t *) sender;
sample_t down[length];
int count;
/* downsample and decode */
memcpy(down, samples, sizeof(down)); // copy, so audio will not be corrupted at loopback
count = samplerate_downsample(&euro->sender.srstate, down, length);
if (euro->rx)
tone_decode(euro, down, count);
/* whenever a chunk of anouncement audio should be played toward caller */
euro->chunk_count += count;
if (euro->chunk_count >= 160) {
euro->chunk_count -= 160;
euro_clock_chunk(sender);
}
}
/* Generate tone of paging digits. */
static void tone_send(euro_t *euro, sample_t *samples, int length)
{
int i;
for (i = 0; i < length; i++) {
if (!euro->tx_digits[0]) {
if (euro->tx_time >= PAUSE_DURATION) {
euro->tx_time -= PAUSE_DURATION;
euro_get_id(euro, euro->tx_digits);
euro->tx_digit_index = 0;
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Sending digit '%s'\n", digit_to_name(euro->tx_digits[0]));
euro->tx_phaseshift65536 = digit_to_phaseshift65536(euro->tx_digits[0]);
}
} else {
if (euro->tx_time >= DIGIT_DURATION) {
euro->tx_time -= DIGIT_DURATION;
if (++euro->tx_digit_index == 6) {
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Sending Idle tone'\n");
euro->tx_digits[0] = '\0';
euro->tx_phaseshift65536 = digit_to_phaseshift65536('I');
} else {
PDEBUG_CHAN(DDSP, DEBUG_DEBUG, "Sending digit '%s'\n", digit_to_name(euro->tx_digits[euro->tx_digit_index]));
euro->tx_phaseshift65536 = digit_to_phaseshift65536(euro->tx_digits[euro->tx_digit_index]);
}
}
}
*samples++ = dsp_tone[(uint16_t)euro->tx_phase];
euro->tx_phase += euro->tx_phaseshift65536;
if (euro->tx_phase >= 65536.0)
euro->tx_phase -= 65536.0;
euro->tx_time += euro->sample_duration;
}
}
/* Provide stream of audio toward radio unit */
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
{
euro_t *euro = (euro_t *) sender;
if (euro->tx) {
memset(power, 1, length);
tone_send(euro, samples, length);
} else {
memset(power, 0, length);
memset(samples, 0, sizeof(*samples) * length);
}
}

5
src/eurosignal/dsp.h Normal file
View File

@ -0,0 +1,5 @@
void dsp_init(int samplerate);
int dsp_init_sender(euro_t *euro, int samplerate, int fm);
void dsp_cleanup_sender(euro_t *euro);

1669
src/eurosignal/es_ges.c Normal file

File diff suppressed because it is too large Load Diff

3
src/eurosignal/es_ges.h Normal file
View File

@ -0,0 +1,3 @@
void init_es_ges(void);

2202
src/eurosignal/es_kaudn.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
void init_es_kaudn(void);

1703
src/eurosignal/es_mitte.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
void init_es_mitte(void);

2261
src/eurosignal/es_teilges.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
void init_es_teilges(void);

816
src/eurosignal/eurosignal.c Normal file
View File

@ -0,0 +1,816 @@
/* Eurosignal protocol handling
*
* (C) 2019 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 euro->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 "../libmobile/call.h"
#include "../libmncc/cause.h"
#include "eurosignal.h"
#include "dsp.h"
/* anouncement timers */
#define ANSWER_TIME 1.0 /* wait after answer */
#define OOO_TIME 3.8 /* announcement 1.7 s, pause 2.1 s */
#define UNASSIGNED_TIME1 2.2 /* announcement 2.2 s s */
#define UNASSIGNED_TIME2 2.9 /* announcement 2.2 s, pause 0.7 s */
#define DEGRADED_TIME 4.95 /* announcement 2.25 s, pause 2.7 s */
#define ACKNOWLEDGE_TIME1 2.8 /* announcement 1.7 s, pause 1.1 s */
#define ACKNOWLEDGE_TIME2 4.6 /* announcement 1.7 s, pause 2.9 s */
#define BEEP_TIME 4.0 /* beep after answer */
/* Call reference for calls from mobile station to network
This offset of 0x400000000 is required for MNCC interface. */
static int new_callref = 0x40000000;
euro_call_t *ooo_call_list = NULL;
struct id_list {
struct id_list *next;
char id[7];
};
static struct id_list *id_list = NULL, **id_list_tail = &id_list;
/* add ID that is allowed to page */
void euro_add_id(const char *id)
{
struct id_list *entry;
entry = calloc(1, sizeof(struct id_list));
if (!entry) {
fprintf(stderr, "No mem!\n");
abort();
}
strcpy(entry->id, id);
(*id_list_tail) = entry;
id_list_tail = &entry->next;
}
static int search_id(const char *id)
{
struct id_list *entry = id_list;
int count = 0;
while (entry) {
count++;
if (!strcmp(entry->id, id))
return count;
entry = entry->next;
}
return 0;
}
static void euro_call_destroy(euro_call_t *call);
static void flush_id(void)
{
struct id_list *entry;
while (ooo_call_list)
euro_call_destroy(ooo_call_list);
while (id_list) {
entry = id_list;
id_list = entry->next;
free(entry);
}
id_list_tail = &id_list;
}
static const char *call_state_name(enum euro_call_state state)
{
static char invalid[16];
switch (state) {
case EURO_CALL_NULL:
return "(NULL)";
case EURO_CALL_ANSWER:
return "ANSWER";
case EURO_CALL_DEGRADED:
return "DEGRADED";
case EURO_CALL_ACKNOWLEDGE:
return "ACKNOWLEDGE";
case EURO_CALL_RELEASED:
return "RELEASED";
case EURO_CALL_UNASSIGNED:
return "UNASSIGNED";
case EURO_CALL_OUTOFORDER:
return "OUT-OF-ORDER";
case EURO_CALL_BEEPING:
return "BEEPING";
}
sprintf(invalid, "invalid(%d)", state);
return invalid;
}
static void euro_display_status(void)
{
sender_t *sender;
euro_t *euro;
euro_call_t *call;
display_status_start();
for (call = ooo_call_list; call; call = call->next)
display_status_subscriber(call->station_id, call_state_name(call->state));
for (sender = sender_head; sender; sender = sender->next) {
euro = (euro_t *) sender;
display_status_channel(euro->sender.kanal, NULL, (euro->degraded) ? "Degraded" : "Working");
for (call = euro->call_list; call; call = call->next)
display_status_subscriber(call->station_id, call_state_name(call->state));
}
display_status_end();
}
static void call_new_state(euro_call_t *call, enum euro_call_state new_state)
{
if (call->state == new_state)
return;
PDEBUG(DEURO, DEBUG_DEBUG, "State change: %s -> %s\n", call_state_name(call->state), call_state_name(new_state));
call->state = new_state;
euro_display_status();
}
/* Convert channel number to frequency */
double euro_kanal2freq(const char *kanal, int fm)
{
double freq = 87.34;
char digit = kanal[0];
if (strlen(kanal) != 1)
return 0.0;
if (digit >= 'a' && digit <= 'd')
digit -= 'a';
else if (digit >= 'A' && digit <= 'D')
digit -= 'A';
else
return 0.0;
if (fm)
freq -= 0.0075;
return (freq + 0.025 * (double)digit) * 1e6;
}
static struct channel_info {
const char *kanal;
const char *country;
const char *station;
} channel_info[] = {
{ "A", "Germany", "Eurosignal Mitte" },
{ NULL, "France", "Centre Est" },
{ "B", "Germany", "Eurosignal Nord" },
{ NULL, "Germany", "Eurosignal Sued" },
{ NULL, "France", "North" },
{ "C", "France", "West" },
{ NULL, "France", "South East" },
{ NULL, "France", "East" },
{ "D", "France", "South West" },
{ NULL, "France", "South West (Corsica)" },
{ NULL, "Switzerland", "" },
{ NULL, NULL, NULL },
};
/* list all channels */
void euro_list_channels(void)
{
int i;
printf("Channel\t\tFrequency\tCountry\t\tStation Name\n");
printf("------------------------------------------------------------------------\n");
for (i = 0; channel_info[i].country; i++) {
if (channel_info[i].kanal)
printf("%s\t\t%.3f MHz\t", channel_info[i].kanal, euro_kanal2freq(channel_info[i].kanal, 0) / 1e6);
else
printf("\t\t\t\t");
printf("%s\t", channel_info[i].country);
if (strlen(channel_info[i].country) < 8)
printf("\t");
printf("%s\n", channel_info[i].station);
}
printf("\n");
}
sample_t beep_tone[160];
/* global init */
int euro_init(void)
{
int i;
for (i = 0; i < 160; i++)
beep_tone[i] = sin(2.0 * M_PI * (double)i * 2500.0 / 8000.0);
return 0;
}
/* global exit */
void euro_exit(void)
{
flush_id();
}
static void call_timeout(struct timer *timer);
/* Create transceiver instance and link to a list. */
int euro_create(const char *kanal, const char *audiodev, int use_sdr, int samplerate, double rx_gain, int fm, int tx, int rx, int repeat, int degraded, int random, uint32_t scan_from, uint32_t scan_to, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback)
{
euro_t *euro;
int rc;
if (euro_kanal2freq(kanal, 0) == 0.0) {
PDEBUG(DEURO, DEBUG_ERROR, "Channel ('Kanal') number %s invalid, use 'list' to get a list.\n", kanal);
return -EINVAL;
}
if (atoi(kanal) == 19) {
PDEBUG(DEURO, DEBUG_ERROR, "Selected calling channel ('Rufkanal') number %s can't be used as traffic channel.\n", kanal);
return -EINVAL;
}
euro = calloc(1, sizeof(*euro));
if (!euro) {
PDEBUG(DEURO, DEBUG_ERROR, "No memory!\n");
return -ENOMEM;
}
PDEBUG(DEURO, DEBUG_DEBUG, "Creating 'Eurosignal' instance for 'Kanal' = %s (sample rate %d).\n", kanal, samplerate);
/* init general part of transceiver */
rc = sender_create(&euro->sender, kanal, euro_kanal2freq(kanal, fm), euro_kanal2freq(kanal, fm), audiodev, use_sdr, samplerate, rx_gain, 0, 0, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, PAGING_SIGNAL_NONE);
if (rc < 0) {
PDEBUG(DEURO, DEBUG_ERROR, "Failed to init transceiver process!\n");
goto error;
}
/* init audio processing */
rc = dsp_init_sender(euro, samplerate, fm);
if (rc < 0) {
PDEBUG(DEURO, DEBUG_ERROR, "Failed to init audio processing!\n");
goto error;
}
euro->tx = tx;
euro->rx = rx;
euro->repeat = repeat;
euro->degraded = degraded;
euro->random = random;
euro->scan_from = scan_from;
euro->scan_to = scan_to;
euro_display_status();
PDEBUG(DEURO, DEBUG_NOTICE, "Created 'Kanal' %s\n", kanal);
return 0;
error:
euro_destroy(&euro->sender);
return rc;
}
/* Destroy transceiver instance and unlink from list. */
void euro_destroy(sender_t *sender)
{
euro_t *euro = (euro_t *) sender;
PDEBUG(DEURO, DEBUG_DEBUG, "Destroying 'Eurosignal' instance for 'Kanal' = %s.\n", sender->kanal);
while (euro->call_list)
euro_call_destroy(euro->call_list);
dsp_cleanup_sender(euro);
sender_destroy(&euro->sender);
free(euro);
}
/* Create call instance */
static euro_call_t *euro_call_create(euro_t *euro, uint32_t callref, const char *id)
{
euro_call_t *call, **callp;
PDEBUG(DEURO, DEBUG_INFO, "Creating call instance to page ID '%s'.\n", id);
/* create */
call = calloc(1, sizeof(*call));
if (!call) {
PDEBUG(DEURO, DEBUG_ERROR, "No mem!\n");
abort();
}
/* init */
call->callref = callref;
strcpy(call->station_id, id);
if (euro)
call->page_count = euro->repeat;
timer_init(&call->timer, call_timeout, call);
timer_start(&call->timer, ANSWER_TIME);
/* link */
call->euro = euro;
callp = (euro) ? (&euro->call_list) : (&ooo_call_list);
while ((*callp))
callp = &(*callp)->next;
(*callp) = call;
return call;
}
/* Destroy call instance */
static void euro_call_destroy(euro_call_t *call)
{
euro_call_t **callp;
/* unlink */
callp = (call->euro) ? (&call->euro->call_list) : (&ooo_call_list);
while ((*callp) != call)
callp = &(*callp)->next;
(*callp) = call->next;
/* cleanup */
timer_exit(&call->timer);
/* destroy */
free(call);
/* update display */
euro_display_status();
}
/* Return ID to page. If there is nothing scheduled, return idle pattern */
void euro_get_id(euro_t *euro, char *id)
{
euro_call_t *call;
int i;
if (euro->sender.loopback) {
PDEBUG_CHAN(DEURO, DEBUG_NOTICE, "Transmitting test ID '123456'.\n");
memcpy(id, "123456", 6);
goto encode;
}
if (euro->scan_from < euro->scan_to) {
sprintf(id, "%06d", euro->scan_from++);
PDEBUG_CHAN(DEURO, DEBUG_NOTICE, "Transmitting ID '%s'.\n", id);
goto encode;
}
for (call = euro->call_list; call; call = call->next) {
if ((call->state == EURO_CALL_ACKNOWLEDGE || call->state == EURO_CALL_RELEASED) && call->page_count) {
call->page_count--;
PDEBUG_CHAN(DEURO, DEBUG_NOTICE, "Transmitting ID '%s'.\n", call->station_id);
memcpy(id, call->station_id, 6);
if (call->page_count == 0 && call->state == EURO_CALL_RELEASED)
euro_call_destroy(call);
/* reset random counter, so aferr call has been transmitted, the random mode continues with a new ID */
euro->random_count = 0;
break;
}
}
if (!call && euro->random) {
if (!euro->random_count) {
if (((uint32_t)random() % 2) == 0 && euro->random_id[0] != 'R') {
memcpy(euro->random_id, "RIIIII", 6);
euro->random_count = (uint32_t)random() % 2 + 2;
} else {
sprintf(euro->random_id, "%06d", (uint32_t)(random() % 1000000));
euro->random_count = (uint32_t)random() % 3 + 2;
}
}
memcpy(id, euro->random_id, 6);
euro->random_count--;
if (id[0] == 'R') {
PDEBUG_CHAN(DEURO, DEBUG_NOTICE, "Randomly transmitting Idle sequence.\n");
return;
}
PDEBUG_CHAN(DEURO, DEBUG_NOTICE, "Randomly transmitting ID '%s'.\n", euro->random_id);
goto encode;
}
if (!call) {
PDEBUG_CHAN(DEURO, DEBUG_DEBUG, "Transmitting Idle sequence.\n");
memcpy(id, "RIIIII", 6);
return;
}
encode:
/* return station ID with repeat digit */
for (i = 1; i < 6; i++) {
if (id[i - 1] == id[i])
id[i] = 'R';
}
}
/* A station ID was received. */
void euro_receive_id(euro_t *euro, char *id)
{
int i;
int count = 0;
if (id[0] == 'R') {
PDEBUG_CHAN(DEURO, DEBUG_DEBUG, "Received Idle sequence'\n");
return;
}
/* turn repeat digit to normal digit */
for (i = 1; i < 6; i++) {
if (id[i] == 'R')
id[i] = id[i - 1];
}
/* loopback display */
if (euro->sender.loopback) {
PDEBUG_CHAN(DEURO, DEBUG_NOTICE, "Received ID '%s'\n", id);
return;
}
/* check for ID selection */
if (id_list) {
count = search_id(id);
if (!count) {
PDEBUG_CHAN(DEURO, DEBUG_INFO, "Received ID '%s' is not for us.\n", id);
return;
}
}
PDEBUG_CHAN(DEURO, DEBUG_NOTICE, "Received ID '%s'\n", id);
/* we want to send beep via MNCC */
if (id_list) {
sender_t *sender;
euro_t *e;
euro_call_t *call;
char dialing[32];
int callref;
int rc;
/* check if we already have a call that beeps */
for (sender = sender_head; sender; sender = sender->next) {
e = (euro_t *) sender;
for (call = e->call_list; call; call = call->next) {
if (!strcmp(call->station_id, id) && call->state == EURO_CALL_BEEPING)
break;
}
if (call)
break;
}
/* if already beeping */
if (sender)
return;
/* create call and send setup */
PDEBUG_CHAN(DEURO, DEBUG_INFO, "Sending setup towards network.'\n");
callref = ++new_callref;
call = euro_call_create(euro, callref, id);
call_new_state(call, EURO_CALL_BEEPING);
sprintf(dialing, "%d", count);
rc = call_up_setup(callref, call->station_id, dialing);
if (rc < 0)
euro_call_destroy(call);
}
}
int16_t *es_mitte_spl;
int es_mitte_size;
int16_t *es_ges_spl;
int es_ges_size;
int16_t *es_teilges_spl;
int es_teilges_size;
int16_t *es_kaudn_spl;
int es_kaudn_size;
/* play announcement for one call */
static void call_play_announcement(euro_call_t *call)
{
int i;
int16_t chunk[160];
sample_t spl[160];
for (i = 0; i < 160; i++) {
/* announcement or silence, if finished or not set */
if (call->announcement_index < call->announcement_size)
chunk[i] = call->announcement_spl[call->announcement_index++] >> 2;
else
chunk[i] = 0.0;
}
int16_to_samples(spl, chunk, 160);
call_up_audio(call->callref, spl, 160);
}
/* play paging tone */
static void call_play_beep(euro_call_t *call)
{
call_up_audio(call->callref, beep_tone, 160);
}
/* loop through all calls and play the announcement */
void euro_clock_chunk(sender_t *sender)
{
euro_t *euro;
euro_call_t *call;
if (sender == sender_head) {
/* use first tranceiver clock to clock calls without a transceiver */
for (call = ooo_call_list; call; call = call->next) {
/* no callref */
if (!call->callref)
continue;
/* beep or announcement */
if (call->state == EURO_CALL_BEEPING)
call_play_beep(call);
else
call_play_announcement(call);
}
}
euro = (euro_t *) sender;
for (call = euro->call_list; call; call = call->next) {
/* no callref */
if (!call->callref)
continue;
/* beep or announcement */
if (call->state == EURO_CALL_BEEPING)
call_play_beep(call);
else
call_play_announcement(call);
}
}
/* Timeout handling */
static void call_timeout(struct timer *timer)
{
euro_call_t *call = (euro_call_t *)timer->priv;
switch (call->state) {
case EURO_CALL_ANSWER:
/* if no station is linked to the call, we are out-of-order */
if (!call->euro) {
PDEBUG(DEURO, DEBUG_INFO, "Station is unavailable, playing announcement.\n");
call->announcement_spl = es_ges_spl;
call->announcement_size = es_ges_size;
call->announcement_index = 0;
timer_start(&call->timer, OOO_TIME);
call_new_state(call, EURO_CALL_OUTOFORDER);
break;
}
/* if subcriber list is available, but ID is not found, we are unassigned */
if (id_list && !search_id(call->station_id)) {
PDEBUG(DEURO, DEBUG_INFO, "Subscriber unknwon, playing announcement.\n");
call->announcement_spl = es_kaudn_spl;
call->announcement_size = es_kaudn_size;
call->announcement_index = 0;
call->announcement_count = 1;
timer_start(&call->timer, UNASSIGNED_TIME1);
call_new_state(call, EURO_CALL_UNASSIGNED);
break;
}
/* if station is degraded, play that announcement */
if (call->euro->degraded) {
PDEBUG(DEURO, DEBUG_INFO, "Station is degraded, playing announcement.\n");
call->announcement_spl = es_teilges_spl;
call->announcement_size = es_teilges_size;
call->announcement_index = 0;
timer_start(&call->timer, DEGRADED_TIME);
call_new_state(call, EURO_CALL_DEGRADED);
break;
}
/* fall through */
case EURO_CALL_DEGRADED:
PDEBUG(DEURO, DEBUG_INFO, "Station acknowledges, playing announcement.\n");
call->announcement_spl = es_mitte_spl;
call->announcement_size = es_mitte_size;
call->announcement_index = 0;
call->announcement_count = 1;
timer_start(&call->timer, ACKNOWLEDGE_TIME1);
call_new_state(call, EURO_CALL_ACKNOWLEDGE);
break;
case EURO_CALL_ACKNOWLEDGE:
if (call->announcement_count == 1) {
call->announcement_spl = es_mitte_spl;
call->announcement_size = es_mitte_size;
call->announcement_index = 0;
call->announcement_count = 2;
timer_start(&call->timer, ACKNOWLEDGE_TIME2);
break;
}
if (call->page_count) {
PDEBUG(DEURO, DEBUG_INFO, "Announcement played, receiver has not been paged yet, releasing call.\n");
call_up_release(call->callref, CAUSE_NORMAL);
call->callref = 0;
call_new_state(call, EURO_CALL_RELEASED);
break;
}
PDEBUG(DEURO, DEBUG_INFO, "Announcement played, receiver has been paged, releasing call.\n");
call_up_release(call->callref, CAUSE_NORMAL);
euro_call_destroy(call);
break;
case EURO_CALL_OUTOFORDER:
PDEBUG(DEURO, DEBUG_INFO, "Announcement played, releasing call.\n");
call_up_release(call->callref, CAUSE_NORMAL);
euro_call_destroy(call);
break;
case EURO_CALL_UNASSIGNED:
if (call->announcement_count == 1) {
call->announcement_spl = es_kaudn_spl;
call->announcement_size = es_kaudn_size;
call->announcement_index = 0;
call->announcement_count = 2;
timer_start(&call->timer, UNASSIGNED_TIME2);
break;
}
PDEBUG(DEURO, DEBUG_INFO, "Announcement played, playing again.\n");
call->announcement_spl = es_kaudn_spl;
call->announcement_size = es_kaudn_size;
call->announcement_index = 0;
call->announcement_count = 1;
timer_start(&call->timer, UNASSIGNED_TIME1);
break;
case EURO_CALL_BEEPING:
PDEBUG(DEURO, DEBUG_INFO, "Beep played, releasing.\n");
call_up_release(call->callref, CAUSE_NORMAL);
call->callref = 0;
euro_call_destroy(call);
break;
default:
break;
}
}
/* Call control starts call towards paging network. */
int call_down_setup(int callref, const char __attribute__((unused)) *caller_id, enum number_type __attribute__((unused)) caller_type, const char *dialing)
{
char channel = '\0';
sender_t *sender;
euro_t *euro;
euro_call_t *call;
int i;
/* check prefix to choose correct channel */
if (strlen(dialing) == 10) {
if (!strncmp(dialing, "0279", 4)) {
dialing += 4;
channel = 'A';
}
if (!strncmp(dialing, "0509", 4)) {
dialing += 4;
channel = 'B';
}
if (!strncmp(dialing, "0709", 4)) {
dialing += 4;
channel = 'B';
}
}
/* number invalid */
if (strlen(dialing) != 6) {
inval:
PDEBUG(DEURO, DEBUG_NOTICE, "Call to invalid ID '%s', rejecting!\n", dialing);
return -CAUSE_INVALNUMBER;
}
for (i = 0; i < 6; i++) {
if (dialing[i] < '0' || dialing[i] > '9')
goto inval;
}
/* find transmitter */
for (sender = sender_head; sender; sender = sender->next) {
/* skip channels that are different than requested */
if (channel && sender->kanal[0] != channel)
continue;
euro = (euro_t *) sender;
/* check if base station cannot transmit */
if (!euro->tx)
continue;
break;
}
/* just (ab)use busy signal when no station is available */
if (!sender) {
if (channel)
PDEBUG(DEURO, DEBUG_NOTICE, "Cannot page receiver, because given station not available, rejecting!\n");
else
PDEBUG(DEURO, DEBUG_NOTICE, "Cannot page receiver, no station not available, rejecting!\n");
euro = NULL;
}
/* create call process to page station or send out-of-order message */
call = euro_call_create(euro, callref, dialing);
call_new_state(call, EURO_CALL_ANSWER);
call_up_answer(callref, dialing);
return 0;
}
void call_down_answer(int __attribute__((unused)) callref)
{
sender_t *sender;
euro_t *euro;
euro_call_t *call;
PDEBUG(DEURO, DEBUG_INFO, "Call has been answered by network.\n");
for (sender = sender_head; sender; sender = sender->next) {
euro = (euro_t *) sender;
for (call = euro->call_list; call; call = call->next) {
if (call->callref == callref)
break;
}
if (call)
break;
}
if (!call) {
PDEBUG(DEURO, DEBUG_NOTICE, "Outgoing disconnect, but no callref!\n");
call_up_release(callref, CAUSE_INVALCALLREF);
return;
}
timer_start(&call->timer, BEEP_TIME);
}
static void _release(int callref, int __attribute__((unused)) cause)
{
sender_t *sender;
euro_t *euro;
euro_call_t *call;
PDEBUG(DEURO, DEBUG_INFO, "Call has been disconnected by network.\n");
for (sender = sender_head; sender; sender = sender->next) {
euro = (euro_t *) sender;
for (call = euro->call_list; call; call = call->next) {
if (call->callref == callref)
break;
}
if (call)
break;
}
if (!sender) {
for (call = ooo_call_list; call; call = call->next) {
if (call->callref == callref)
break;
}
}
if (!call) {
PDEBUG(DEURO, DEBUG_NOTICE, "Outgoing disconnect, but no callref!\n");
call_up_release(callref, CAUSE_INVALCALLREF);
return;
}
call->callref = 0;
/* queued ID will keep in release state until trasmission has finished */
if (call->state == EURO_CALL_ACKNOWLEDGE && call->page_count) {
call_new_state(call, EURO_CALL_RELEASED);
return;
}
euro_call_destroy(call);
}
/* Call control sends disconnect.
* A queued ID will be kept until transmitted by mobile station.
*/
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, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count)
{
}
void dump_info(void) {}

View File

@ -0,0 +1,91 @@
#include "../libfm/fm.h"
#include "../libmobile/sender.h"
#include "../libtimer/timer.h"
/* current state of transmitter */
enum euro_health_state {
EURO_HEALTH_WORKING = 0,/* */
EURO_HEALTH_DEGRADED, /* */
EURO_HEALTH_OUTOFORDER, /* */
};
/* current state of incoming call */
enum euro_call_state {
EURO_CALL_NULL = 0,
EURO_CALL_ANSWER, /* answer the call */
EURO_CALL_DEGRADED, /* play announcement 'teilgestoert' */
EURO_CALL_ACKNOWLEDGE, /* play announcement 'eurosignal xxx', transmit ID */
EURO_CALL_RELEASED, /* caller hangs up, ID still not transmitted */
EURO_CALL_UNASSIGNED, /* play announcement 'kein anschluss' */
EURO_CALL_OUTOFORDER, /* play announcement 'gestoert' */
EURO_CALL_BEEPING, /* call towards MNCC that beeps */
};
struct eurosignal;
/* instance of incoming call */
typedef struct euro_call {
struct euro_call *next;
struct eurosignal *euro;
int callref; /* call reference */
char station_id[7]; /* current station ID */
int page_count; /* number of transmissions left */
struct timer timer;
enum euro_call_state state; /* current state */
int announcement_count; /* used to replay annoucements */
int16_t *announcement_spl; /* current sample */
int announcement_size; /* current size */
int announcement_index; /* current sample index */
} euro_call_t;
/* instance of eurosignal transmitter/receiver */
typedef struct eurosignal {
sender_t sender;
/* system info */
int tx; /* can transmit */
int rx; /* can receive */
int repeat; /* repetitions of ID transmission */
int degraded; /* station is degraded */
int random; /* random ID transmission */
char random_id[7]; /* current ID */
char random_count; /* number of transmissions left */
uint32_t scan_from; /* scan ID from */
uint32_t scan_to; /* scan ID to (scan if not 0) */
/* calls */
euro_call_t *call_list; /* linked list of all calls */
/* display measurements */
dispmeasparam_t *dmp_tone_level;
dispmeasparam_t *dmp_tone_quality;
/* dsp states */
double sample_duration; /* how many seconds lasts a sample */
double tx_phaseshift65536; /* current tone's phase shift per sample */
double tx_phase; /* current phase of tone */
double tx_time; /* current elapsed time of tone */
char tx_digits[7]; /* current ID beeing transmitted */
int tx_digit_index; /* current digit beein transmitted */
int chunk_count; /* current elapsed sample of 20ms audio chunk */
fm_demod_t rx_demod; /* demodulator for frequency */
iir_filter_t rx_lp; /* low pass to filter the frequency result */
int rx_digit_count; /* count the tone until detected */
char rx_digit_last; /* last tone, so we detect any change */
int rx_digit_receiving; /* we recive digis */
char rx_digits[7]; /* current ID being received */
int rx_digit_index; /* current digit receiving */
int rx_timeout_count; /* count the timeout */
} euro_t;
void euro_add_id(const char *id);
double euro_kanal2freq(const char *kanal, int fm);
void euro_list_channels(void);
int euro_init(void);
void euro_exit(void);
int euro_create(const char *kanal, const char *audiodev, int use_sdr, int samplerate, double rx_gain, int fm, int tx, int rx, int repeat, int degraded, int random, uint32_t scan_from, uint32_t scan_to, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, int loopback);
void euro_destroy(sender_t *sender);
void euro_get_id(euro_t *euro, char *id);
void euro_receive_id(euro_t *euro, char *id);
void euro_clock_chunk(sender_t *sender);

99
src/eurosignal/image.c Normal file
View File

@ -0,0 +1,99 @@
#include <stdio.h>
#include <string.h>
#include "../libmobile/image.h"
const char *image[] = {
"@W",
" @B()",
" @B\\",
" @Y____________ @B\\",
" @Y/ @Y\\ @B\\",
" @Y/ @BEuro- @Y\\ @B\\",
" @Y| @BSignal @Y/ @B\\",
" @Y| /\\___________/ @B\\",
" @Y|/ @B\\",
" @w.--------------------------------------------------------.",
" @w| @y__@w .----------------------------------------. @y__@w |" ,
" @w| @y/ \\@w | @W88 92 94 96 100 104 108 MHz@w | @y/ \\@w |",
" @w| @y\\__/@w | @r|@Y|@r||||||||||||||||||||||||||||||||||||@w | @y\\__/@w |",
" @w| '----------------------------------------' |",
" @w|--------------------------------------------------------|",
" @w| @y:::::::::::::::::::::::::::::::::::::::::::::::::::::: @w|",
" @w| / \\ |",
" @w| @y:::::::::::::::::::::::::::::::::::::::::::::::::::::: @w|",
" @w| | | |",
" @w| @y:::::::::::::::::::::::::::::::::::::::::::::::::::::: @w|",
" @w| (UKW-Radio) \\ / |",
" @w| @y:::::::::::::::::::::::::::::::::::::::::::::::::::::: @w|",
" @w| |",
" @w'--------------------------------------------------------'",
"",
NULL
};
void print_image(void)
{
int i, j;
for (i = 0; image[i]; i++) {
for (j = 0; j < (int)strlen(image[i]); j++) {
if (image[i][j] == '@') {
j++;
switch(image[i][j]) {
case 'k': /* black */
printf("\033[0;30m");
break;
case 'r': /* red */
printf("\033[0;31m");
break;
case 'g': /* green */
printf("\033[0;32m");
break;
case 'y': /* yellow */
printf("\033[0;33m");
break;
case 'b': /* blue */
printf("\033[0;34m");
break;
case 'm': /* magenta */
printf("\033[0;35m");
break;
case 'c': /* cyan */
printf("\033[0;36m");
break;
case 'w': /* white */
printf("\033[0;37m");
break;
case 'K': /* bright black */
printf("\033[1;30m");
break;
case 'R': /* bright red */
printf("\033[1;31m");
break;
case 'G': /* bright green */
printf("\033[1;32m");
break;
case 'Y': /* bright yellow */
printf("\033[1;33m");
break;
case 'B': /* bright blue */
printf("\033[1;34m");
break;
case 'M': /* bright magenta */
printf("\033[1;35m");
break;
case 'C': /* bright cyan */
printf("\033[1;36m");
break;
case 'W': /* bright white */
printf("\033[1;37m");
break;
}
} else
printf("%c", image[i][j]);
}
printf("\n");
}
printf("\033[0;39m");
}

251
src/eurosignal/main.c Normal file
View File

@ -0,0 +1,251 @@
/* Eurosignal main
*
* (C) 2019 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../libmobile/call.h"
#include "../libmobile/main_mobile.h"
#include "../liboptions/options.h"
#include "eurosignal.h"
#include "dsp.h"
#include "../anetz/besetztton.h"
#include "es_mitte.h"
#include "es_ges.h"
#include "es_teilges.h"
#include "es_kaudn.h"
static int fm = 0; /* use fm */
static int tx = 0; /* we transmit */
static int rx = 0; /* we receive */
static int repeat = 4; /* repeat given ID */
static int degraded = 0; /* if station is degraded */
static int random_id = 0; /* transmit pseudo random pattern */
static uint32_t scan_from = 0;
static uint32_t scan_to = 0;
void print_help(const char *arg0)
{
main_mobile_print_help(arg0, "[--random] ");
/* - - */
printf(" -F --fm\n");
printf(" Use frequency modulation instead of amplitude modulation. The carrier\n");
printf(" frequency will be 7.5 KHz above/below the actual frequency. This\n");
printf(" ensures that the change in frequency results as a change in amplitude\n");
printf(" at the demodulator of the receiver.\n");
printf(" -T --tx\n");
printf(" Transmit Eurosignal on given channel, to page a receiver. (default)\n");
printf(" -R --rx\n");
printf(" Receive Eurosignal 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(" -I --id <id> [-I ...]\n");
printf(" Give one or more IDs to allow only the given IDs to be transmitted and\n");
printf(" received. This option can be repeated many times. If this option is not\n");
printf(" specified, any ID is allowed to transmit.\n");
printf(" Also, any ID given will cause a beep when the ID was received. This\n");
printf(" requires call device (sound card) to be defined or MNCC interface.\n");
printf(" -D --degraded\n");
printf(" Play the anouncement that the system is degraded due to failure of one\n");
printf(" or more transmitters. If the caller hangs up during or rigt after the\n");
printf(" announcement, no paging is performed.\n");
printf(" -S --scan <from> <to>\n");
printf(" Scan through given IDs once (no repetition). This can be useful to find\n");
printf(" the ID of a vintage receiver. Note that scanning all IDs from 000000\n");
printf(" through 999999 would take almost 10 Days.\n");
printf(" --random\n");
printf(" Repeat some pseudo random IDs, to make the transmitted signal sound\n");
printf(" authentic and as it would handle many calls. It will still be possible\n");
printf(" to page a recevier.\n");
printf(" --repeat <num>\n");
printf(" Repead paging ID <num> times when transmitting. (default = %d)\n", repeat);
printf("\nstation-id: Give 6 digit station-id, you don't need to enter it for every\n");
printf(" start of this program.\n");
main_mobile_print_hotkeys();
}
#define OPT_RANDOM 255
#define OPT_REPEAT 258
static void add_options(void)
{
main_mobile_add_options();
option_add('F', "fm", 0);
option_add('T', "tx", 0);
option_add('R', "rx", 0);
option_add('I', "id", 1);
option_add('D', "degraded", 0);
option_add('S', "scan", 2);
option_add(OPT_RANDOM, "random", 0);
option_add(OPT_REPEAT, "repeat", 1);
}
static int check_id(const char *id)
{
int i;
if (strlen(id) != 6) {
fprintf(stderr, "Given paging ID must have exactly 6 digits!\n");
return -EINVAL;
}
for (i = 0; i < 6; i++) {
if (id[i] < '0' || id[i] > '9') {
fprintf(stderr, "Given paging ID must have digits (0..9) only!\n");
return -EINVAL;
}
}
return 0;
}
static int handle_options(int short_option, int argi, char **argv)
{
switch (short_option) {
case 'F':
fm = 1;
break;
case 'T':
tx = 1;
break;
case 'R':
rx = 1;
break;
case 'I':
if (check_id(argv[argi]))
return -EINVAL;
euro_add_id(argv[argi]);
break;
case 'D':
degraded = 1;
break;
case 'S':
if (check_id(argv[argi]))
return -EINVAL;
scan_from = atoi(argv[argi++]);
if (check_id(argv[argi]))
return -EINVAL;
scan_to = atoi(argv[argi++]) + 1;
break;
case OPT_RANDOM:
random_id = 1;
break;
case OPT_REPEAT:
repeat = atoi(argv[argi]);
break;
default:
return main_mobile_handle_options(short_option, argi, argv);
}
return 1;
}
int main(int argc, char *argv[])
{
int rc, argi;
const char *station_id = "";
int i;
/* init common tones */
init_besetzton();
/* init system specific tones */
init_es_mitte();
init_es_ges();
init_es_teilges();
init_es_kaudn();
main_mobile_init();
/* handle options / config file */
add_options();
rc = options_config_file("~/.osmocom/analog/eurosignal.conf", handle_options);
if (rc < 0)
return 0;
argi = options_command_line(argc, argv, handle_options);
if (argi <= 0)
return argi;
if (argi < argc) {
station_id = argv[argi];
if (strlen(station_id) != 6) {
printf("Given receiver ID '%s' does not have 6 digits\n", station_id);
return 0;
}
}
if (!num_kanal) {
printf("No channel (\"Kanal\") is specified, Use '-k list' to get a list of all channels.\n\n");
print_help(argv[0]);
return 0;
}
if (!strcasecmp(kanal[0], "list")) {
euro_list_channels();
goto fail;
}
if (use_sdr) {
/* set audiodev */
for (i = 0; i < num_kanal; i++)
audiodev[i] = "sdr";
num_audiodev = num_kanal;
}
if (num_kanal == 1 && num_audiodev == 0)
num_audiodev = 1; /* use default */
if (num_kanal != num_audiodev) {
fprintf(stderr, "You need to specify as many sound devices as you have channels.\n");
exit(0);
}
/* inits */
fm_init(fast_math);
dsp_init(samplerate);
euro_init();
/* TX is default */
if (!tx && !rx)
tx = 1;
/* create transceiver instance */
for (i = 0; i < num_kanal; i++) {
rc = euro_create(kanal[i], audiodev[i], use_sdr, samplerate, rx_gain, fm, tx, rx, repeat, degraded, random_id, scan_from, scan_to, 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 for channel %s ready, please tune transmitter and/or receiver to %.4f MHz\n", kanal[i], euro_kanal2freq(kanal[i], fm) / 1e6);
}
main_mobile(&quit, latency, interval, NULL, station_id, 6);
fail:
/* destroy transceiver instance */
while(sender_head)
euro_destroy(sender_head);
/* exits */
fm_exit();
euro_exit();
return 0;
}

View File

@ -53,6 +53,7 @@ struct debug_cat {
{ "r2000", "\033[1;34m" },
{ "imts", "\033[1;34m" },
{ "jollycom", "\033[1;34m" },
{ "eurosignal", "\033[1;34m" },
{ "frame", "\033[0;36m" },
{ "call", "\033[0;37m" },
{ "mncc", "\033[1;32m" },

View File

@ -16,22 +16,23 @@
#define DR2000 9
#define DIMTS 10
#define DJOLLY 11
#define DFRAME 12
#define DCALL 13
#define DMNCC 14
#define DDB 15
#define DTRANS 16
#define DDMS 17
#define DSMS 18
#define DSDR 19
#define DUHD 20
#define DSOAPY 21
#define DWAVE 22
#define DRADIO 23
#define DAM791X 24
#define DUART 25
#define DDEVICE 26
#define DDATENKLO 27
#define DEURO 12
#define DFRAME 13
#define DCALL 14
#define DMNCC 15
#define DDB 16
#define DTRANS 17
#define DDMS 18
#define DSMS 19
#define DSDR 20
#define DUHD 21
#define DSOAPY 22
#define DWAVE 23
#define DRADIO 24
#define DAM791X 25
#define DUART 26
#define DDEVICE 27
#define DDATENKLO 28
void get_win_size(int *w, int *h);