2021-11-14 14:53:23 +00:00
|
|
|
/* 5-Ton-Folge call processing
|
|
|
|
*
|
|
|
|
* (C) 2021 by Andreas Eversberg <jolly@eversberg.eu>
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define CHAN fuenf->sender.kanal
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include "../libsample/sample.h"
|
2024-01-05 13:20:36 +00:00
|
|
|
#include "../liblogging/logging.h"
|
2021-11-14 14:53:23 +00:00
|
|
|
#include "../libmobile/call.h"
|
|
|
|
#include "../libmobile/cause.h"
|
2024-01-05 13:20:36 +00:00
|
|
|
#include <osmocom/cc/message.h>
|
2021-11-14 14:53:23 +00:00
|
|
|
#include "../liboptions/options.h"
|
|
|
|
#include "fuenf.h"
|
|
|
|
#include "dsp.h"
|
|
|
|
|
|
|
|
void bos_list_channels(void)
|
|
|
|
{
|
|
|
|
printf("Channels\tBand\n");
|
|
|
|
printf("------------------------\n");
|
|
|
|
printf("101 - 125\t2 Meter\n");
|
|
|
|
printf(" 1 - 92\t2 Meter\n");
|
|
|
|
printf("347 - 509\t4 Meter\n");
|
|
|
|
printf("-> Give channel number or any frequency in MHz (using a dot, e.g. '169.810').\n");
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert channel to frequency */
|
|
|
|
double bos_kanal2freq(const char *kanal)
|
|
|
|
{
|
|
|
|
int k;
|
|
|
|
|
|
|
|
if (strchr(kanal, '.'))
|
|
|
|
return atof(kanal) * 1e6;
|
|
|
|
|
|
|
|
k = atoi(kanal);
|
|
|
|
|
|
|
|
if (k >= 101 && k <= 125)
|
|
|
|
return 169.810e6 + 20e3 * (k - 101);
|
|
|
|
|
|
|
|
if (k >= 1 && k <= 92)
|
|
|
|
return 172.160e6 + 20e3 * (k - 1);
|
|
|
|
|
|
|
|
if (k >= 347 && k <= 509)
|
|
|
|
return 84.015e6 + 20e3 * (k - 347);
|
|
|
|
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert frequency to channel, if possible */
|
|
|
|
const char *bos_freq2kanal(const char *freq)
|
|
|
|
{
|
|
|
|
double f;
|
|
|
|
char kanal[8];
|
|
|
|
|
|
|
|
if (!strchr(freq, '.'))
|
|
|
|
return options_strdup(freq);
|
|
|
|
|
|
|
|
f = atof(freq) * 1e6;
|
|
|
|
|
|
|
|
if (f >= 169.810e6 && f <= 170.290e6 && fmod(f - 169.810e6, 20e3) == 0.0) {
|
|
|
|
sprintf(kanal, "%.0f", (f - 169.810e6) / 20e3 + 101);
|
|
|
|
return options_strdup(kanal);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (f >= 172.160e6 && f <= 173.980e6 && fmod(f - 172.160e6, 20e3) == 0.0) {
|
|
|
|
sprintf(kanal, "%.0f", (f - 172.160e6) / 20e3 + 1);
|
|
|
|
return options_strdup(kanal);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (f >= 84.015e6 && f <= 87.255e6 && fmod(f - 84.015e6, 20e3) == 0.0) {
|
|
|
|
sprintf(kanal, "%.0f", (f - 84.015e6) / 20e3 + 347);
|
|
|
|
return options_strdup(kanal);
|
|
|
|
}
|
|
|
|
|
|
|
|
return options_strdup(freq);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *fuenf_state_name[] = {
|
|
|
|
"IDLE",
|
|
|
|
"RUF",
|
|
|
|
"DURCHSAGE",
|
|
|
|
};
|
|
|
|
|
|
|
|
const char *fuenf_funktion_name[8] = {
|
|
|
|
"Ruf",
|
|
|
|
"Feueralarm",
|
|
|
|
"Probealarm",
|
|
|
|
"Warnung der Befoelkerung",
|
|
|
|
"ABC-Alarm",
|
|
|
|
"Entwarnung",
|
|
|
|
"Katastrophenalarm",
|
|
|
|
"Turbo-Scanner",
|
|
|
|
};
|
|
|
|
|
|
|
|
/* check if number is a valid pager ID */
|
|
|
|
const char *bos_number_valid(const char *number)
|
|
|
|
{
|
|
|
|
/* assume that the number has valid length(s) and digits */
|
|
|
|
|
|
|
|
if (number[5] && (number[5] < '0' || number[5] > '6'))
|
|
|
|
return "Illegal 'Sirenenalarm' digit #6 (Use 1..6 only)";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fuenf_init(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void fuenf_exit(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fuenf_display_status(void)
|
|
|
|
{
|
|
|
|
sender_t *sender;
|
|
|
|
fuenf_t *fuenf;
|
|
|
|
|
|
|
|
display_status_start();
|
|
|
|
for (sender = sender_head; sender; sender = sender->next) {
|
|
|
|
fuenf = (fuenf_t *) sender;
|
|
|
|
display_status_channel(fuenf->sender.kanal, NULL, fuenf_state_name[fuenf->state]);
|
|
|
|
}
|
|
|
|
display_status_end();
|
|
|
|
}
|
|
|
|
|
|
|
|
void fuenf_new_state(fuenf_t *fuenf, enum fuenf_state new_state)
|
|
|
|
{
|
|
|
|
if (fuenf->state == new_state)
|
|
|
|
return;
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP_CHAN(DFUENF, LOGL_DEBUG, "State change: %s -> %s\n", fuenf_state_name[fuenf->state], fuenf_state_name[new_state]);
|
2021-11-14 14:53:23 +00:00
|
|
|
fuenf->state = new_state;
|
|
|
|
fuenf_display_status();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fuenf_scan_or_loopback(fuenf_t *fuenf)
|
|
|
|
{
|
|
|
|
char rufzeichen[16];
|
|
|
|
|
|
|
|
if (fuenf->scan_from < fuenf->scan_to) {
|
|
|
|
sprintf(rufzeichen, "%05d", fuenf->scan_from++);
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP_CHAN(DFUENF, LOGL_NOTICE, "Transmitting ID '%s'.\n", rufzeichen);
|
2021-11-14 14:53:23 +00:00
|
|
|
dsp_setup(fuenf, rufzeichen, fuenf->default_funktion);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fuenf->sender.loopback) {
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP(DFUENF, LOGL_INFO, "Sending 5-Ton-Ruf for loopback test.\n");
|
2021-11-14 14:53:23 +00:00
|
|
|
dsp_setup(fuenf, "10357", FUENF_FUNKTION_FEUER);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create transceiver instance and link to a list. */
|
|
|
|
int fuenf_create(const char *kanal, double frequency, const char *device, int use_sdr, int samplerate, double rx_gain, double tx_gain, int tx, int rx, double max_deviation, double signal_deviation, enum fuenf_funktion funktion, 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)
|
|
|
|
{
|
|
|
|
fuenf_t *fuenf;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
fuenf = calloc(1, sizeof(*fuenf));
|
|
|
|
if (!fuenf) {
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP(DFUENF, LOGL_ERROR, "No memory!\n");
|
2021-11-14 14:53:23 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP(DFUENF, LOGL_DEBUG, "Creating '5-Ton-Folge' instance for 'Kanal' = %s (sample rate %d).\n", kanal, samplerate);
|
2021-11-14 14:53:23 +00:00
|
|
|
|
|
|
|
/* init general part of transceiver */
|
|
|
|
rc = sender_create(&fuenf->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) {
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP(DFUENF, LOGL_ERROR, "Failed to init transceiver process!\n");
|
2021-11-14 14:53:23 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* init audio processing */
|
|
|
|
rc = dsp_init_sender(fuenf, samplerate, max_deviation, signal_deviation);
|
|
|
|
if (rc < 0) {
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP(DFUENF, LOGL_ERROR, "Failed to init audio processing!\n");
|
2021-11-14 14:53:23 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
fuenf->tx = tx;
|
|
|
|
fuenf->rx = rx;
|
|
|
|
fuenf->default_funktion = funktion;
|
|
|
|
fuenf->scan_from = scan_from;
|
|
|
|
fuenf->scan_to = scan_to;
|
|
|
|
|
|
|
|
fuenf_display_status();
|
|
|
|
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP(DFUENF, LOGL_NOTICE, "Created 'Kanal' %s\n", kanal);
|
2021-11-14 14:53:23 +00:00
|
|
|
|
|
|
|
/* start scanning, if enabled, otherwise send loopback sequence, if enabled */
|
|
|
|
fuenf_scan_or_loopback(fuenf);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
fuenf_destroy(&fuenf->sender);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Destroy transceiver instance and unlink from list. */
|
|
|
|
void fuenf_destroy(sender_t *sender)
|
|
|
|
{
|
|
|
|
fuenf_t *fuenf = (fuenf_t *) sender;
|
|
|
|
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP(DFUENF, LOGL_DEBUG, "Destroying '5-Ton-Folge' instance for 'Kanal' = %s.\n", sender->kanal);
|
2021-11-14 14:53:23 +00:00
|
|
|
|
|
|
|
dsp_cleanup_sender(fuenf);
|
|
|
|
sender_destroy(&fuenf->sender);
|
|
|
|
free(fuenf);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* call sign was transmitted */
|
|
|
|
void fuenf_tx_done(fuenf_t *fuenf)
|
|
|
|
{
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP_CHAN(DFUENF, LOGL_INFO, "Done sending 5-Ton-Ruf.\n");
|
2021-11-14 14:53:23 +00:00
|
|
|
|
|
|
|
/* start scanning, if enabled, otherwise send loopback sequence, if enabled */
|
|
|
|
if (fuenf_scan_or_loopback(fuenf)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* go talker state */
|
|
|
|
if (fuenf->callref && fuenf->tx_funktion == FUENF_FUNKTION_RUF) {
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP_CHAN(DFUENF, LOGL_INFO, "Caller may talk now.\n");
|
2021-11-14 14:53:23 +00:00
|
|
|
fuenf_new_state(fuenf, FUENF_STATE_DURCHSAGE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* go idle */
|
|
|
|
fuenf_new_state(fuenf, FUENF_STATE_IDLE);
|
|
|
|
if (fuenf->callref) {
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP_CHAN(DFUENF, LOGL_INFO, "Releasing call toward network.\n");
|
2021-11-14 14:53:23 +00:00
|
|
|
call_up_release(fuenf->callref, CAUSE_NORMAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void fuenf_rx_callsign(fuenf_t *fuenf, const char *callsign)
|
|
|
|
{
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP_CHAN(DFUENF, LOGL_INFO, "Received 5-Ton-Ruf with call sign '%s'.\n", callsign);
|
2021-11-14 14:53:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void fuenf_rx_function(fuenf_t *fuenf, enum fuenf_funktion funktion)
|
|
|
|
{
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP_CHAN(DFUENF, LOGL_INFO, "Received function '%s'.\n", fuenf_funktion_name[funktion]);
|
2021-11-14 14:53:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Call control starts call towards transmitter. */
|
|
|
|
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;
|
|
|
|
fuenf_t *fuenf;
|
|
|
|
char rufzeichen[6];
|
|
|
|
enum fuenf_funktion funktion;
|
|
|
|
|
|
|
|
/* find transmitter */
|
|
|
|
for (sender = sender_head; sender; sender = sender->next) {
|
|
|
|
/* skip channels that are different than requested */
|
|
|
|
if (channel && sender->kanal[0] != channel)
|
|
|
|
continue;
|
|
|
|
fuenf = (fuenf_t *) sender;
|
|
|
|
if (fuenf->state != FUENF_STATE_IDLE)
|
|
|
|
continue;
|
|
|
|
/* check if base station cannot transmit */
|
|
|
|
if (!fuenf->tx)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!sender) {
|
|
|
|
if (channel)
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP(DFUENF, LOGL_NOTICE, "Cannot page, because given station not available, rejecting!\n");
|
2021-11-14 14:53:23 +00:00
|
|
|
else
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP(DFUENF, LOGL_NOTICE, "Cannot page, no trasmitting station idle, rejecting!\n");
|
2021-11-14 14:53:23 +00:00
|
|
|
return -CAUSE_NOCHANNEL;
|
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(rufzeichen, dialing, 5);
|
|
|
|
rufzeichen[5] = '\0';
|
|
|
|
switch (dialing[5]) {
|
|
|
|
case '0':
|
|
|
|
funktion = FUENF_FUNKTION_RUF;
|
|
|
|
break;
|
|
|
|
case '1':
|
|
|
|
funktion = FUENF_FUNKTION_FEUER;
|
|
|
|
break;
|
|
|
|
case '2':
|
|
|
|
funktion = FUENF_FUNKTION_PROBE;
|
|
|
|
break;
|
|
|
|
case '3':
|
|
|
|
funktion = FUENF_FUNKTION_WARNUNG;
|
|
|
|
break;
|
|
|
|
case '4':
|
|
|
|
funktion = FUENF_FUNKTION_ABC;
|
|
|
|
break;
|
|
|
|
case '5':
|
|
|
|
funktion = FUENF_FUNKTION_ENTWARNUNG;
|
|
|
|
break;
|
|
|
|
case '6':
|
|
|
|
funktion = FUENF_FUNKTION_KATASTROPHE;
|
|
|
|
break;
|
|
|
|
case '\0':
|
|
|
|
funktion = fuenf->default_funktion;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -CAUSE_INVALNUMBER;
|
|
|
|
}
|
|
|
|
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP_CHAN(DFUENF, LOGL_INFO, "Sending 5-Ton-Ruf with call sign '%s' and function '%s'.\n", rufzeichen, fuenf_funktion_name[funktion]);
|
2021-11-14 14:53:23 +00:00
|
|
|
|
|
|
|
dsp_setup(fuenf, rufzeichen, funktion);
|
|
|
|
|
|
|
|
fuenf_new_state(fuenf, FUENF_STATE_RUF);
|
|
|
|
fuenf->callref = callref;
|
|
|
|
/* must answer to hear paging tones. */
|
|
|
|
call_up_answer(fuenf->callref, "");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-01-27 21:53:24 +00:00
|
|
|
void call_down_answer(int __attribute__((unused)) callref, struct timeval __attribute__((unused)) *tv_meter)
|
2021-11-14 14:53:23 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void _release(int __attribute__((unused)) callref, int __attribute__((unused)) cause)
|
|
|
|
{
|
|
|
|
sender_t *sender;
|
|
|
|
fuenf_t *fuenf;
|
|
|
|
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP(DFUENF, LOGL_INFO, "Call has been disconnected by network.\n");
|
2021-11-14 14:53:23 +00:00
|
|
|
|
|
|
|
for (sender = sender_head; sender; sender = sender->next) {
|
|
|
|
fuenf = (fuenf_t *) sender;
|
|
|
|
if (fuenf->callref == callref)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!sender) {
|
2024-01-05 13:20:36 +00:00
|
|
|
LOGP(DBNETZ, LOGL_NOTICE, "Outgoing release, but no callref!\n");
|
2021-11-14 14:53:23 +00:00
|
|
|
/* don't send release, because caller already released */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove call. go idle, if talking */
|
|
|
|
fuenf->callref = 0;
|
|
|
|
if (fuenf->state == FUENF_STATE_DURCHSAGE)
|
|
|
|
fuenf_new_state(fuenf, FUENF_STATE_IDLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dump_info(void) {}
|
|
|
|
|