Andreas Eversberg e2a9c2c6a5 Automatic FSK polarity selection on TX side
This is done by simulating two cells with different polarities.
On first message from mobile station, the cell with the correct
polarity is used, the other is disabled.

On RX side the polarity is not selected, since FSK decoder detects
polarity for every frame.
2016-05-13 09:37:01 +02:00

1601 lines
45 KiB
C

/* C-Netz telegramm transcoding
*
* (C) 2016 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 <math.h>
#include "../common/debug.h"
#include "../common/timer.h"
#include "cnetz.h"
#include "dsp.h"
#include "sysinfo.h"
#include "telegramm.h"
/* debug coding */
//#define DEBUG_RAW /* debug raw bits */
//#define DEBUG_BLOCK /* debug interleaved blocks */
//#define DEBUG_CODER /* debug block coder / decoder */
static const char *param_ja[] = {
"nein",
"ja",
};
static const char *param_betriebsart[] = {
"Sprache klar",
"Sprache invertiert",
};
static const char *param_gespraechsende[] = {
"kein bevorstehendes Gespraechsende",
"bevorstehendes Gespraechsende",
};
static const char *param_frequenz[1024];
static const char *param_anpassen[] = {
"Sendeleistung erhoehen",
"Sendeleistung erniedrigen",
};
static const char *param_opcode[64];
static const char *param_power[] = {
"50-125 mW",
"0.5-1 W",
"4-8 W",
"10-20 W",
};
static const char *param_sonderruf[] = {
"Verbindungsaufbau gehend",
"Sonderruf",
};
static const char *param_wskennung[] = {
"keine Warteschlange",
"Warteschlange",
"Warteschlange blockiert",
"Illegaler Parameter 3",
};
static const char *param_ausloesen[] = {
"26 dB S/N + Pegel",
"25 dB S/N + Pegel",
"24 dB S/N + Pegel",
"23 dB S/N + Pegel",
"22 dB S/N + Pegel",
"21 dB S/N + Pegel",
"20 dB S/N + Pegel",
"26 dB S/N",
"25 dB S/N",
"24 dB S/N",
"23 dB S/N",
"22 dB S/N",
"21 dB S/N",
"20 dB S/N",
"19 dB S/N",
"18 dB S/N",
};
static const char *param_ausloesegrund[] = {
"gassenbesetzt (oder Sperre)",
"teilnehmerbesetzt",
"funktechnisch",
"Illegaler Parameter 3",
};
static const char *param_genauigkeit[] = {
"volle Genauigkeit",
"bedingte Genauigkeit",
};
static const char *param_grenzwert[] = {
"Illegaler Parameter 0",
"> 15 dB S/N",
"> 17 dB S/N",
"> 19 dB S/N",
"> 21 dB S/N",
"> 25 dB S/N",
"> 28 dB S/N",
"> 32 dB S/N",
};
static const char *param_kennung[] = {
"Test-FuFSt",
"Normal-FuFSt",
"Vorzugs-FuFSt",
"FuFSt hoechster Prioritaet",
};
static const char *param_mittelung[] = {
"2",
"4",
"8",
"16",
"32",
"64",
"Illegaler Parameter 6",
"Illegaler Parameter 7",
"Illegaler Parameter 8",
"Illegaler Parameter 9",
"Illegaler Parameter 10",
"Illegaler Parameter 11",
"Illegaler Parameter 12",
"Illegaler Parameter 13",
"Illegaler Parameter 14",
"Illegaler Parameter 15",
};
static const char *param_entfernung[] = {
"1,5 km",
"2 km",
"2,5 km",
"3 km",
"4 km",
"5 km",
"6 km",
"7 km",
"8 km",
"10 km",
"12 km",
"14 km",
"16 km",
"17 km",
"23 km",
"30 km",
};
static const char *param_sperren[] = {
"Ein- & Umbuchen / Gehende Verbindung",
"Nur Ein- & Umbuchen",
"Nur Gehende Verbindung",
"gesperrt",
};
static const char *param_bewertung[] = {
"Auswahl nach relativer Entfernungsbewertung",
"Auswahl nach Pegelkreterium",
};
static const char *param_gueltig[] = {
"gueltig",
"ungueltig",
};
static const char *param_verkehrsanteil[] = {
"Sonderfall",
"1 Zeitschlitz",
"2 Zeitschlitze",
"3 Zeitschlitze",
"4 Zeitschlitze",
"5 Zeitschlitze",
"6 Zeitschlitze",
"7 Zeitschlitze",
"8 Zeitschlitze",
"9 Zeitschlitze",
"10 Zeitschlitze",
"11 Zeitschlitze",
"12 Zeitschlitze",
"13 Zeitschlitze",
"14 Zeitschlitze",
"15 Zeitschlitze",
"16 Zeitschlitze",
"17 Zeitschlitze",
"18 Zeitschlitze",
"19 Zeitschlitze",
"10 Zeitschlitze",
"21 Zeitschlitze",
"22 Zeitschlitze",
"23 Zeitschlitze",
"24 Zeitschlitze",
"25 Zeitschlitze",
"26 Zeitschlitze",
"27 Zeitschlitze",
"28 Zeitschlitze",
"29 Zeitschlitze",
"30 Zeitschlitze",
"31 Zeitschlitze",
};
static const char *param_signalisierung[] = {
"Spontansignalisierung",
"Signalisierung aus Wiederholstellung",
};
static const char *param_chipkarte[] = {
"Magnetkarte",
"Chipkarte",
};
static const char *param_auth[] = {
"Authentifikation nicht durchfuehrbar",
"Authentifikation durchfuehrbar",
};
static const char *param_reduzierung[] = {
"4",
"3",
"2",
"1",
};
static struct definition_parameter {
char digit;
const char *param_name;
char bits;
const char **value_names; /* points to a list of parameter names, NULL for integer */
} definition_parameter[] = {
{ 'A',"FuZ-FuVSt-Nr.", 5, NULL },
{ 'B',"Betriebs-Art", 2, param_betriebsart },
{ 'C',"Ankuendigung Gespraechsende", 1, param_gespraechsende },
{ 'D',"Teilnehmergruppensperre", 4, NULL },
{ 'E',"Anzahl der gesperrten Teilnehmergruppen", 4, NULL },
{ 'F',"FuZ-Rest-Nr.", 8, NULL },
{ 'G',"Gebuehren-Stand", 12, NULL },
{ 'H',"OgK-Vorschlag", 10, param_frequenz },
{ 'I',"FuZ-Nationalitaet", 3, NULL },
{ 'J',"Sendeleistungsanpassung", 1, param_anpassen },
{ 'K',"Frequenz-Nr.", 10, param_frequenz },
{ 'L',"Art der Signalisierung im OgK", 1, param_signalisierung },
{ 'M',"OgK-Verkehrsanteil", 5, param_verkehrsanteil },
{ 'N',"FuTln-Nationalitaet", 3, NULL },
{ 'O',"OP-Code der Signalisierung", 6, param_opcode },
{ 'P',"Max. Sendeleistung", 2, param_power },
{ 'Q',"Kartenkennung", 3, NULL },
{ 'R',"Durchfuehrung der Ueberlastbehandlung", 1, param_ja },
{ 'S',"Sonderruf", 1, param_sonderruf },
{ 'T',"FuTln-Rest-Nr.", 16, NULL },
{ 'U',"FuTln-Heimmat FuVSt-Nr.", 5, NULL },
{ 'V',"Sicherungs-Code", 16, NULL },
{ 'W',"WS-Kennung", 2, param_wskennung },
{ 'X',"Wahlziffer beliebig 16 Ziffer", 64, NULL },
{ 'Z',"Zeitschlitz-Nr.", 5, NULL },
{ 'a',"Grenzert fuer Ausloesen", 4, param_ausloesen },
{ 'b',"Chipkarten-FuTelG-Bit", 1, param_chipkarte },
{ 'c',"Ausloesegrund", 2, param_ausloesegrund },
{ 'd',"Bedingte Genauigkeit der FuFSt", 1, param_genauigkeit },
{ 'e',"Entfernung", 8, param_entfernung },
{ 'f',"Grenzwert fuer Einbuchen und Umbuchen", 3, param_grenzwert },
{ 'g',"Nachbarschafts-Prioritaets-Bit", 1, NULL },
{ 'h',"Herstellerkennung", 5, NULL },
{ 'i',"Hardwarestand des FuTelG", 5, NULL },
{ 'j',"Softwarestand des FuTelG", 5, NULL },
{ 'k',"Kennung FuFSt", 2, param_kennung },
{ 'l',"Authentifikationsbit", 1, param_auth },
{ 'm',"Mittelungs-Faktor fuer Ausloesen", 4, param_mittelung },
{ 'n',"Mittelungs-Faktor fuer Umschalten", 4, param_mittelung },
{ 'o',"Zufallszahl" , 64, NULL },
{ 'p',"Bewertung nach Pegel und Entfernung", 1, param_bewertung },
{ 'q',"Autorisierungsparameter", 64, NULL },
{ 'r',"Entfernungsangabe der FuFSt", 4, param_entfernung },
{ 's',"Gueltigkeit des Gebuehrenstandes", 1, param_gueltig },
{ 't',"Test-Telefonteilnehmer-Geraet", 1, param_ja },
{ 'u',"Grenzwert fuer Umschalten", 4, param_ausloesen },
{ 'v',"Vermittlungtechnische Sperren", 2, param_sperren },
{ 'y',"Reduzierungsfaktor", 2, param_reduzierung },
{ '_',"Illegaler Opcode", 64,NULL },
{ 0 ,"", 0,NULL },
};
static struct definition_parameter *get_parameter(char digit)
{
struct definition_parameter *parameter = definition_parameter;
for (parameter = definition_parameter; parameter->digit; parameter++) {
if (parameter->digit == digit)
return parameter;
}
return NULL;
}
static struct definition_opcode {
const char *no_auth_bits, *auth_bits;
const char *message_name;
int block;
const char *message_text;
} definition_opcode[64] = {
/* 8888888877777777666666665555555544444444333333332222222211111111 message block text */
{ "-bRL----VVVVVVVVVVVVVVVVIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT",
"-bRL-QQQ-hhhhhiiiiijjjjjIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", "EM(R)", BLOCK_R,"Erstmeldung" },
{ "-bRL----VVVVVVVVVVVVVVVVIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT",
"-bRL-QQQ-hhhhhiiiiijjjjjIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", "UM(R)", BLOCK_R,"Umbuchantrag" },
{ "SbRL----VVVVVVVVVVVVVVVVIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT",
"SbRL-QQQ-hhhhhiiiiijjjjjIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", "UWG(R)", BLOCK_R,"Umbuchantrag bei Warteschlange (gehende Verbindung)" },
{ "--RL-----hhhhhiiiiijjjjjIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "ATO(R)", BLOCK_R,"Ausloesen des FuTelG im OgK-Betrieb bei WS" },
{ "--RL--WW-hhhhhiiiiijjjjjIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "VWG(R)", BLOCK_R,"Verbindungswunsch gehend" },
{ "--RL-----hhhhhiiiiijjjjjIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "SRG(R)", BLOCK_R,"Sonderruf (Notruf)" },
{ "SbRL----VVVVVVVVVVVVVVVVIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT",
"SbRL-QQQ-hhhhhiiiiijjjjjIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", "NUG(R)", BLOCK_R,"Verbindungswunsch gehend bei Nachbarschaftsunterstuetzung" },
{ "-bRL----VVVVVVVVVVVVVVVVIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT",
"-bRL-QQQ-hhhhhiiiiijjjjjIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", "UWK(R)", BLOCK_R,"Umbuchantrag bei Warteschlange (kommende Verbindung)" },
{ "------------------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "MFT(M)", BLOCK_M,"Meldung: Funktelefonteilnehmer" },
{ "________________________________________________________________", NULL, "opcode 9", BLOCK_I,"Illegaler Opcode" },
{ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", NULL, "WUE(M)", BLOCK_M,"Wahluebertragung" },
{ "________________________________________________________________", NULL, "opcode 11",BLOCK_I,"Illegaler Opcode" },
{ "________________________________________________________________", NULL, "opcode 12",BLOCK_I,"Illegaler Opcode" },
{ "________________________________________________________________", NULL, "opcode 13",BLOCK_I,"Illegaler Opcode" },
{ "________________________________________________________________", NULL, "opcode 14",BLOCK_I,"Illegaler Opcode" },
{ "________________________________________________________________", NULL, "opcode 15",BLOCK_I,"Illegaler Opcode" },
{ "------dJ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "BEL(K)", BLOCK_K,"Belegung" },
{ "------dJ--------eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "VH(K)", BLOCK_K,"Verbindung halten" },
{ "------dJ--------eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "RTAQ(K)", BLOCK_K,"Quittung Rufton anschalten" },
{ "------dJBB------eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "AH(K)", BLOCK_K,"Abhebe-Signal" },
{ "------dJBBCt----eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "VH(V)", BLOCK_V,"Verbindung halten" },
{ "------dJ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "AT(K)", BLOCK_K,"Ausloesen durch Funktelefonteilnehmer" },
{ "------dJBBC-------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "AT(V)", BLOCK_V,"Ausloesen durch Funktelefonteilnehmer" },
{ "------dJBB------eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "DSQ(K)", BLOCK_K,"Durchschalten Quittung" },
{ "------dJBBCt----eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "USAI(V)", BLOCK_V,"Umschaltantrag intern" },
{ "------dJBBCt----eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "USAE(V)", BLOCK_V,"Umschaltantrag extern" },
{ "------dJBB--------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "USTLN(K)", BLOCK_K,"Umschalten Funktelefonteilnehmer" },
{ "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", NULL, "ZFZQ(K)", BLOCK_K,"Zufallszahlquittung" },
{ "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq", NULL, "AP(K)", BLOCK_K,"Autorisierungsparameter" },
{ "________________________________________________________________", NULL, "opcode 29",BLOCK_I,"Illegaler Opcode" },
{ "________________________________________________________________", NULL, "opcode 30",BLOCK_I,"Illegaler Opcode" },
{ "________________________________________________________________", NULL, "opcode 31",BLOCK_I,"Illegaler Opcode" },
{ "PP-MMMMMDDDDEEEE------HHHHHHHHHHFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "MA(M)", BLOCK_M,"Meldeaufruf" },
{ "PPdZZZZZ------KKKKKKKKKKIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "VAK(R)", BLOCK_R,"Verbindungsaufbau kommend" },
{ "________________________________________________________________", NULL, "opcode 34",BLOCK_I,"Illegaler Opcode" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "EBQ(R)", BLOCK_R,"Einbuchquittung" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "UBQ(R)", BLOCK_R,"Umbuchquittung" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "WSK(R)", BLOCK_R,"Wartescglange kommend" },
{ "PP-MMMMMDDDDEEEE------HHHHHHHHHHFFFFFFFF------------------------", NULL, "MLR(M)", BLOCK_M,"Melde-Leer-Ruf" },
{ "PPdZZZZZffflvvWW------yyIIIAAAAAFFFFFFFFkkgprrrrmmmmnnnnuuuuaaaa", NULL, "LR(R)", BLOCK_R,"Leer-Ruf" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "ATQ(R)", BLOCK_R,"Quittung fuer Ausloesen des FuTelG im OgK-Betrieb" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "SAR(R)", BLOCK_R,"Sperraufruf" },
{ "PP-MMMMMDDDDEEEE------HHHHHHHHHHFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "WAF(M)", BLOCK_M,"Wahlaufforderung" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "WBP(R)", BLOCK_R,"Wahlbestaetigung positiv" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "WBN(R)", BLOCK_R,"Wahlbestaetigung negativ" },
{ "PPdZZZZZ----------------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "WWBP(R)", BLOCK_R,"Wahlbestaetigung positiv in Warteschlange" },
{ "PPdZZZZZ------KKKKKKKKKKIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "VAG(R)", BLOCK_R,"Verbindungsaufbau gehend" },
{ "PPdZZZZZ------cc--------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "VA(R)", BLOCK_R,"Vorzeitiges Ausloesen" },
{ "PP----dJ--------eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "BQ(K)", BLOCK_K,"Belegungsquittung" },
{ "PP----dJ--------eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "VHQ(K)", BLOCK_K,"Quittung Verbindung halten" },
{ "PP----dJ--------eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "RTA(K)", BLOCK_K,"Rufton anschalten" },
{ "PP----dJ--------eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "AHQ(K)", BLOCK_K,"Abhebe-Quittierung" },
{ "PP----dJ--C-----eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "VHQ1(V)", BLOCK_V,"Verbindung halten Quittung 1" },
{ "PP----dJ--CsGGGGGGGGGGGGIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "VHQ2(V)", BLOCK_V,"Verbindung halten Quittung 2" },
{ "PP----dJ------cc--------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "AF(K)", BLOCK_K,"Ausloesen durch FuFSt in konzentr. Signalisierung" },
{ "PP----dJ------cc--------IIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "AF(V)", BLOCK_V,"Ausloesen durch FuFSt in verteilter Signalisierung" },
{ "PP----dJ--------eeeeeeeeIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "DSB(K)", BLOCK_K,"Durchschaltung" },
{ "PP----dJ------KKKKKKKKKKIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "DSBI(V)", BLOCK_V,"Umschaltbefehl intern (neuer SpK in der gleichen FuZ)" },
{ "PP----dJ------KKKKKKKKKKIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "USF(K)", BLOCK_K,"Umschalten FuFst" },
{ "PP----dJ------KKKKKKKKKKIIIAAAAAFFFFFFFFNNNUUUUUTTTTTTTTTTTTTTTT", NULL, "USBE(V)", BLOCK_V,"Umschaltbefehl extern (neuer SpK in einer anderen Funkzelle)" },
{ "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", NULL, "ZFZ(K)", BLOCK_K,"Zufallszahl" },
{ "________________________________________________________________", NULL, "opcode 61",BLOCK_I,"Illegaler Opcode" },
{ "________________________________________________________________", NULL, "opcode 62",BLOCK_I,"Illegaler Opcode" },
{ "________________________________________________________________", NULL, "opcode 63",BLOCK_I,"Illegaler Opcode" },
};
const char *telegramm_name(uint8_t opcode)
{
return definition_opcode[opcode].message_name;
}
int init_telegramm(void)
{
struct definition_parameter *parameter;
int i, j;
const char *bits;
char last_bit;
int count_bits;
/* copy no_auth_bits to auth_bits, if required
* check if the number of bits in a message matches the number of bits of a parameter */
for (i = 0; i < 64; i++) {
if (definition_opcode[i].auth_bits == NULL)
definition_opcode[i].auth_bits = definition_opcode[i].no_auth_bits;
for (bits = definition_opcode[i].no_auth_bits; ; bits = definition_opcode[i].auth_bits) {
last_bit = '-';
count_bits = 0;
for (j = 0; j < 65; j++) { /* include termination character */
if (last_bit != bits[j]) {
if (last_bit != '-') {
parameter = get_parameter(last_bit);
if (!parameter) {
printf("Message #%d has invalid digit '%c'\n", i, last_bit);
return -1;
}
if (parameter->bits != count_bits) {
printf("Message #%d has digit '%c' with %d bits, but parameter has %d bits\n", i, last_bit, count_bits, parameter->bits);
return -1;
}
}
last_bit = bits[j];
count_bits = 0;
}
count_bits++;
}
if (bits == definition_opcode[i].auth_bits)
break;
}
}
/* generate frequency names */
for (i = 0; i < 1024; i++) {
char *frequenz = calloc(16, 1);
if ((i & 1))
sprintf(frequenz, "%.4f MHz", 465.750 - (double)(i+1) / 2.0 * 0.010);
else
sprintf(frequenz, "%.4f MHz", 465.750 - (double)i / 2.0 * 0.0125);
param_frequenz[i] = frequenz;
}
/* generate opcode names */
for (i = 0; i < 64; i++)
param_opcode[i] = definition_opcode[i].message_name;
return 0;
}
const char *telegramm2rufnummer(telegramm_t *telegramm)
{
static char rufnummer[9];
sprintf(rufnummer, "%d%d%05d", telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr);
return rufnummer;
}
const char *transaction2rufnummer(transaction_t *trans)
{
static char rufnummer[9];
sprintf(rufnummer, "%d%d%05d", trans->futln_nat, trans->futln_fuvst, trans->futln_rest);
return rufnummer;
}
static void decode_dialstring(char *number, uint64_t value)
{
int index, max, shift;
int digit;
if ((value & 0x000000000000000f) == 0) {
max = 16;
index = 1;
number[0] = '0';
} else {
max = value & 0x000000000000000f;
index = 0;
}
shift = 4;
while (index < max) {
digit = (value >> shift) & 0xf;
switch (digit) {
case 0xb:
digit = '*';
break;
case 0xc:
digit = '#';
break;
case 0xa:
case 0xd:
case 0xe:
case 0xf:
digit = digit - 0xa + 'a';
break;
default:
digit = digit + '0';
}
number[index] = digit;
index++;
shift += 4;
}
number[index] = '\0';
}
static int encode_dialstring(uint64_t *value, const char *number)
{
int max, index, shift, digit;
max = strlen(number);
if (max > 16) {
PDEBUG(DFRAME, DEBUG_NOTICE, "Given number '%s' has more than 16 digits\n", number);
return -EINVAL;
}
if (max == 16) {
if (number[0] != '0') {
PDEBUG(DFRAME, DEBUG_NOTICE, "Given 16 digit number '%s' does not start with '0'\n", number);
return -EINVAL;
}
*value = 0;
index = 1;
} else {
*value = strlen(number);
index = 0;
}
shift = 4;
while (index < max) {
digit = number[index];
switch (digit) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
digit = digit - '0';
break;
case 'a':
case 'd':
case 'e':
case 'f':
digit = digit - 'a' + 0xa;
break;
case 'A':
case 'D':
case 'E':
case 'F':
digit = digit - 'A' + 0xa;
break;
case '*':
digit = 0xb;
break;
case '#':
digit = 0xc;
break;
default:
return -EINVAL;
}
*value |= (uint64_t)digit << shift;
index++;
shift += 4;
}
return 0;
}
int match_fuz(cnetz_t *cnetz, telegramm_t *telegramm, int cell)
{
if (telegramm->fuz_nationalitaet != si[cell].fuz_nat
|| telegramm->fuz_fuvst_nr != si[cell].fuz_fuvst
|| telegramm->fuz_rest_nr != si[cell].fuz_rest) {
if (!cnetz->cell_auto)
PDEBUG(DFRAME, DEBUG_NOTICE, "Ignoring message from mobile phone %d,%d,%d: Cell 'Funkzelle' does not match!\n", telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr);
return 0;
}
return 1;
}
int match_futln(telegramm_t *telegramm, uint8_t futln_nat, uint8_t futln_fuvst, uint16_t futln_rest)
{
if (telegramm->futln_nationalitaet != futln_nat
|| telegramm->futln_heimat_fuvst_nr != futln_fuvst
|| telegramm->futln_rest_nr != futln_rest) {
PDEBUG(DFRAME, DEBUG_NOTICE, "Ignoring message from mobile phone %d,%d,%d: Mobile station 'Funktelefongeraet' does not match!\n", telegramm->futln_nationalitaet, telegramm->futln_heimat_fuvst_nr, telegramm->futln_rest_nr);
return 0;
}
return 1;
}
static void debug_parameter(char digit, uint64_t value)
{
struct definition_parameter *parameter;
parameter = get_parameter(digit);
if (!parameter) {
PDEBUG(DFRAME, DEBUG_ERROR, "Digit '%c' not found in definition_parameter list, please fix!\n", digit);
abort();
}
if (parameter->value_names)
PDEBUG(DFRAME, DEBUG_DEBUG, " (%c) %s : %s\n", digit, parameter->param_name, parameter->value_names[value]);
else if (parameter->bits == 64)
PDEBUG(DFRAME, DEBUG_DEBUG, " (%c) %s : 0x%016x\n", digit, parameter->param_name, value);
else if (digit == 'X') {
char wahlziffern[17];
decode_dialstring(wahlziffern, value);
PDEBUG(DFRAME, DEBUG_DEBUG, " (%c) %s : '%s'\n", digit, parameter->param_name, wahlziffern);
} else
PDEBUG(DFRAME, DEBUG_DEBUG, " (%c) %s : %d\n", digit, parameter->param_name, value);
}
/* encode telegram to 70 bits
* bit order MSB
*/
static char *assemble_telegramm(const telegramm_t *telegramm, int debug)
{
static char bits[71]; /* + termination char for debug */
char parameter;
const char *string;
uint64_t value, val;
int i, j;
int rc;
if (telegramm->opcode >= 64) {
PDEBUG(DFRAME, DEBUG_ERROR, "Opcode '0x%x' exceeds bit range, please fix!\n", telegramm->opcode);
abort();
}
PDEBUG(DFRAME, DEBUG_DEBUG, "Coding %s %s\n", definition_opcode[telegramm->opcode].message_name, definition_opcode[telegramm->opcode].message_text);
/* copy opcode */
for (i = 0; i < 6; i++)
bits[5 - i] = (telegramm->opcode & (1 << i)) ? '1' : '0';
/* copy parameters */
string = definition_opcode[telegramm->opcode].no_auth_bits;
for (i = 0; i < 64; i++) {
parameter = string[63 - i];
if (parameter == '-') {
bits[69 - i] = '0';
continue;
}
switch (parameter) {
case 'A':
value = telegramm->fuz_fuvst_nr;
break;
case 'B':
value = telegramm->betriebs_art;
break;
case 'C':
value = telegramm->ankuendigung_gespraechsende;
break;
case 'D':
value = telegramm->teilnehmersperre;
break;
case 'E':
value = telegramm->anzahl_gesperrter_teilnehmergruppen;
break;
case 'F':
value = telegramm->fuz_rest_nr;
break;
case 'G':
value = telegramm->gebuehren_stand;
break;
case 'H':
value = telegramm->ogk_vorschlag;
break;
case 'I':
value = telegramm->fuz_nationalitaet;
break;
case 'J':
value = telegramm->sendeleistungsanpassung;
break;
case 'K':
value = telegramm->frequenz_nr;
break;
case 'L':
value = telegramm->art_der_signalisierung_im_ogk;
break;
case 'M':
value = telegramm->ogk_verkehrsanteil;
break;
case 'N':
value = telegramm->futln_nationalitaet;
break;
case 'P':
value = telegramm->max_sendeleistung;
break;
case 'Q':
value = telegramm->kartenkennung;
break;
case 'R':
value = telegramm->durchfuehrung_der_ueberlastbehandlung;
break;
case 'S':
value = telegramm->sonderruf;
break;
case 'T':
value = telegramm->futln_rest_nr;
break;
case 'U':
value = telegramm->futln_heimat_fuvst_nr;
break;
case 'V':
value = telegramm->sicherungs_code;
break;
case 'W':
value = telegramm->ws_kennung;
break;
case 'X':
rc = encode_dialstring(&value, telegramm->wahlziffern);
if (rc < 0) {
PDEBUG(DFRAME, DEBUG_ERROR, "Illegal dial string '%s', please fix!\n", telegramm->wahlziffern);
abort();
}
break;
case 'Z':
value = telegramm->zeitschlitz_nr;
break;
case 'a':
value = telegramm->grenze_fuer_ausloesen;
break;
case 'b':
value = telegramm->chipkarten_futelg_bit;
break;
case 'c':
value = telegramm->ausloesegrund;
break;
case 'd':
value = telegramm->bedingte_genauigkeit_der_fufst;
break;
case 'e':
value = telegramm->entfernung;
break;
case 'f':
value = telegramm->grenzwert_fuer_einbuchen_und_umbuchen;
break;
case 'g':
value = telegramm->nachbarschafts_prioritaets_bit;
break;
case 'h':
value = telegramm->herstellerkennung;
break;
case 'i':
value = telegramm->hardware_des_futelg;
break;
case 'j':
value = telegramm->software_des_futelg;
break;
case 'k':
value = telegramm->kennung_fufst;
break;
case 'l':
value = telegramm->authentifikationsbit;
break;
case 'm':
value = telegramm->mittelungsfaktor_fuer_ausloesen;
break;
case 'n':
value = telegramm->mittelungsfaktor_fuer_umschalten;
break;
case 'o':
value = telegramm->zufallszahl;
break;
case 'p':
value = telegramm->bewertung_nach_pegel_und_entfernung;
break;
case 'q':
value = telegramm->authorisierungsparameter;
break;
case 'r':
value = telegramm->entfernungsangabe_der_fufst;
break;
case 's':
value = telegramm->gueltigkeit_des_gebuehrenstandes;
break;
case 't':
value = telegramm->test_telefonteilnehmer_geraet;
break;
case 'u':
value = telegramm->grenzwert_fuer_umschalten;
break;
case 'v':
value = telegramm->vermittlungstechnische_sperren;
break;
case 'y':
value = telegramm->reduzierungsfaktor;
break;
case '_':
value = telegramm->illegaler_opcode;
break;
default:
PDEBUG(DFRAME, DEBUG_ERROR, "Parameter '%c' does not exist, please fix!\n", parameter);
abort();
}
if (debug && debuglevel <= DEBUG_DEBUG)
debug_parameter(parameter, value);
val = value;
for (j = 0; string[63 - i - j] == parameter; j++) {
bits[69 - i - j] = (val & 1) ? '1' : '0';
val >>= 1;
}
if (val)
PDEBUG(DFRAME, DEBUG_ERROR, "Parameter '%c' value '0x%x' exceeds bit range!\n", parameter, value);
i += j - 1;
}
bits[70] = '\0';
if (debug) {
PDEBUG(DFRAME, DEBUG_DEBUG, "OOOOOO%s\n", string);
PDEBUG(DFRAME, DEBUG_DEBUG, "%s\n", bits);
}
return bits;
}
/* decode telegram from 70 bits
* bit order MSB
*/
static void disassemble_telegramm(telegramm_t *telegramm, const char *bits, int auth)
{
uint64_t value;
const char *string;
char parameter;
int i, j;
memset(telegramm, 0, sizeof(*telegramm));
/* copy opcode */
value = 0;
for (i = 0; i < 6; i++)
value = (value << 1) | (bits[i] == '1');
telegramm->opcode = value;
PDEBUG(DFRAME, DEBUG_DEBUG, "Decoding %s %s\n", definition_opcode[telegramm->opcode].message_name, definition_opcode[telegramm->opcode].message_text);
/* copy parameters */
if (auth && bits[1]) /* auth flag and chip card flag */
string = definition_opcode[telegramm->opcode].auth_bits;
else
string = definition_opcode[telegramm->opcode].no_auth_bits;
for (i = 0; i < 64; i++) {
parameter = string[63 - i];
if (parameter == '-')
continue;
value = 0;
for (j = 0; i + j < 64 && string[63 - i - j] == parameter; j++)
value = (value >> 1) | ((uint64_t)(bits[69 - i - j] == '1') << 63);
value >>= 64 - j;
i += j - 1;
if (debuglevel <= DEBUG_DEBUG)
debug_parameter(parameter, value);
switch (parameter) {
case 'A':
telegramm->fuz_fuvst_nr = value;
break;
case 'B':
telegramm->betriebs_art = value;
break;
case 'C':
telegramm->ankuendigung_gespraechsende = value;
break;
case 'D':
telegramm->teilnehmersperre = value;
break;
case 'E':
telegramm->anzahl_gesperrter_teilnehmergruppen = value;
break;
case 'F':
telegramm->fuz_rest_nr = value;
break;
case 'G':
telegramm->gebuehren_stand = value;
break;
case 'H':
telegramm->ogk_vorschlag = value;
break;
case 'I':
telegramm->fuz_nationalitaet = value;
break;
case 'J':
telegramm->sendeleistungsanpassung = value;
break;
case 'K':
telegramm->frequenz_nr = value;
break;
case 'L':
telegramm->art_der_signalisierung_im_ogk = value;
break;
case 'M':
telegramm->ogk_verkehrsanteil = value;
break;
case 'N':
telegramm->futln_nationalitaet = value;
break;
case 'P':
telegramm->max_sendeleistung = value;
break;
case 'Q':
telegramm->kartenkennung = value;
break;
case 'R':
telegramm->durchfuehrung_der_ueberlastbehandlung = value;
break;
case 'S':
telegramm->sonderruf = value;
break;
case 'T':
telegramm->futln_rest_nr = value;
break;
case 'U':
telegramm->futln_heimat_fuvst_nr = value;
break;
case 'V':
telegramm->sicherungs_code = value;
break;
case 'W':
telegramm->ws_kennung = value;
break;
case 'X':
decode_dialstring(telegramm->wahlziffern, value);
break;
case 'Z':
telegramm->zeitschlitz_nr = value;
break;
case 'a':
telegramm->grenze_fuer_ausloesen = value;
break;
case 'b':
telegramm->chipkarten_futelg_bit = value;
break;
case 'c':
telegramm->ausloesegrund = value;
break;
case 'd':
telegramm->bedingte_genauigkeit_der_fufst = value;
break;
case 'e':
telegramm->entfernung = value;
break;
case 'f':
telegramm->grenzwert_fuer_einbuchen_und_umbuchen = value;
break;
case 'g':
telegramm->nachbarschafts_prioritaets_bit = value;
break;
case 'h':
telegramm->herstellerkennung = value;
break;
case 'i':
telegramm->hardware_des_futelg = value;
break;
case 'j':
telegramm->software_des_futelg = value;
break;
case 'k':
telegramm->kennung_fufst = value;
break;
case 'l':
telegramm->authentifikationsbit = value;
break;
case 'm':
telegramm->mittelungsfaktor_fuer_ausloesen = value;
break;
case 'n':
telegramm->mittelungsfaktor_fuer_umschalten = value;
break;
case 'o':
telegramm->zufallszahl = value;
break;
case 'p':
telegramm->bewertung_nach_pegel_und_entfernung = value;
break;
case 'q':
telegramm->authorisierungsparameter = value;
break;
case 'r':
telegramm->entfernungsangabe_der_fufst = value;
break;
case 's':
telegramm->gueltigkeit_des_gebuehrenstandes = value;
break;
case 't':
telegramm->test_telefonteilnehmer_geraet = value;
break;
case 'u':
telegramm->grenzwert_fuer_umschalten = value;
break;
case 'v':
telegramm->vermittlungstechnische_sperren = value;
break;
case 'y':
telegramm->reduzierungsfaktor = value;
break;
case '_':
telegramm->illegaler_opcode = value;
break;
default:
PDEBUG(DFRAME, DEBUG_ERROR, "Parameter '%c' does not exist, please fix!\n", parameter);
abort();
}
}
if (debuglevel <= DEBUG_DEBUG) {
char debug_bits[71];
memcpy(debug_bits, bits, 70);
debug_bits[70] = '\0';
PDEBUG(DFRAME, DEBUG_DEBUG, "OOOOOO%s\n", string);
PDEBUG(DFRAME, DEBUG_DEBUG, "%s\n", debug_bits);
}
}
static const char *barker_string = "11100010010";
static int16_t barker_code = 0x712; /* 11 bits: 11100010010 */
static uint8_t barker_decode[2048]; /* detected bits */
static char *blockcode[128] = {
/* 0123456 = Nutzbits */
/* 01234567 = Redundanzbits */
"0000000" "00000000",
"1000000" "11101000",
"0100000" "01110100",
"1100000" "10011100",
"0010000" "00111010",
"1010000" "11010010",
"0110000" "01001110",
"1110000" "10100110",
"0001000" "00011101",
"1001000" "11110101",
"0101000" "01101001",
"1101000" "10000001",
"0011000" "00100111",
"1011000" "11001111",
"0111000" "01010011",
"1111000" "10111011",
"0000100" "11100110",
"1000100" "00001110",
"0100100" "10010010",
"1100100" "01111010",
"0010100" "11011100",
"1010100" "00110100",
"0110100" "10101000",
"1110100" "01000000",
"0001100" "11111011",
"1001100" "00010011",
"0101100" "10001111",
"1101100" "01100111",
"0011100" "11000001",
"1011100" "00101001",
"0111100" "10110101",
"1111100" "01011101",
"0000010" "01110011",
"1000010" "10011011",
"0100010" "00000111",
"1100010" "11101111",
"0010010" "01001001",
"1010010" "10100001",
"0110010" "00111101",
"1110010" "11010101",
"0001010" "01101110",
"1001010" "10000110",
"0101010" "00011010",
"1101010" "11110010",
"0011010" "01010100",
"1011010" "10111100",
"0111010" "00100000",
"1111010" "11001000",
"0000110" "10010101",
"1000110" "01111101",
"0100110" "11100001",
"1100110" "00001001",
"0010110" "10101111",
"1010110" "01000111",
"0110110" "11011011",
"1110110" "00110011",
"0001110" "10001000",
"1001110" "01100000",
"0101110" "11111100",
"1101110" "00010100",
"0011110" "10110010",
"1011110" "01011010",
"0111110" "11000110",
"1111110" "00101110",
"0000001" "11010001",
"1000001" "00111001",
"0100001" "10100101",
"1100001" "01001101",
"0010001" "11101011",
"1010001" "00000011",
"0110001" "10011111",
"1110001" "01110111",
"0001001" "11001100",
"1001001" "00100100",
"0101001" "10111000",
"1101001" "01010000",
"0011001" "11110110",
"1011001" "00011110",
"0111001" "10000010",
"1111001" "01101010",
"0000101" "00110111",
"1000101" "11011111",
"0100101" "01000011",
"1100101" "10101011",
"0010101" "00001101",
"1010101" "11100101",
"0110101" "01111001",
"1110101" "10010001",
"0001101" "00101010",
"1001101" "11000010",
"0101101" "01011110",
"1101101" "10110110",
"0011101" "00010000",
"1011101" "11111000",
"0111101" "01100100",
"1111101" "10001100",
"0000011" "10100010",
"1000011" "01001010",
"0100011" "11010110",
"1100011" "00111110",
"0010011" "10011000",
"1010011" "01110000",
"0110011" "11101100",
"1110011" "00000100",
"0001011" "10111111",
"1001011" "01010111",
"0101011" "11001011",
"1101011" "00100011",
"0011011" "10000101",
"1011011" "01101101",
"0111011" "11110001",
"1111011" "00011001",
"0000111" "01000100",
"1000111" "10101100",
"0100111" "00110000",
"1100111" "11011000",
"0010111" "01111110",
"1010111" "10010110",
"0110111" "00001010",
"1110111" "11100010",
"0001111" "01011001",
"1001111" "10110001",
"0101111" "00101101",
"1101111" "11000101",
"0011111" "01100011",
"1011111" "10001011",
"0111111" "00010111",
"1111111" "11111111",
};
static uint16_t block_code[128];
static uint16_t block_decode[32768]; /* code word + flag / 0xffff=decode error */
int init_coding(void)
{
int i, j, k;
/* create table to decode barker code.
* ech table entry returns the number of detected bits */
for (i = 0; i < 2048; i++) {
int match = 0;
for (j = 0; j < 11; j++) {
/* check if i matches barker code at given bit j */
if (((i ^ barker_code) & (0x400 >> j)) == 0)
match++;
}
barker_decode[i] = match;
}
/* convert string to block code words */
for (i = 0; i < 128; i++) {
int word = 0;
for (j = 0; j < 15; j++)
word = (word << 1) + (blockcode[i][14 - j] - '0');
if ((word & 0x7f) != i) {
printf("Databits are wrong, expecting %d, but got %d\n", i, word & 0x7f);
return -1;
}
block_code[i] = word;
}
/* check if redunancy of a single bit matches the combined redundancy */
for (i = 0; i < 128; i++) {
int r = 0;
for (j = 0; j < 7; j++) {
if ((i & (1 << j)))
r ^= block_code[1 << j] >> 7;
}
if (r != block_code[i] >> 7) {
printf("Redundancy bits are wrong\n");
return -1;
}
}
/* create table to decode one block code and return value + error */
/* set all combinations invalid */
for (i = 0; i < 32768; i++)
block_decode[i] = 0xffff;
for (i = 0; i < 128; i++) {
int word;
/* set all error free combinations valid */
word = block_code[i];
if (block_decode[word] != 0xffff) {
printf("Overlap, please fix!\n");
return -1;
}
block_decode[word] = i;
/* set all one bit error combinations valid with flag */
for (j = 0; j < 15; j++) {
word = block_code[i];
word ^= (1 << j);
if (block_decode[word] != 0xffff) {
printf("Overlap, please fix!\n");
return -1;
}
block_decode[word] = i | 0x100; /* indicate 1 error */
/* set all two bit error combinations valid with flag */
for (k = j + 1; k < 15; k++) {
word = block_code[i];
word ^= (1 << j) | (1 << k);
if (block_decode[word] != 0xffff) {
printf("Overlap, please fix!\n");
return -1;
}
block_decode[word] = i | 0x200; /* indicate 2 errors */
}
}
}
#if 0
int count = 0;
for (i = 0; i < 32768; i++) {
printf("%d,", (int16_t)block_decode[i]);
if (block_decode[i] == 0xffff)
count++;
}
printf("bad blocks = %d\n", count);
#endif
return 0;
}
/* check for sync (3 * barker code) + 1 bit */
int detect_sync(uint64_t bitstream)
{
int match;
/* hack: ignore first 3 bits of first barker code */
bitstream |= 0x380000000;
/* metch 33 bits, not as specified by FTZ */
match = barker_decode[(bitstream >> 23) & 0x7ff];
if (match < 11)
return 0;
match += barker_decode[(bitstream >> 12) & 0x7ff];
if (match < 22)
return 0;
match += barker_decode[(bitstream >> 1) & 0x7ff];
if (match < 33)
return 0;
return 1;
}
/* encode data block
* input: 70 data bits MSB first
* output: 10*15 code words (LSB first)
* FTZ 171 TR 60 / 5.1.1.3 */
static char *encode(const char *input)
{
static char output[150];
int16_t word;
int i, j;
#ifdef DEBUG_CODER
printf("Encoding block to transmit:\n");
printf("0123456.01234567\n");
#endif
for (i = 0; i < 10; i++) {
word = 0;
for (j = 0; j < 7; j++)
word = (word << 1) | (input[(9 - i) * 7 + j] == '1');
word = block_code[word];
for (j = 0; j < 15; j++) {
output[i * 15 + j] = ((word >> j) & 1) + '0';
#ifdef DEBUG_CODER
printf("%c", output[i * 15 + j]);
if (j == 6)
printf(".");
#endif
}
#ifdef DEBUG_CODER
printf("\n");
#endif
}
return output;
}
/* decode data block
* input: 10*15 code words (LSB first)
* output: 70 data bits MSB first
* FTZ 171 TR 60 / 5.1.1.3 */
static const char *decode(const char *input, int *_bit_errors)
{
int failed = 0, warn = 0;
char fail_str[11];
static char output[70];
uint16_t word;
int i, j;
#ifdef DEBUG_CODER
printf("Decoding received block:\n");
printf("0123456.01234567 Without errors: Error bits:\n");
#endif
for (i = 0; i < 10; i++) {
word = 0;
for (j = 0; j < 15; j++) {
word = (word << 1) | (input[i * 15 + 14 - j] == '1');
#ifdef DEBUG_CODER
printf("%c", input[i * 15 + j]);
if (j == 6)
printf(".");
#endif
}
word = block_decode[word];
for (j = 0; j < 7; j++) {
output[(9 - i) * 7 + j] = ((word >> (6 - j)) & 1) + '0';
}
if (word > 0x2ff) {
failed = 1;
fail_str[i] = 'X';
} else
if (word > 0x1ff) {
warn += 2;
fail_str[i] = '2';
}else
if (word > 0xff) {
warn += 1;
fail_str[i] = '1';
} else
fail_str[i] = '.';
#ifdef DEBUG_CODER
if (word > 0x2ff)
printf("decode failed");
else {
printf(" ");
for (j = 0; j < 15; j++) {
printf("%c", blockcode[word & 0x7f][j]);
if (j == 6)
printf(".");
}
printf(" ");
for (j = 0; j < 15; j++) {
if (blockcode[word & 0x7f][j] != input[i * 15 + j])
printf("*");
else
printf("-");
if (j == 6)
printf(".");
}
}
printf("\n");
#endif
}
fail_str[10] = '\0';
if (failed)
PDEBUG(DFRAME, DEBUG_DEBUG, "Received Telegram with these block errors: '%s' (X = uncorrectable)\n", fail_str);
else if (warn)
PDEBUG(DFRAME, DEBUG_DEBUG, "Received Telegram with these block errors: '%s' (1 / 2 = correctable)\n", fail_str);
else
PDEBUG(DFRAME, DEBUG_DEBUG, "Received Telegram with no block errors.\n");
if (failed)
return NULL;
*_bit_errors = warn;
return output;
}
/* interleving of code words
* input: 10*15 code words (LSB first)
* output: stream of 33 sync + 1 + 150 interleaved bits
* FTZ 171 TR 60 / 5.1.1.2 and 5.1.1.2 */
static char *interleave(const char *input)
{
static char output[185]; /* + termination char for debug */
int i, j;
strcpy(output, barker_string);
strcpy(output + 11, barker_string);
strcpy(output + 22, barker_string);
output[33] = '1';
#ifdef DEBUG_BLOCK
printf("Interleaving block to transmit:\n");
#endif
for (i = 0; i < 10; i++) {
for (j = 0; j < 15; j++) {
output[i + j * 10 + 34] = input[j + i * 15];
#ifdef DEBUG_BLOCK
printf("%c", input[j + i * 15]);
#endif
}
#ifdef DEBUG_BLOCK
printf("\n");
#endif
}
#ifdef DEBUG_RAW
output[184] = '\0';
printf("Raw TX: %s\n", output + 34);
#endif
return output;
}
/* deinterleave of code words
* input: stream of 150 interleaved bits
* output: 10*15 code words (LSB first)
* FTZ 171 TR 60 / 5.1.1.4 */
static const char *deinterleave(const char *input)
{
static char output[150];
int i, j;
#ifdef DEBUG_RAW
char debug_bits[151];
memcpy(debug_bits, input, 150);
debug_bits[151] = '\0';
printf("Raw RX: %s\n", debug_bits);
#endif
#ifdef DEBUG_BLOCK
printf("Deinterleaving received block:\n");
#endif
for (i = 0; i < 10; i++) {
for (j = 0; j < 15; j++) {
output[j + i * 15] = input[i + j * 10];
#ifdef DEBUG_BLOCK
printf("%c", output[j + i * 15]);
#endif
}
#ifdef DEBUG_BLOCK
printf("\n");
#endif
}
return output;
}
void cnetz_decode_telegramm(cnetz_t *cnetz, const char *bits, double level, double sync_time, double jitter)
{
telegramm_t telegramm;
uint8_t opcode;
int i;
int block;
int bit_errors;
bits = deinterleave(bits);
bits = decode(bits, &bit_errors);
if (!bits)
return;
/* filter out mysterious zero-telegramm */
for (i = 0; i < 70; i++) {
if (bits[i] != bits[0])
break;
}
if (i == 70) {
PDEBUG(DFRAME, DEBUG_INFO, "Ignoring mysterious unmodulated telegramm (noise from phone's transmitter)\n");
return;
}
disassemble_telegramm(&telegramm, bits, cnetz->auth);
opcode = telegramm.opcode;
telegramm.level = level;
telegramm.sync_time = sync_time;
telegramm.jitter = jitter;
if (bit_errors)
PDEBUG(DDSP, DEBUG_INFO, "RX Level: %.0f%% Jitter: %.2f Sync Time: %.2f (TS %.2f) Bit errors: %d %s\n", fabs(level) * 32767.0 / cnetz->fsk_deviation * 100.0, jitter, sync_time, sync_time / 396.0, bit_errors, (level < 0) ? "NEGATIVE" : "POSITIVE");
else
PDEBUG(DDSP, DEBUG_INFO, "RX Level: %.0f%% Jitter: %.2f Sync Time: %.2f (TS %.2f) %s\n", fabs(level) * 32767.0 / cnetz->fsk_deviation * 100.0, jitter, sync_time, sync_time / 396.0, (level < 0) ? "NEGATIVE" : "POSITIVE");
if (cnetz->sender.loopback) {
PDEBUG(DFRAME, DEBUG_NOTICE, "Received Telegramm in loopback test mode (opcode %d = %s)\n", opcode, definition_opcode[opcode].message_name);
cnetz_sync_frame(cnetz, sync_time, -1);
return;
}
if (opcode >= 32) {
PDEBUG(DFRAME, DEBUG_NOTICE, "Received Telegramm that is not used by mobile station, ignoring! (opcode %d = %s)\n", opcode, definition_opcode[opcode].message_name);
return;
}
if (definition_opcode[opcode].block == BLOCK_I) {
PDEBUG(DFRAME, DEBUG_NOTICE, "Received Telegramm that is an illegal opcode, ignoring! (opcode %d = %s)\n", opcode, definition_opcode[opcode].message_name);
return;
}
/* auto select cell */
if (cnetz->cell_auto) {
if (!match_fuz(cnetz, &telegramm, 0)) {
cnetz->cell_nr = 1;
selected:
cnetz->cell_auto = 0;
printf("***********************************************\n");
printf("*** Autoselecting %stive FSK TX polarity! ***\n", (si[cnetz->cell_nr].flip_polarity) ? "nega" : "posi");
printf("***********************************************\n");
} else if (!match_fuz(cnetz, &telegramm, 1)) {
cnetz->cell_nr = 0;
goto selected;
} else {
PDEBUG(DFRAME, DEBUG_NOTICE, "Received Telegramm with no cell number, ignoring!\n");
return;
}
}
switch (cnetz->dsp_mode) {
case DSP_MODE_OGK:
if (definition_opcode[opcode].block != BLOCK_R && definition_opcode[opcode].block != BLOCK_M) {
PDEBUG(DFRAME, DEBUG_NOTICE, "Received Telegramm that is not used OgK channel signalling, ignoring! (opcode %d = %s)\n", opcode, definition_opcode[opcode].message_name);
return;
}
/* determine block by last timeslot sent and by message type */
block = cnetz->sched_last_ts[cnetz->cell_nr] * 2;
if (definition_opcode[opcode].block == BLOCK_M)
block++;
cnetz_receive_telegramm_ogk(cnetz, &telegramm, block);
break;
case DSP_MODE_SPK_K:
if (definition_opcode[opcode].block != BLOCK_K) {
PDEBUG(DFRAME, DEBUG_NOTICE, "Received Telegramm that is not used for concentrated signalling, ignoring! (opcode %d = %s)\n", opcode, definition_opcode[opcode].message_name);
return;
}
cnetz_receive_telegramm_spk_k(cnetz, &telegramm);
break;
case DSP_MODE_SPK_V:
if (definition_opcode[opcode].block != BLOCK_V) {
PDEBUG(DFRAME, DEBUG_NOTICE, "Received Telegramm that is not used for distributed signalling, ignoring! (opcode %d = %s)\n", opcode, definition_opcode[opcode].message_name);
return;
}
cnetz_receive_telegramm_spk_v(cnetz, &telegramm);
break;
default:
;
}
}
const char *cnetz_encode_telegramm(cnetz_t *cnetz)
{
const telegramm_t *telegramm = NULL;
uint8_t opcode;
char *bits;
switch (cnetz->dsp_mode) {
case DSP_MODE_OGK:
if (!cnetz->sched_r_m)
telegramm = cnetz_transmit_telegramm_rufblock(cnetz);
else
telegramm = cnetz_transmit_telegramm_meldeblock(cnetz);
break;
case DSP_MODE_SPK_K:
telegramm = cnetz_transmit_telegramm_spk_k(cnetz);
break;
case DSP_MODE_SPK_V:
telegramm = cnetz_transmit_telegramm_spk_v(cnetz);
break;
default:
;
}
opcode = telegramm->opcode;
bits = assemble_telegramm(telegramm, (opcode != OPCODE_LR_R) && (opcode != OPCODE_MLR_M));
bits = encode(bits);
bits = interleave(bits);
/* invert, if polarity of the cell is negative */
if (si[cnetz->cell_nr].flip_polarity) {
int i;
for (i = 0; i < 184; i++)
bits[i] ^= 1;
}
return bits;
}