aboutsummaryrefslogtreecommitdiffstats
path: root/src/nmt
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2017-07-22 10:38:18 +0200
committerAndreas Eversberg <jolly@eversberg.eu>2017-08-08 12:52:45 +0200
commit3274812eab64469a118dc66f902eb182d55975e9 (patch)
tree43c3bc6419236b3c2db1d1dd43fab6ad74d62f7c /src/nmt
parent0c9de251bedc16e51a1b5f5dc2735fa878708098 (diff)
NMT-900 System implementation
NMT can now be run as 450 or 900 Network.
Diffstat (limited to 'src/nmt')
-rw-r--r--src/nmt/countries.c151
-rw-r--r--src/nmt/countries.h10
-rw-r--r--src/nmt/frame.c281
-rw-r--r--src/nmt/frame.h16
-rw-r--r--src/nmt/main.c56
-rw-r--r--src/nmt/nmt.c141
-rw-r--r--src/nmt/nmt.h16
7 files changed, 488 insertions, 183 deletions
diff --git a/src/nmt/countries.c b/src/nmt/countries.c
index c809c91..f2e58fd 100644
--- a/src/nmt/countries.c
+++ b/src/nmt/countries.c
@@ -32,7 +32,7 @@ struct nmt_frequency {
};
/* channel allocation used in scandinavian countries */
-static struct nmt_frequency frq_scandinavia[] = {
+static struct nmt_frequency frq_450_scandinavia[] = {
{ 1, 180, 463.000, 0.025, 1.0, 10.0, 1 },
{ 181,200, 462.500, 0.025, 1.0, 10.0, 1 },
{ 201,380, 463.0125, 0.025, 1.0, 10.0, 1 },
@@ -40,45 +40,69 @@ static struct nmt_frequency frq_scandinavia[] = {
{ 0, 0, 0.0, 0.0, 0.0, 0.0, 0 }
};
+static struct nmt_frequency frq_900_scandinavia[] = {
+ { 1, 1000, 935.0125, 0.025, 1.0, 45.0, 0 },
+ { 1025,2023, 935.025, 0.025, 1.0, 45.0, 0 },
+ { 0, 0, 0.0, 0.0, 0.0, 0.0, 0 }
+};
+
/* channel allocation used in Netherlands, Luxemburg, Belgium */
-static struct nmt_frequency frq_nl_l_b[] = {
+static struct nmt_frequency frq_450_nl_l_b[] = {
{ 1, 222, 461.310, 0.020, 0.8, 10.0, 0 },
{ 0, 0, 0.0, 0.0, 0.0, 0.0, 0 }
};
+static struct nmt_frequency frq_900_nl[] = {
+ { 1, 1000, 935.0125, 0.025, 1.0, 45.0, 0 },
+ { 0, 0, 0.0, 0.0, 0.0, 0.0, 0 }
+};
+
/* channel allocation used in Malaysia */
-static struct nmt_frequency frq_mal[] = {
+static struct nmt_frequency frq_450_mal[] = {
{ 1, 180, 462.000, 0.025, 1.0, 10.0, 0 },
{ 0, 0, 0.0, 0.0, 0.0, 0.0, 0 }
};
/* channel allocation used in Thailand, Indonesia */
-static struct nmt_frequency frq_t_ri[] = {
+static struct nmt_frequency frq_450_t_ri[] = {
{ 1, 224, 489.000, 0.020, 0.8, 10.0, 0 },
{ 0, 0, 0.0, 0.0, 0.0, 0.0, 0 }
};
/* channel allocation used in Spain */
-static struct nmt_frequency frq_e[] = {
+static struct nmt_frequency frq_450_e[] = {
{ 1, 180, 464.325, 0.025, 1.0, 10.0, 0 },
{ 0, 0, 0.0, 0.0, 0.0, 0.0, 0 }
};
/* channel allocation used in Austria */
-static struct nmt_frequency frq_a[] = {
+static struct nmt_frequency frq_450_a[] = {
{ 1, 180, 465.730, -0.020, 0.8, 10.0, 0 },
{ 0, 0, 0.0, 0.0, 0.0, 0.0, 0 }
};
/* channel allocation used in Czech Republic and Slovakia */
-static struct nmt_frequency frq_cz_sk[] = {
+static struct nmt_frequency frq_450_cz_sk[] = {
{ 1, 222, 465.730, -0.020, 0.8, 10.0, 0 },
{ 0, 0, 0.0, 0.0, 0.0, 0.0, 0 }
};
+/* channel allocation used in Turkey */
+static struct nmt_frequency frq_900_tr[] = {
+ { 1, 180, 461.500, 0.025, 1.0, 10.0, 0 },
+ { 0, 0, 0.0, 0.0, 0.0, 0.0, 0 }
+};
+
+/* channel allocation used in France */
+static struct nmt_frequency frq_900_f[] = {
+ { 1, 540, 430.050, 0.025, 1.0, -10.0, 0 },
+ { 0, 0, 0.0, 0.0, 0.0, 0.0, 0 }
+};
+
/* country selector */
static struct nmt_country {
- int tested;
+ int system; /* 450 or 900 */
+ int tested; /* tested with a real phone */
int y; /* country code of traffic area */
int first_ta, last_ta; /* range of used traffic areas */
const char *short_name;
@@ -86,47 +110,64 @@ static struct nmt_country {
const char *provider_name;
struct nmt_frequency *nmt_frequency; /* list of frequency allocations */
} nmt_country[] = {
- { 1, 5, 1,9, "DK", "Denmark", "Tele Danmark Mobile", frq_scandinavia },
- { 1, 6, 1,9, "SE", "Sweden", "Telia Mobitel", frq_scandinavia },
- { 1, 7, 1,9, "NO", "Norway", "Telenor Mobil", frq_scandinavia },
- { 1, 8, 1,9, "FI", "Finland", "Telecom Finland", frq_scandinavia },
- { 1, 8, 1,9, "IS", "Iceland", "Post & Telecom", frq_scandinavia },
- { 1, 5, 1,9, "FO", "Faroe Island", "Faroese Telecom", frq_scandinavia },
- { 1, 7, 1,9, "EE", "Estonia", "Eesti Mobiiltelefon", frq_scandinavia },
- { 1, 5, 1,9, "LV", "Latvia", "Latvian Mobile Telephone", frq_scandinavia },
- { 1, 8, 1,9, "LT", "Lithuania", "COMLIET", frq_scandinavia },
- { 1, 6, 1,9, "BY", "Belarus", "Belcel", frq_scandinavia },
- { 1, 5, 1,9, "MO", "OSS/Moscow", "Moscow Cellular Comm.", frq_scandinavia },
- { 1, 6, 1,9, "STP", "OSS/St Petersburg", "Delta Telecom", frq_scandinavia },
- { 1, 6, 1,9, "STP", "OSS/Leningrads Dist.", "Delta Telecom", frq_scandinavia },
- { 1, 7, 1,9, "CAR", "OSS/Carelian Rep.", "Telecom Finland", frq_scandinavia },
- { 1, 5, 1,9, "MUR", "OSS/Murmansk", "Telecom Finland", frq_scandinavia },
- { 1, 5, 1,9, "LED", "OSS/Leningrads Dist.", "Telecom Finland", frq_scandinavia },
- { 1, 5, 1,9, "KAL", "Kaliningrad", "Telecom Finland", frq_scandinavia },
- { 1, 7, 1,9, "PL", "Poland", "CENTERTEL", frq_scandinavia },
- { 1, 6, 1,9, "BG", "Bulgaria", "MOBIFON", frq_scandinavia },
- { 1, 5, 1,9, "RO", "Romania", "Telefonica Romania", frq_scandinavia },
- { 1, 6, 1,9, "UA", "Ukraine", "Ukraine Mobile Comm.", frq_scandinavia },
- { 1, 1, 1,9, "RU1", "", "", frq_scandinavia },
- { 1, 2, 1,9, "RU2", "", "", frq_scandinavia },
- { 1, 3, 1,9, "RU3", "", "", frq_scandinavia },
- { 1, 4, 1,9, "RU4", "", "", frq_scandinavia },
- { 1, 1, 1,9, "NL", "Netherlands", "Royal Dutch Post & Telecom", frq_nl_l_b },
- { 1, 1, 15,15, "L", "Luxemburg", "Enterprise des P&T Luxembourg",frq_nl_l_b },
- { 1, 2, 1,9, "B", "Belgium", "Belgacom Mobile", frq_nl_l_b },
- { 1, 7, 1,9, "CZ", "Czech Republic", "Eurotel Prague", frq_cz_sk },
- { 1, 6, 1,9, "SK", "Slovakia", "Eurotel Bratislava", frq_cz_sk },
+ /* 450 */
+ { 450, 1, 5, 1,9, "DK", "Denmark", "Tele Danmark Mobile", frq_450_scandinavia },
+ { 450, 1, 6, 1,9, "SE", "Sweden", "Telia Mobitel", frq_450_scandinavia },
+ { 450, 1, 7, 1,9, "NO", "Norway", "Telenor Mobil", frq_450_scandinavia },
+ { 450, 1, 8, 1,9, "FI", "Finland", "Telecom Finland", frq_450_scandinavia },
+ { 450, 1, 8, 1,9, "IS", "Iceland", "Post & Telecom", frq_450_scandinavia },
+ { 450, 1, 5, 1,9, "FO", "Faroe Island", "Faroese Telecom", frq_450_scandinavia },
+ { 450, 1, 7, 1,9, "EE", "Estonia", "Eesti Mobiiltelefon", frq_450_scandinavia },
+ { 450, 1, 5, 1,9, "LV", "Latvia", "Latvian Mobile Telephone", frq_450_scandinavia },
+ { 450, 1, 8, 1,9, "LT", "Lithuania", "COMLIET", frq_450_scandinavia },
+ { 450, 1, 6, 1,9, "BY", "Belarus", "Belcel", frq_450_scandinavia },
+ { 450, 1, 5, 1,9, "MO", "OSS/Moscow", "Moscow Cellular Comm.", frq_450_scandinavia },
+ { 450, 1, 6, 1,9, "STP", "OSS/St Petersburg", "Delta Telecom", frq_450_scandinavia },
+ { 450, 1, 6, 1,9, "STP", "OSS/Leningrads Dist.", "Delta Telecom", frq_450_scandinavia },
+ { 450, 1, 7, 1,9, "CAR", "OSS/Carelian Rep.", "Telecom Finland", frq_450_scandinavia },
+ { 450, 1, 5, 1,9, "MUR", "OSS/Murmansk", "Telecom Finland", frq_450_scandinavia },
+ { 450, 1, 5, 1,9, "LED", "OSS/Leningrads Dist.", "Telecom Finland", frq_450_scandinavia },
+ { 450, 1, 5, 1,9, "KAL", "Kaliningrad", "Telecom Finland", frq_450_scandinavia },
+ { 450, 1, 7, 1,9, "PL", "Poland", "CENTERTEL", frq_450_scandinavia },
+ { 450, 1, 6, 1,9, "BG", "Bulgaria", "MOBIFON", frq_450_scandinavia },
+ { 450, 1, 5, 1,9, "RO", "Romania", "Telefonica Romania", frq_450_scandinavia },
+ { 450, 1, 6, 1,9, "UA", "Ukraine", "Ukraine Mobile Comm.", frq_450_scandinavia },
+ { 450, 1, 1, 1,9, "RU1", "", "", frq_450_scandinavia },
+ { 450, 1, 2, 1,9, "RU2", "", "", frq_450_scandinavia },
+ { 450, 1, 3, 1,9, "RU3", "", "", frq_450_scandinavia },
+ { 450, 1, 4, 1,9, "RU4", "", "", frq_450_scandinavia },
+ { 450, 1, 1, 1,9, "NL", "Netherlands", "Royal Dutch Post & Telecom", frq_450_nl_l_b },
+ { 450, 1, 1, 15,15, "L", "Luxemburg", "Enterprise des P&T Luxembourg",frq_450_nl_l_b },
+ { 450, 1, 2, 1,9, "B", "Belgium", "Belgacom Mobile", frq_450_nl_l_b },
+ { 450, 1, 7, 1,9, "CZ", "Czech Republic", "Eurotel Prague", frq_450_cz_sk },
+ { 450, 1, 6, 1,9, "SK", "Slovakia", "Eurotel Bratislava", frq_450_cz_sk },
+ /* 900 */
+ { 900, 1, 1, 1,9, "DK", "Denmark", "Tele Danmark Mobile", frq_900_scandinavia },
+ { 900, 1, 2, 1,9, "SE", "Sweden", "Telia Mobitel", frq_900_scandinavia },
+ { 900, 1, 3, 1,9, "NO", "Norway", "Telenor Mobil", frq_900_scandinavia },
+ { 900, 1, 4, 1,9, "FI", "Finland", "Telecom Finland", frq_900_scandinavia },
/* untested... */
- { 0, 8, 8,8, "MAL", "Malaysia", "Jabatan Telekom Malaysia", frq_mal },
- { 0, 4, 1,9, "T", "Thailand", "Telephone Organization of Thailand",frq_t_ri },
- { 0, 8, 1,9, "E", "Spain", "Telefonica Servicios Moviles", frq_e },
- { 0, 8, 1,1, "RI", "Indonesia", "PT Mobisel", frq_t_ri },
- { 0, 0, 1,3, "A", "Austria", "PTV", frq_a },
- { 0, 9, 1,3, "A2", "Austria 2", "PTV", frq_a },
- { 0, 0, 0,0, NULL, NULL, NULL, NULL }
+ { 450, 0, 8, 8,8, "MAL", "Malaysia", "Jabatan Telekom Malaysia", frq_450_mal },
+ { 450, 0, 4, 1,9, "T", "Thailand", "Telephone Organization of Thailand",frq_450_t_ri },
+ { 450, 0, 8, 1,9, "E", "Spain", "Telefonica Servicios Moviles", frq_450_e },
+ { 450, 0, 8, 1,1, "RI", "Indonesia", "PT Mobisel", frq_450_t_ri },
+ { 450, 0, 0, 1,3, "A", "Austria", "PTV", frq_450_a },
+ { 450, 0, 9, 1,3, "A2", "Austria 2", "PTV", frq_450_a },
+ { 900, 0, 5, 1,9, "CH", "Switzerland", "PTT", frq_900_scandinavia },
+ { 900, 0, 6, 1,15, "NL", "Netherlands", "Royal Dutch Post & Telecom", frq_900_nl },
+ { 900, 0, 1, 1,9, "TR", "Turkey", "Turkcell", frq_900_tr },
+ { 900, 0, 0, 1,9, "F0", "France (Group 0)", "France Telecom", frq_900_f },
+ { 900, 0, 1, 1,9, "F1", "France (Group 1)", "France Telecom", frq_900_f },
+ { 900, 0, 2, 1,9, "F2", "France (Group 2)", "France Telecom", frq_900_f },
+ { 900, 0, 3, 1,9, "F3", "France (Group 3)", "France Telecom", frq_900_f },
+ { 900, 0, 4, 1,9, "F4", "France (Group 4)", "France Telecom", frq_900_f },
+ { 900, 0, 5, 1,9, "F5", "France (Group 5)", "France Telecom", frq_900_f },
+ { 900, 0, 6, 1,9, "F6", "France (Group 6)", "France Telecom", frq_900_f },
+ { 900, 0, 7, 1,9, "F7", "France (Group 7)", "France Telecom", frq_900_f },
+ { 0,0, 0, 0,0, NULL, NULL, NULL, NULL }
};
-void nmt_country_list(void)
+void nmt_country_list(int nmt_system)
{
int i, j;
int ch_from = 0, ch_to = 0;
@@ -135,6 +176,8 @@ void nmt_country_list(void)
printf("TA from\tTA to\tYY Code\tChannels\tShort\tCountry (Provider)\n");
printf("--------------------------------------------------------------------------------\n");
for (i = 0; nmt_country[i].short_name; i++) {
+ if (nmt_system != nmt_country[i].system)
+ continue;
printf("%s,%d\t", nmt_country[i].short_name, nmt_country[i].first_ta);
if (nmt_country[i].first_ta != nmt_country[i].last_ta)
printf("%s,%d", nmt_country[i].short_name, nmt_country[i].last_ta);
@@ -155,11 +198,13 @@ void nmt_country_list(void)
}
}
-int nmt_country_by_short_name(const char *short_name)
+int nmt_country_by_short_name(int nmt_system, const char *short_name)
{
int i;
for (i = 0; nmt_country[i].short_name; i++) {
+ if (nmt_system != nmt_country[i].system)
+ continue;
if (!strcasecmp(nmt_country[i].short_name, short_name))
return nmt_country[i].y;
}
@@ -167,11 +212,13 @@ int nmt_country_by_short_name(const char *short_name)
return -1;
}
-const char *nmt_long_name_by_short_name(const char *short_name)
+const char *nmt_long_name_by_short_name(int nmt_system, const char *short_name)
{
int i;
for (i = 0; nmt_country[i].short_name; i++) {
+ if (nmt_system != nmt_country[i].system)
+ continue;
if (!strcasecmp(nmt_country[i].short_name, short_name))
return nmt_country[i].long_name;
}
@@ -179,11 +226,13 @@ const char *nmt_long_name_by_short_name(const char *short_name)
return NULL;
}
-int nmt_ta_by_short_name(const char *short_name, int ta)
+int nmt_ta_by_short_name(int nmt_system, const char *short_name, int ta)
{
int i;
for (i = 0; nmt_country[i].short_name; i++) {
+ if (nmt_system != nmt_country[i].system)
+ continue;
if (!strcasecmp(nmt_country[i].short_name, short_name) && ta >= nmt_country[i].first_ta && ta <= nmt_country[i].last_ta)
return ta;
}
@@ -193,12 +242,14 @@ int nmt_ta_by_short_name(const char *short_name, int ta)
/* Convert channel number to frequency number of base station.
Set 'uplink' to 1 to get frequency of mobile station. */
-double nmt_channel2freq(const char *short_name, int channel, int uplink, double *deviation_factor, int *scandinavia, int *tested)
+double nmt_channel2freq(int nmt_system, const char *short_name, int channel, int uplink, double *deviation_factor, int *scandinavia, int *tested)
{
int i, j;
double freq;
for (i = 0; nmt_country[i].short_name; i++) {
+ if (nmt_system != nmt_country[i].system)
+ continue;
if (!strcasecmp(nmt_country[i].short_name, short_name)) {
for (j = 0; nmt_country[i].nmt_frequency[j].first_frequency; j++) {
if (channel >= nmt_country[i].nmt_frequency[j].first_channel
diff --git a/src/nmt/countries.h b/src/nmt/countries.h
index b8a7121..adfbdde 100644
--- a/src/nmt/countries.h
+++ b/src/nmt/countries.h
@@ -1,7 +1,7 @@
-void nmt_country_list(void);
-int nmt_country_by_short_name(const char *short_name);
-const char *nmt_long_name_by_short_name(const char *short_name);
-int nmt_ta_by_short_name(const char *short_name, int ta);
-double nmt_channel2freq(const char *short_name, int channel, int uplink, double *deviation_factor, int *scandinavia, int *tested);
+void nmt_country_list(int nmt_system);
+int nmt_country_by_short_name(int nmt_system, const char *short_name);
+const char *nmt_long_name_by_short_name(int nmt_system, const char *short_name);
+int nmt_ta_by_short_name(int nmt_system, const char *short_name, int ta);
+double nmt_channel2freq(int nmt_system, const char *short_name, int channel, int uplink, double *deviation_factor, int *scandinavia, int *tested);
diff --git a/src/nmt/frame.c b/src/nmt/frame.c
index 8cad3ca..b64a37c 100644
--- a/src/nmt/frame.c
+++ b/src/nmt/frame.c
@@ -29,41 +29,116 @@
#include "frame.h"
#include "hagelbarger.h"
-uint64_t nmt_encode_channel(int channel, int power)
+uint64_t nmt_encode_channel(int nmt_system, int channel, int power)
{
uint64_t value = 0;
- if (channel >= 200) {
- value |= 0x800;
- channel -= 200;
- }
- if (channel >= 100) {
- value |= 0x100;
- channel -= 100;
+ if (nmt_system == 450) {
+ if (channel >= 200) {
+ value |= 0x800;
+ channel -= 200;
+ }
+ if (channel >= 100) {
+ value |= 0x100;
+ channel -= 100;
+ }
+ value |= channel % 10;
+ value |= (channel / 10) << 4;
+ value |= power << 9;
+ } else {
+ /* interleaved channels are indicated in traffic area */
+ if (value > 1000)
+ value -= 1000;
+ value |= channel;
+ /* if channel >= 512, set upper bit */
+ if (value & 0x200)
+ value = value - 0x200 + 0x800;
+ value |= power << 9;
}
- value |= channel % 10;
- value |= (channel / 10) << 4;
- value |= power << 9;
return value;
}
-int nmt_decode_channel(uint64_t value, int *channel, int *power)
+int nmt_decode_channel(int nmt_system, uint64_t value, int *channel, int *power)
{
- if ((value & 0x00f) > 0x009)
- return -1;
- if ((value & 0x0f0) > 0x090)
- return -1;
-
- *channel = (value & 0x00f) +
- ((value & 0x0f0) >> 4) * 10 +
- ((value & 0x100) >> 8) * 100 +
- ((value & 0x800) >> 11) * 200;
+ if (nmt_system == 450) {
+ if ((value & 0x00f) > 0x009)
+ return -1;
+ if ((value & 0x0f0) > 0x090)
+ return -1;
+
+ *channel = (value & 0x00f) +
+ ((value & 0x0f0) >> 4) * 10 +
+ ((value & 0x100) >> 8) * 100 +
+ ((value & 0x800) >> 11) * 200;
+ } else {
+ *channel = (value & 0x1ff) +
+ ((value & 0x800) >> 2);
+ }
*power = (value & 0x600) >> 9;
return 0;
}
+uint64_t nmt_encode_tc(int nmt_system, int channel, int power)
+{
+ uint64_t value = 0;
+
+ if (nmt_system == 450) {
+ if (channel >= 200) {
+ value |= 0x800;
+ channel -= 200;
+ }
+ if (channel >= 100) {
+ value |= 0x100;
+ channel -= 100;
+ }
+ value |= channel % 10;
+ value |= (channel / 10) << 4;
+ value |= power << 9;
+ } else {
+ value = channel;
+ }
+
+ return value;
+}
+
+static int nmt_decode_tc(int nmt_system, uint64_t value, int *channel, int *power)
+{
+ if (nmt_system == 450) {
+ if ((value & 0x00f) > 0x009)
+ return -1;
+ if ((value & 0x0f0) > 0x090)
+ return -1;
+
+ *channel = (value & 0x00f) +
+ ((value & 0x0f0) >> 4) * 10 +
+ ((value & 0x100) >> 8) * 100 +
+ ((value & 0x800) >> 11) * 200;
+ *power = (value & 0x600) >> 9;
+ } else {
+ *channel = value & 0x3ff;
+ }
+
+ return 0;
+}
+
+uint64_t nmt_encode_traffic_area(int nmt_system, int channel, uint8_t traffic_area)
+{
+ uint64_t value = 0;
+
+ if (nmt_system == 450) {
+ value = traffic_area;
+ } else {
+ /* upper bit is used for indication of interleaved channel */
+ value = traffic_area & 0x7f;
+ if (channel > 1000)
+ value |= 0x80;
+ }
+
+ return value;
+}
+
void nmt_value2digits(uint64_t value, char *digits, int num)
{
int digit, i;
@@ -214,24 +289,30 @@ static struct nmt_frame {
} nmt_frame[] = {
/* Define Digits Dir. Prefix Nr. Description */
{ NMT_MESSAGE_1a, "NNNPYYHHHHHHHHHH", MTX_TO_MS, 12, "1a", "Calling channel indication" },
+ { NMT_MESSAGE_1a_a, "NNNPYYHHHHHHHHHH", MTX_TO_MS, 11, "1a'", "Calling channel indication (for MS group A)" },
+ { NMT_MESSAGE_1a_b, "NNNPYYHHHHHHHHHH", MTX_TO_MS, 13, "1a''", "Calling channel indication (for MS group B)" },
{ NMT_MESSAGE_1b, "NNNPYYHHHHHHHHHH", MTX_TO_MS, 4, "1b", "Combined calling and traffic channel indication" },
{ NMT_MESSAGE_2a, "NNNPYYZXXXXXXHHH", MTX_TO_MS, 12, "2a", "Call to mobile subscriber on calling channel" },
{ NMT_MESSAGE_2b, "NNNPYYZXXXXXXnnn", MTX_TO_MS, 12, "2b", "Traffic channel allocation on calling channel" },
{ NMT_MESSAGE_2c, "NNNPYYZXXXXXXHHH", MTX_TO_MS, 12, "2c", "Queueing information to MS with priority on calling channel" },
{ NMT_MESSAGE_2d, "NNNPYYZXXXXXXHHH", MTX_TO_MS, 12, "2d", "Traffic channel scanning order on calling channel" },
+ { NMT_MESSAGE_2e, "NNNPYYZXXXXXXHHH", MTX_TO_MS, 4, "2e", "Alternative type of call to MS on combinded CC/TC" },
{ NMT_MESSAGE_2f, "NNNPYYZXXXXXXHHH", MTX_TO_MS, 12, "2f", "Queuing information to ordinary MS" },
{ NMT_MESSAGE_3a, "NNNPYYZXXXXXXnnn", MTX_TO_MS, 5, "3a", "Traffic channel allocation on traffic channel" },
{ NMT_MESSAGE_3b, "NNNPYYZXXXXXXHHH", MTX_TO_MS, 5, "3b", "Identity request on traffic channel" },
{ NMT_MESSAGE_3c, "NNNPYYZXXXXXXnnn", MTX_TO_MS, 9, "3c", "Traffic channel allocation on traffic channel, short procedure" },
+ { NMT_MESSAGE_3d, "NNNPYYZXXXXXXnnn", MTX_TO_MS, 7, "3d", "Traffic channel allocation on access channel" },
{ NMT_MESSAGE_4, "NNNPYYJJJJJJJHHH", MTX_TO_MS, 3, "4", "Free traffic channel indication" },
+ { NMT_MESSAGE_4b, "NNNPYYJJJJJJJHHH", MTX_TO_MS, 7, "4b", "Access channel indication" },
{ NMT_MESSAGE_5a, "NNNPYYZXXXXXXLLL", MTX_TO_MS, 6, "5a", "Line signal" },
{ NMT_MESSAGE_5b, "NNNPYYZXXXXXXLQQ", MTX_TO_MS, 6, "5b", "Line signal: Answer to coin-box" },
{ NMT_MESSAGE_6, "JJJPJJJJJJJJJJJJ", MTX_TO_XX, 0, "6", "Idle frame" },
{ NMT_MESSAGE_7, "NNNPYYCCCCCCCJJJ", MTX_TO_MS, 8, "7", "Authentication request" },
{ NMT_MESSAGE_8, "NNNPYYMHHHHHHHWW", MTX_TO_MS, 1, "8", "A-subscriber number" },
- { NMT_MESSAGE_10a, "NNNPZXXXXXXTJJJJ", MS_TO_MTX, 1, "10a", "Call acknowledgment from MS on calling channel (shortened frame)" },
+ { NMT_MESSAGE_10a, "NNNPZXXXXXXTJJJJ", MS_TO_MTX, 1, "10a", "Call acknowledgment from MS on calling channel and access on access channel (shortened frame)" },
{ NMT_MESSAGE_10b, "NNNPZXXXXXXTYKKK", MS_TO_MTX, 1, "10b", "Seizure from ordinary MS and identity on traffic channel" },
{ NMT_MESSAGE_10c, "NNNPZXXXXXXTYKKK", MS_TO_MTX, 6, "10c", "Seizure and identity from called MS on traffic channel" },
+ { NMT_MESSAGE_10d, "NNNPZXXXXXXTJJJJ", MS_TO_MTX, 6, "10c", "Call acknowledgement from MS on the alternative type of call on combined CC/TC (shortened frame)" },
{ NMT_MESSAGE_11a, "NNNPZXXXXXXTYKKK", MS_TO_MTX, 14, "11a", "Roaming updating seizure and identity on traffic channel" },
{ NMT_MESSAGE_11b, "NNNPZXXXXXXTYKKK", MS_TO_MTX, 15, "11b", "Seizure and call achnowledgment on calling channel from MS with priority (shortened frame)" },
{ NMT_MESSAGE_12, "NNNPZXXXXXXTYKKK", MS_TO_MTX, 11, "12", "Seizure from coin-box on traffic channel" },
@@ -288,12 +369,40 @@ static const char *param_hex(uint64_t value, int __attribute__((unused)) ndigits
return result;
}
-static const char *param_channel_no(uint64_t value, int __attribute__((unused)) ndigits, enum nmt_direction __attribute__((unused)) direction)
+static const char *param_channel_no_450(uint64_t value, int __attribute__((unused)) ndigits, enum nmt_direction __attribute__((unused)) direction)
+{
+ static char result[32];
+ int rc, channel, power;
+
+ rc = nmt_decode_channel(450, value, &channel, &power);
+ if (rc < 0)
+ sprintf(result, "invalid(%" PRIu64 ")", value);
+ else
+ sprintf(result, "channel=%d power=%d", channel, power);
+
+ return result;
+}
+
+static const char *param_channel_no_900(uint64_t value, int __attribute__((unused)) ndigits, enum nmt_direction __attribute__((unused)) direction)
+{
+ static char result[32];
+ int rc, channel, power;
+
+ rc = nmt_decode_channel(900, value, &channel, &power);
+ if (rc < 0)
+ sprintf(result, "invalid(%" PRIu64 ")", value);
+ else
+ sprintf(result, "channel=%d power=%d", channel, power);
+
+ return result;
+}
+
+static const char *param_tc_no_450(uint64_t value, int __attribute__((unused)) ndigits, enum nmt_direction __attribute__((unused)) direction)
{
static char result[32];
int rc, channel, power;
- rc = nmt_decode_channel(value, &channel, &power);
+ rc = nmt_decode_tc(450, value, &channel, &power);
if (rc < 0)
sprintf(result, "invalid(%" PRIu64 ")", value);
else
@@ -302,6 +411,20 @@ static const char *param_channel_no(uint64_t value, int __attribute__((unused))
return result;
}
+static const char *param_tc_no_900(uint64_t value, int __attribute__((unused)) ndigits, enum nmt_direction __attribute__((unused)) direction)
+{
+ static char result[32];
+ int rc, channel;
+
+ rc = nmt_decode_tc(900, value, &channel, NULL);
+ if (rc < 0)
+ sprintf(result, "invalid(%" PRIu64 ")", value);
+ else
+ sprintf(result, "channel=%d", channel);
+
+ return result;
+}
+
static const char *param_country(uint64_t value, int __attribute__((unused)) ndigits, enum nmt_direction __attribute__((unused)) direction)
{
static char result[32];
@@ -310,7 +433,7 @@ static const char *param_country(uint64_t value, int __attribute__((unused)) ndi
case 0:
return "no additional info";
case 1:
- return "Netherlands / Luxemburg / Malaysia";
+ return "Netherlands / Luxemburg / Malaysia / Switzerland";
case 2:
return "Belgium";
case 4:
@@ -347,7 +470,7 @@ static const char *param_number(uint64_t value, int ndigits, enum nmt_direction
return result;
}
-static const char *param_ta(uint64_t value, int ndigits, enum nmt_direction __attribute__((unused)) direction)
+static const char *param_ta_450(uint64_t value, int ndigits, enum nmt_direction __attribute__((unused)) direction)
{
static char result[32];
@@ -357,6 +480,18 @@ static const char *param_ta(uint64_t value, int ndigits, enum nmt_direction __at
return result;
}
+static const char *param_ta_900(uint64_t value, int __attribute__((unused)) ndigits, enum nmt_direction __attribute__((unused)) direction)
+{
+ static char result[32];
+
+ if ((value & 0x80))
+ sprintf(result, "%" PRIu64 " (Channel No. + 1000)", value & 0x7f);
+ else
+ sprintf(result, "%" PRIu64, value);
+
+ return result;
+}
+
static const char *param_line_signal(uint64_t value, int __attribute__((unused)) ndigits, enum nmt_direction direction)
{
char *desc = "Spare";
@@ -477,34 +612,38 @@ static const char *param_password(uint64_t value, int ndigits, enum nmt_directio
}
static struct nmt_parameter {
+ int system;
char digit;
const char *description;
const char *(*decoder)(uint64_t value, int ndigits, enum nmt_direction direction);
} nmt_parameter[] = {
- { 'N', "Channel No.", param_channel_no },
- { 'n', "TC No.", param_channel_no },
- { 'Y', "Traffic area", param_ta },
- { 'Z', "Mobile subscriber country", param_country },
- { 'X', "Mobile subscriber No.", param_number },
- { 'Q', "Tariff class", param_integer },
- { 'L', "Line signal", param_line_signal },
- { 'S', "Digit signals", param_digit },
- { 'J', "Idle information", param_hex },
- { 'A', "Channel activation", param_hex },
- { 'V', "Management order", param_hex },
- { 'r', "Measurement results", param_hex },
- { 'P', "Prefix", param_integer },
- { 'f', "Supervisory signal", param_supervisory },
- { 'K', "Mobile subscriber password", param_password },
- { 'T', "Area info", param_hex },
- { 'H', "Additional info", param_hex },
- { 'C', "Random challenge", param_hex },
- { 'R', "Signed response", param_hex },
- { 'l', "Limit strength evaluation", param_hex },
- { 'c', "c", param_hex },
- { 'M', "Sequence Number", param_integer },
- { 'W', "Checksum", param_hex },
- { 0, NULL, NULL }
+ { 450, 'N', "Channel No.", param_channel_no_450 },
+ { 900, 'N', "Channel No.", param_channel_no_900 },
+ { 450, 'n', "TC No.", param_tc_no_450 },
+ { 900, 'n', "TC No.", param_tc_no_900 },
+ { 450, 'Y', "Traffic area", param_ta_450 },
+ { 900, 'Y', "Traffic area", param_ta_900 },
+ { 0, 'Z', "Mobile subscriber country", param_country },
+ { 0, 'X', "Mobile subscriber No.", param_number },
+ { 0, 'Q', "Tariff class", param_integer },
+ { 0, 'L', "Line signal", param_line_signal },
+ { 0, 'S', "Digit signals", param_digit },
+ { 0, 'J', "Idle information", param_hex },
+ { 0, 'A', "Channel activation", param_hex },
+ { 0, 'V', "Management order", param_hex },
+ { 0, 'r', "Measurement results", param_hex },
+ { 0, 'P', "Prefix", param_integer },
+ { 0, 'f', "Supervisory signal", param_supervisory },
+ { 0, 'K', "Mobile subscriber password", param_password },
+ { 0, 'T', "Area info", param_hex },
+ { 0, 'H', "Additional info", param_hex },
+ { 0, 'C', "Random challenge", param_hex },
+ { 0, 'R', "Signed response", param_hex },
+ { 0, 'l', "Limit strength evaluation", param_hex },
+ { 0, 'c', "c", param_hex },
+ { 0, 'M', "Sequence Number", param_integer },
+ { 0, 'W', "Checksum", param_hex },
+ { 0, 0, NULL, NULL }
};
/* Depending on P-value, direction and additional info, frame index (used for
@@ -582,7 +721,16 @@ enum nmt_mt decode_frame_mt(const uint8_t *digits, enum nmt_direction direction,
return NMT_MESSAGE_21b;
return NMT_MESSAGE_4;
case 4:
- return NMT_MESSAGE_1b;
+ switch((digits[13] << 8) + (digits[14] << 4) + digits[15]) {
+ case 0x3f3:
+ case 0x3f4:
+ case 0x3f5:
+ case 0x3f6:
+ case 0x000:
+ return NMT_MESSAGE_2e;
+ default:
+ return NMT_MESSAGE_1b;
+ }
case 5:
if (digits[6] == 15)
return NMT_MESSAGE_21c;
@@ -601,7 +749,16 @@ enum nmt_mt decode_frame_mt(const uint8_t *digits, enum nmt_direction direction,
return NMT_MESSAGE_5b;
return NMT_MESSAGE_5a;
case 7:
- break;
+ switch((digits[13] << 8) + (digits[14] << 4) + digits[15]) {
+ case 0x3f3:
+ case 0x3f4:
+ case 0x3f5:
+ case 0x3f6:
+ case 0x000:
+ return NMT_MESSAGE_4b;
+ default:
+ return NMT_MESSAGE_3d;
+ }
case 8:
return NMT_MESSAGE_7;
case 9:
@@ -609,7 +766,7 @@ enum nmt_mt decode_frame_mt(const uint8_t *digits, enum nmt_direction direction,
case 10:
return NMT_MESSAGE_30;
case 11:
- break;
+ return NMT_MESSAGE_1a_a;
case 12:
/* no subscriber */
if (digits[6] == 0)
@@ -637,7 +794,7 @@ enum nmt_mt decode_frame_mt(const uint8_t *digits, enum nmt_direction direction,
return NMT_MESSAGE_2b;
}
case 13:
- break;
+ return NMT_MESSAGE_1a_b;
case 14:
if (digits[13] != 15)
break;
@@ -697,7 +854,7 @@ int init_frame(void)
}
/* decode 16 digits frame */
-static void disassemble_frame(frame_t *frame, const uint8_t *digits, enum nmt_direction direction, int callack)
+static void disassemble_frame(int nmt_system, frame_t *frame, const uint8_t *digits, enum nmt_direction direction, int callack)
{
enum nmt_mt mt;
int i, j, ndigits;
@@ -804,6 +961,8 @@ static void disassemble_frame(frame_t *frame, const uint8_t *digits, enum nmt_di
}
if (debuglevel <= DEBUG_DEBUG) {
for (j = 0; nmt_parameter[j].digit; j++) {
+ if (nmt_parameter[j].system != 0 && nmt_parameter[j].system != nmt_system)
+ continue;
if (nmt_parameter[j].digit == digit) {
PDEBUG(DFRAME, DEBUG_DEBUG, " %c: %s\n", digit, nmt_parameter[j].decoder(value, ndigits, direction));
}
@@ -823,7 +982,7 @@ static void disassemble_frame(frame_t *frame, const uint8_t *digits, enum nmt_di
}
/* encode 16 digits frame */
-static void assemble_frame(frame_t *frame, uint8_t *digits, int debug)
+static void assemble_frame(int nmt_system, frame_t *frame, uint8_t *digits, int debug)
{
enum nmt_mt mt;
int i, j;
@@ -956,6 +1115,8 @@ static void assemble_frame(frame_t *frame, uint8_t *digits, int debug)
i++;
}
for (j = 0; nmt_parameter[j].digit; j++) {
+ if (nmt_parameter[j].system != 0 && nmt_parameter[j].system != nmt_system)
+ continue;
if (nmt_parameter[j].digit == digit) {
PDEBUG(DFRAME, DEBUG_DEBUG, " %c: %s\n", digit, nmt_parameter[j].decoder(value, ndigits, direction));
}
@@ -973,13 +1134,13 @@ static void assemble_frame(frame_t *frame, uint8_t *digits, int debug)
/* encode frame to bits
* debug can be turned on or off
*/
-const char *encode_frame(frame_t *frame, int debug)
+const char *encode_frame(int nmt_system, frame_t *frame, int debug)
{
uint8_t digits[16], message[9], code[18];
static char bits[166];
int i;
- assemble_frame(frame, digits, debug);
+ assemble_frame(nmt_system, frame, digits, debug);
/* hagelbarger code */
message[8] = 0x00;
@@ -994,7 +1155,7 @@ const char *encode_frame(frame_t *frame, int debug)
}
/* decode bits to frame */
-int decode_frame(frame_t *frame, const char *bits, enum nmt_direction direction, int callack)
+int decode_frame(int nmt_system, frame_t *frame, const char *bits, enum nmt_direction direction, int callack)
{
uint8_t digits[16], message[8], code[19];
int i;
@@ -1009,7 +1170,7 @@ int decode_frame(frame_t *frame, const char *bits, enum nmt_direction direction,
digits[i * 2 + 1] = message[i] & 0x0f;
}
- disassemble_frame(frame, digits, direction, callack);
+ disassemble_frame(nmt_system, frame, digits, direction, callack);
return 0;
}
diff --git a/src/nmt/frame.h b/src/nmt/frame.h
index 3eb7abc..85b2aa1 100644
--- a/src/nmt/frame.h
+++ b/src/nmt/frame.h
@@ -1,16 +1,21 @@
enum nmt_mt {
NMT_MESSAGE_1a = 0,
+ NMT_MESSAGE_1a_a,
+ NMT_MESSAGE_1a_b,
NMT_MESSAGE_1b,
NMT_MESSAGE_2a,
NMT_MESSAGE_2b,
NMT_MESSAGE_2c,
NMT_MESSAGE_2d,
+ NMT_MESSAGE_2e,
NMT_MESSAGE_2f,
NMT_MESSAGE_3a,
NMT_MESSAGE_3b,
NMT_MESSAGE_3c,
+ NMT_MESSAGE_3d,
NMT_MESSAGE_4,
+ NMT_MESSAGE_4b,
NMT_MESSAGE_5a,
NMT_MESSAGE_5b,
NMT_MESSAGE_6,
@@ -19,6 +24,7 @@ enum nmt_mt {
NMT_MESSAGE_10a,
NMT_MESSAGE_10b,
NMT_MESSAGE_10c,
+ NMT_MESSAGE_10d,
NMT_MESSAGE_11a,
NMT_MESSAGE_11b,
NMT_MESSAGE_12,
@@ -77,8 +83,10 @@ typedef struct frame {
int init_frame(void);
-uint64_t nmt_encode_channel(int channel, int power);
-int nmt_decode_channel(uint64_t value, int *channel, int *power);
+uint64_t nmt_encode_channel(int nmt_system, int channel, int power);
+int nmt_decode_channel(int nmt_system, uint64_t value, int *channel, int *power);
+uint64_t nmt_encode_tc(int nmt_system, int channel, int power);
+uint64_t nmt_encode_traffic_area(int nmt_system, int channel, uint8_t traffic_area);
void nmt_value2digits(uint64_t value, char *digits, int num);
uint64_t nmt_digits2value(const char *digits, int num);
char nmt_value2digit(uint64_t value);
@@ -86,6 +94,6 @@ uint16_t nmt_encode_area_no(uint8_t area_no);
const char *nmt_frame_name(enum nmt_mt mt);
-const char *encode_frame(frame_t *frame, int debug);
-int decode_frame(frame_t *frame, const char *bits, enum nmt_direction direction, int callack);
+const char *encode_frame(int nmt_system, frame_t *frame, int debug);
+int decode_frame(int nmt_system, frame_t *frame, const char *bits, enum nmt_direction direction, int callack);
diff --git a/src/nmt/main.c b/src/nmt/main.c
index cec5266..396f7d0 100644
--- a/src/nmt/main.c
+++ b/src/nmt/main.c
@@ -44,9 +44,10 @@
static int sms_deliver_fd = -1;
/* settings */
+int nmt_system = 450;
int num_chan_type = 0;
enum nmt_chan_type chan_type[MAX_SENDER] = { CHAN_TYPE_CC_TC };
-int ms_power = 1; /* 1..3 */
+int ms_power = 1; /* 0..3 */
char country[16] = "";
char traffic_area[3] = "";
char area_no = 0;
@@ -58,13 +59,19 @@ int send_callerid = 0;
void print_help(const char *arg0)
{
- print_help_common(arg0, "-Y <traffic area> | list [-I 1] [-0 1] ");
+ print_help_common(arg0, "[-N 900] -Y <traffic area> | list [-I 1] [-0 1] ");
/* - - */
+ printf(" -N --nmt-system 450/900\n");
+ printf(" Give NMT type as first paramer. (default = '%d')\n", nmt_system);
+ printf(" Note: This option must be given at first!\n");
printf(" -T --channel-type <channel type> | list\n");
- printf(" Give channel type, use 'list' to get a list. (default = '%s')\n", chan_type_short_name(chan_type[0]));
+ printf(" Give channel type, use 'list' to get a list. (default = '%s')\n", chan_type_short_name(nmt_system, chan_type[0]));
printf(" -P --ms-power <power level>\n");
- printf(" Give power level of the mobile station 1..3. (default = '%d')\n", ms_power);
- printf(" 3 = 15 W / 7 W (handheld), 2 = 1.5 W, 1 = 150 mW\n");
+ printf(" Give power level of the mobile station 0..3. (default = '%d')\n", ms_power);
+ if (nmt_system == 450)
+ printf(" NMT-450: 3 = 15 W / 7 W (handheld), 2 = 1.5 W, 1/0 = 150 mW\n");
+ else
+ printf(" NMT-900: 3/2 = 6 W, 1 = 1.5 W, 0 = 150 mW\n");
printf(" -Y --traffic-area <traffic area> | list\n");
printf(" NOTE: MUST MATCH WITH YOUR ROAMING SETTINGS IN THE PHONE!\n");
printf(" Your phone will not connect, if country code is different!\n");
@@ -97,6 +104,7 @@ static int handle_options(int argc, char **argv)
int skip_args = 0;
static struct option long_options_special[] = {
+ {"nmt-system", 1, 0, 'N'},
{"channel-type", 1, 0, 'T'},
{"ms-power", 1, 0, 'P'},
{"traffic-area", 1, 0, 'Y'},
@@ -108,10 +116,11 @@ static int handle_options(int argc, char **argv)
{0, 0, 0, 0}
};
- set_options_common("T:P:Y:A:C:0:S:I:", long_options_special);
+ set_options_common("N:T:P:Y:A:C:0:S:I:", long_options_special);
while (1) {
int option_index = 0, c, rc;
+ static int first_option = 1;
c = getopt_long(argc, argv, optstring, long_options, &option_index);
@@ -119,12 +128,26 @@ static int handle_options(int argc, char **argv)
break;
switch (c) {
+ case 'N':
+ nmt_system = atoi(optarg);
+ if (nmt_system != 450 && nmt_system != 900) {
+ fprintf(stderr, "Error, NMT system type '%s' unknown. Please use '-N 450' for NMT-450 or '-N 900' for NMT-900.\n", optarg);
+ exit(0);
+ }
+ if (nmt_system == 900)
+ ms_power = 0;
+ if (!first_option) {
+ fprintf(stderr, "Please specify the NMT system (-N) as first command line option!\n");
+ exit(0);
+ }
+ skip_args += 2;
+ break;
case 'T':
if (!strcmp(optarg, "list")) {
- nmt_channel_list();
+ nmt_channel_list(nmt_system);
exit(0);
}
- rc = nmt_channel_by_short_name(optarg);
+ rc = nmt_channel_by_short_name(nmt_system, optarg);
if (rc < 0) {
fprintf(stderr, "Error, channel type '%s' unknown. Please use '-t list' to get a list. I suggest to use the default.\n", optarg);
exit(0);
@@ -136,14 +159,14 @@ static int handle_options(int argc, char **argv)
ms_power = atoi(optarg);
if (ms_power > 3)
ms_power = 3;
- if (ms_power < 1)
- ms_power = 1;
+ if (ms_power < 0)
+ ms_power = 0;
skip_args += 2;
break;
case 'Y':
if (!strcmp(optarg, "list")) {
- nmt_country_list();
+ nmt_country_list(nmt_system);
exit(0);
}
/* digits */
@@ -155,7 +178,7 @@ static int handle_options(int argc, char **argv)
exit(0);
}
*p++ = '\0';
- rc = nmt_country_by_short_name(country);
+ rc = nmt_country_by_short_name(nmt_system, country);
if (rc < 0) {
error_ta:
fprintf(stderr, "Invalid traffic area '%s', use '-Y list' for a list of valid areas\n", optarg);
@@ -163,7 +186,7 @@ error_ta:
}
traffic_area[0] = rc + '0';
if (p[strlen(p) - 1] != '!') {
- rc = nmt_ta_by_short_name(country, atoi(p));
+ rc = nmt_ta_by_short_name(nmt_system, country, atoi(p));
if (rc < 0)
goto error_ta;
}
@@ -203,6 +226,7 @@ error_ta:
default:
opt_switch_common(c, argv[0], &skip_args);
}
+ first_option = 0;
}
free(long_options);
@@ -378,15 +402,15 @@ int main(int argc, char *argv[])
/* create transceiver instance */
for (i = 0; i < num_kanal; i++) {
- rc = nmt_create(country, kanal[i], (loopback) ? CHAN_TYPE_TEST : chan_type[i], audiodev[i], use_sdr, samplerate, rx_gain, do_pre_emphasis, do_de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, ms_power, nmt_digits2value(traffic_area, 2), area_no, compandor, supervisory[i], smsc_number, send_callerid, loopback);
+ rc = nmt_create(nmt_system, country, kanal[i], (loopback) ? CHAN_TYPE_TEST : chan_type[i], audiodev[i], use_sdr, samplerate, rx_gain, do_pre_emphasis, do_de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, ms_power, nmt_digits2value(traffic_area, 2), area_no, compandor, supervisory[i], smsc_number, send_callerid, loopback);
if (rc < 0) {
fprintf(stderr, "Failed to create transceiver instance. Quitting!\n");
goto fail;
}
- printf("base station on channel %d ready, please tune transmitter to %.4f MHz and receiver to %.4f MHz.\n", kanal[i], nmt_channel2freq(country, kanal[i], 0, NULL, NULL, NULL) / 1e6, nmt_channel2freq(country, kanal[i], 1, NULL, NULL, NULL) / 1e6);
+ printf("base station on channel %d ready, please tune transmitter to %.4f MHz and receiver to %.4f MHz.\n", kanal[i], nmt_channel2freq(nmt_system, country, kanal[i], 0, NULL, NULL, NULL) / 1e6, nmt_channel2freq(nmt_system, country, kanal[i], 1, NULL, NULL, NULL) / 1e6);
}
- nmt_check_channels();
+ nmt_check_channels(nmt_system);
main_common(&quit, latency, interval, myhandler, station_id, 7);
diff --git a/src/nmt/nmt.c b/src/nmt/nmt.c
index 809a168..01a7b3c 100644
--- a/src/nmt/nmt.c
+++ b/src/nmt/nmt.c
@@ -119,7 +119,7 @@ void nmt_display_status(void)
display_status_start();
for (sender = sender_head; sender; sender = sender->next) {
nmt = (nmt_t *) sender;
- display_status_channel(nmt->sender.kanal, chan_type_short_name(nmt->sysinfo.chan_type), nmt_state_name(nmt->state));
+ display_status_channel(nmt->sender.kanal, chan_type_short_name(nmt->sysinfo.system, nmt->sysinfo.chan_type), nmt_state_name(nmt->state));
if (nmt->trans)
display_status_subscriber(nmt->trans->subscriber.number, NULL);
}
@@ -136,32 +136,41 @@ static void nmt_new_state(nmt_t *nmt, enum nmt_state new_state)
}
static struct nmt_channels {
+ int system;
enum nmt_chan_type chan_type;
const char *short_name;
const char *long_name;
} nmt_channels[] = {
- { CHAN_TYPE_CC, "CC", "calling channel (incomming calls)" },
- { CHAN_TYPE_TC, "TC", "traffic channel (outgoing calls)" },
- { CHAN_TYPE_CC_TC, "CC/TC","combined calling & traffic channel (both way calls)" },
- { CHAN_TYPE_TEST, "TEST", "test channel" },
- { 0, NULL, NULL }
+ { 0, CHAN_TYPE_CC, "CC", "calling channel (incomming calls)" },
+ { 900, CHAN_TYPE_CCA, "CCA", "calling channel for group A mobiles with odd secret key (incomming calls)" },
+ { 900, CHAN_TYPE_CCB, "CCB", "calling channel for group B mobiles with even secret key (incomming calls)" },
+ { 0, CHAN_TYPE_TC, "TC", "traffic channel (outgoing calls)" },
+ { 900, CHAN_TYPE_AC_TC, "AC/TC","combined access & traffic channel (outgoing calls)" },
+ { 0, CHAN_TYPE_CC_TC, "CC/TC","combined calling & traffic channel (both way calls)" },
+ { 0, CHAN_TYPE_TEST, "TEST", "test channel" },
+ { 0, 0, NULL, NULL }
};
-void nmt_channel_list(void)
+void nmt_channel_list(int nmt_system)
{
int i;
printf("Type\tDescription\n");
printf("------------------------------------------------------------------------\n");
- for (i = 0; nmt_channels[i].long_name; i++)
+ for (i = 0; nmt_channels[i].long_name; i++) {
+ if (nmt_channels[i].system != 0 && nmt_channels[i].system != nmt_system)
+ continue;
printf("%s\t%s\n", nmt_channels[i].short_name, nmt_channels[i].long_name);
+ }
}
-int nmt_channel_by_short_name(const char *short_name)
+int nmt_channel_by_short_name(int nmt_system, const char *short_name)
{
int i;
for (i = 0; nmt_channels[i].short_name; i++) {
+ if (nmt_channels[i].system != 0 && nmt_channels[i].system != nmt_system)
+ continue;
if (!strcasecmp(nmt_channels[i].short_name, short_name))
return nmt_channels[i].chan_type;
}
@@ -169,11 +178,13 @@ int nmt_channel_by_short_name(const char *short_name)
return -1;
}
-const char *chan_type_short_name(enum nmt_chan_type chan_type)
+const char *chan_type_short_name(int nmt_system, enum nmt_chan_type chan_type)
{
int i;
for (i = 0; nmt_channels[i].short_name; i++) {
+ if (nmt_channels[i].system != 0 && nmt_channels[i].system != nmt_system)
+ continue;
if (nmt_channels[i].chan_type == chan_type)
return nmt_channels[i].short_name;
}
@@ -181,11 +192,13 @@ const char *chan_type_short_name(enum nmt_chan_type chan_type)
return "invalid";
}
-const char *chan_type_long_name(enum nmt_chan_type chan_type)
+const char *chan_type_long_name(int nmt_system, enum nmt_chan_type chan_type)
{
int i;
for (i = 0; nmt_channels[i].long_name; i++) {
+ if (nmt_channels[i].system != 0 && nmt_channels[i].system != nmt_system)
+ continue;
if (nmt_channels[i].chan_type == chan_type)
return nmt_channels[i].long_name;
}
@@ -231,7 +244,7 @@ static int dialstring2number(const char *dialstring, char *ms_country, char *ms_
static void nmt_timeout(struct timer *timer);
/* Create transceiver instance and link to a list. */
-int nmt_create(const char *country, int channel, enum nmt_chan_type chan_type, const char *audiodev, int use_sdr, int samplerate, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compandor, int supervisory, const char *smsc_number, int send_callerid, int loopback)
+int nmt_create(int nmt_system, const char *country, int channel, enum nmt_chan_type chan_type, const char *audiodev, int use_sdr, int samplerate, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compandor, int supervisory, const char *smsc_number, int send_callerid, int loopback)
{
nmt_t *nmt;
int rc;
@@ -240,7 +253,7 @@ int nmt_create(const char *country, int channel, enum nmt_chan_type chan_type, c
int tested;
/* check channel matching and set deviation factor */
- if (nmt_channel2freq(country, channel, 0, &deviation_factor, &scandinavia, &tested) == 0.0) {
+ if (nmt_channel2freq(nmt_system, country, channel, 0, &deviation_factor, &scandinavia, &tested) == 0.0) {
PDEBUG(DNMT, DEBUG_NOTICE, "Channel number %d invalid, use '-Y list' to get a list of available channels.\n", channel);
return -EINVAL;
}
@@ -270,13 +283,14 @@ int nmt_create(const char *country, int channel, enum nmt_chan_type chan_type, c
PDEBUG(DNMT, DEBUG_DEBUG, "Creating 'NMT' instance for channel = %d (sample rate %d).\n", channel, samplerate);
/* init general part of transceiver */
- rc = sender_create(&nmt->sender, channel, nmt_channel2freq(country, channel, 0, NULL, NULL, NULL), nmt_channel2freq(country, channel, 1, NULL, NULL, NULL), audiodev, use_sdr, samplerate, rx_gain, pre_emphasis, de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, 0, PAGING_SIGNAL_NONE);
+ rc = sender_create(&nmt->sender, channel, nmt_channel2freq(nmt_system, country, channel, 0, NULL, NULL, NULL), nmt_channel2freq(nmt_system, country, channel, 1, NULL, NULL, NULL), audiodev, use_sdr, samplerate, rx_gain, pre_emphasis, de_emphasis, write_rx_wave, write_tx_wave, read_rx_wave, read_tx_wave, loopback, 0, PAGING_SIGNAL_NONE);
if (rc < 0) {
PDEBUG(DNMT, DEBUG_ERROR, "Failed to init transceiver process!\n");
goto error;
}
timer_init(&nmt->timer, nmt_timeout, nmt);
+ nmt->sysinfo.system = nmt_system;
nmt->sysinfo.chan_type = chan_type;
nmt->sysinfo.ms_power = ms_power;
nmt->sysinfo.traffic_area = traffic_area;
@@ -310,9 +324,9 @@ int nmt_create(const char *country, int channel, enum nmt_chan_type chan_type, c
/* go into idle state */
nmt_go_idle(nmt);
- PDEBUG(DNMT, DEBUG_NOTICE, "Created channel #%d of type '%s' = %s\n", channel, chan_type_short_name(chan_type), chan_type_long_name(chan_type));
- if (nmt_long_name_by_short_name(country))
- PDEBUG(DNMT, DEBUG_NOTICE, " -> Using country '%s'\n", nmt_long_name_by_short_name(country));
+ PDEBUG(DNMT, DEBUG_NOTICE, "Created channel #%d of type '%s' = %s\n", channel, chan_type_short_name(nmt_system, chan_type), chan_type_long_name(nmt_system, chan_type));
+ if (nmt_long_name_by_short_name(nmt_system, country))
+ PDEBUG(DNMT, DEBUG_NOTICE, " -> Using country '%s'\n", nmt_long_name_by_short_name(nmt_system, country));
PDEBUG(DNMT, DEBUG_NOTICE, " -> Using traffic area %d,%d and area no %d\n", traffic_area >> 4, traffic_area & 0xf, area_no);
if (nmt->supervisory)
PDEBUG(DNMT, DEBUG_NOTICE, " -> Using supervisory signal %d\n", supervisory);
@@ -327,32 +341,64 @@ error:
return rc;
}
-void nmt_check_channels(void)
+void nmt_check_channels(int __attribute__((unused)) nmt_system)
{
sender_t *sender;
nmt_t *nmt;
- int cc = 0, tc = 0;
+ int cca = 0, ccb = 0, tc = 0;
+ int note = 0;
for (sender = sender_head; sender; sender = sender->next) {
nmt = (nmt_t *) sender;
- if (nmt->sysinfo.chan_type == CHAN_TYPE_CC)
- cc = 1;
+ if (nmt->sysinfo.chan_type == CHAN_TYPE_CC) {
+ cca = 1;
+ ccb = 1;
+ }
+ if (nmt->sysinfo.chan_type == CHAN_TYPE_CCA)
+ cca = 1;
+ if (nmt->sysinfo.chan_type == CHAN_TYPE_CCB)
+ ccb = 1;
if (nmt->sysinfo.chan_type == CHAN_TYPE_TC)
tc = 1;
+ if (nmt->sysinfo.chan_type == CHAN_TYPE_AC_TC)
+ tc = 1;
if (nmt->sysinfo.chan_type == CHAN_TYPE_CC_TC) {
- cc = 1;
+ cca = 1;
+ ccb = 1;
tc = 1;
}
}
- if (cc && !tc) {
+ if ((cca || ccb) && !tc) {
+ if (note)
+ PDEBUG(DNMT, DEBUG_NOTICE, "\n");
PDEBUG(DNMT, DEBUG_NOTICE, "*** Selected channel(s) can be used for calling only.\n");
PDEBUG(DNMT, DEBUG_NOTICE, "*** No call from the mobile phone is possible on this channel.\n");
PDEBUG(DNMT, DEBUG_NOTICE, "*** Use combined 'CC/TC' instead!\n");
+ note = 1;
}
- if (tc && !cc) {
+ if (tc && !(cca || ccb)) {
+ if (note)
+ PDEBUG(DNMT, DEBUG_NOTICE, "\n");
PDEBUG(DNMT, DEBUG_NOTICE, "*** Selected channel(s) can be used for traffic only.\n");
PDEBUG(DNMT, DEBUG_NOTICE, "*** No call to the mobile phone is possible on this channel.\n");
PDEBUG(DNMT, DEBUG_NOTICE, "*** Use combined 'CC/TC' instead!\n");
+ note = 1;
+ }
+ if (cca && !ccb) {
+ if (note)
+ PDEBUG(DNMT, DEBUG_NOTICE, "\n");
+ PDEBUG(DNMT, DEBUG_NOTICE, "*** Selected channel(s) can be used for calling of MS type A only.\n");
+ PDEBUG(DNMT, DEBUG_NOTICE, "*** No call from the MS type B phone is possible on this channel.\n");
+ PDEBUG(DNMT, DEBUG_NOTICE, "*** Use combined 'CC' or 'CC/TC' instead!\n");
+ note = 1;
+ }
+ if (!cca && ccb) {
+ if (note)
+ PDEBUG(DNMT, DEBUG_NOTICE, "\n");
+ PDEBUG(DNMT, DEBUG_NOTICE, "*** Selected channel(s) can be used for calling of MS type B only.\n");
+ PDEBUG(DNMT, DEBUG_NOTICE, "*** No call from the MS type A phone is possible on this channel.\n");
+ PDEBUG(DNMT, DEBUG_NOTICE, "*** Use combined 'CC' or 'CC/TC' instead!\n");
+ note = 1;
}
}
@@ -378,7 +424,7 @@ void nmt_go_idle(nmt_t *nmt)
dms_reset(nmt);
sms_reset(nmt);
- PDEBUG_CHAN(DNMT, DEBUG_INFO, "Entering IDLE state, sending idle frames on %s.\n", chan_type_long_name(nmt->sysinfo.chan_type));
+ PDEBUG_CHAN(DNMT, DEBUG_INFO, "Entering IDLE state, sending idle frames on %s.\n", chan_type_long_name(nmt->sysinfo.system, nmt->sysinfo.chan_type));
nmt->trans = NULL; /* remove transaction before state change, so status is shown correctly */
nmt_new_state(nmt, STATE_IDLE);
nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
@@ -446,8 +492,8 @@ static int match_channel(nmt_t *nmt, frame_t *frame)
int channel, power;
/* check channel match */
- nmt_decode_channel(frame->channel_no, &channel, &power);
- if (channel != nmt->sender.kanal) {
+ nmt_decode_channel(nmt->sysinfo.system, frame->channel_no, &channel, &power);
+ if ((channel & 0x3ff) != (nmt->sender.kanal & 0x3ff)) {
PDEBUG_CHAN(DNMT, DEBUG_NOTICE, "Frame for different channel %d received, ignoring.\n", channel);
return 0;
}
@@ -505,8 +551,8 @@ static void tx_ident(nmt_t *nmt, frame_t *frame)
transaction_t *trans = nmt->trans;
frame->mt = NMT_MESSAGE_3b;
- frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
- frame->traffic_area = nmt->sysinfo.traffic_area;
+ frame->channel_no = nmt_encode_channel(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.ms_power);
+ frame->traffic_area = nmt_encode_traffic_area(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.traffic_area);
frame->ms_country = nmt_digits2value(&trans->subscriber.country, 1);
frame->ms_number = nmt_digits2value(trans->subscriber.number, 6);
frame->additional_info = nmt_encode_area_no(nmt->sysinfo.area_no);
@@ -517,8 +563,8 @@ static void set_line_signal(nmt_t *nmt, frame_t *frame, uint8_t signal)
transaction_t *trans = nmt->trans;
frame->mt = NMT_MESSAGE_5a;
- frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
- frame->traffic_area = nmt->sysinfo.traffic_area;
+ frame->channel_no = nmt_encode_channel(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.ms_power);
+ frame->traffic_area = nmt_encode_traffic_area(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.traffic_area);
frame->ms_country = nmt_digits2value(&trans->subscriber.country, 1);
frame->ms_number = nmt_digits2value(trans->subscriber.number, 6);
frame->line_signal = (signal << 8) | (signal << 4) | signal;
@@ -547,8 +593,8 @@ static int encode_a_number(nmt_t *nmt, frame_t *frame, int index, enum number_ty
/* encode */
frame->mt = NMT_MESSAGE_8;
- frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
- frame->traffic_area = nmt->sysinfo.traffic_area;
+ frame->channel_no = nmt_encode_channel(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.ms_power);
+ frame->traffic_area = nmt_encode_traffic_area(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.traffic_area);
frame->seq_number = index;
if (index == 0) {
/* number type */
@@ -614,9 +660,18 @@ static void tx_idle(nmt_t *nmt, frame_t *frame)
case CHAN_TYPE_CC:
frame->mt = NMT_MESSAGE_1a;
break;
+ case CHAN_TYPE_CCA:
+ frame->mt = NMT_MESSAGE_1a_a;
+ break;
+ case CHAN_TYPE_CCB:
+ frame->mt = NMT_MESSAGE_1a_b;
+ break;
case CHAN_TYPE_TC:
frame->mt = NMT_MESSAGE_4;
break;
+ case CHAN_TYPE_AC_TC:
+ frame->mt = NMT_MESSAGE_4b;
+ break;
case CHAN_TYPE_CC_TC:
frame->mt = NMT_MESSAGE_1b;
break;
@@ -624,8 +679,8 @@ static void tx_idle(nmt_t *nmt, frame_t *frame)
frame->mt = NMT_MESSAGE_30;
break;
}
- frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
- frame->traffic_area = nmt->sysinfo.traffic_area;
+ frame->channel_no = nmt_encode_channel(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.ms_power);
+ frame->traffic_area = nmt_encode_traffic_area(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.traffic_area);
frame->additional_info = nmt_encode_area_no(nmt->sysinfo.area_no);
}
@@ -663,6 +718,7 @@ static void rx_idle(nmt_t *nmt, frame_t *frame)
break;
case NMT_MESSAGE_10b: /* seizure from ordinary MS */
case NMT_MESSAGE_12: /* seizure from coinbox MS */
+ case NMT_MESSAGE_10a: /* access signal */
if (!match_channel(nmt, frame))
break;
if (!match_area(nmt, frame))
@@ -973,8 +1029,8 @@ static void tx_mt_paging(nmt_t *nmt, frame_t *frame)
transaction_t *trans = nmt->trans;
frame->mt = NMT_MESSAGE_2a;
- frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
- frame->traffic_area = nmt->sysinfo.traffic_area;
+ frame->channel_no = nmt_encode_channel(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.ms_power);
+ frame->traffic_area = nmt_encode_traffic_area(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.traffic_area);
frame->ms_country = nmt_digits2value(&trans->subscriber.country, 1);
frame->ms_number = nmt_digits2value(trans->subscriber.number, 6);
frame->additional_info = nmt_encode_area_no(nmt->sysinfo.area_no);
@@ -1004,6 +1060,7 @@ static void rx_mt_paging(nmt_t *nmt, frame_t *frame)
switch (frame->mt) {
case NMT_MESSAGE_10a: /* call acknowledgment */
+ case NMT_MESSAGE_10d: /* call ack on alternate type */
if (!match_channel(nmt, frame))
break;
if (!match_subscriber(trans, frame))
@@ -1034,11 +1091,11 @@ static void tx_mt_channel(nmt_t *nmt, frame_t *frame)
transaction_t *trans = nmt->trans;
frame->mt = NMT_MESSAGE_2b;
- frame->channel_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
- frame->traffic_area = nmt->sysinfo.traffic_area;
+ frame->channel_no = nmt_encode_channel(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.ms_power);
+ frame->traffic_area = nmt_encode_traffic_area(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.traffic_area);
frame->ms_country = nmt_digits2value(&trans->subscriber.country, 1);
frame->ms_number = nmt_digits2value(trans->subscriber.number, 6);
- frame->tc_no = nmt_encode_channel(nmt->sender.kanal, nmt->sysinfo.ms_power);
+ frame->tc_no = nmt_encode_tc(nmt->sysinfo.system, nmt->sender.kanal, nmt->sysinfo.ms_power);
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Send channel activation to mobile.\n");
nmt_new_state(nmt, STATE_MT_IDENT);
}
@@ -1477,7 +1534,7 @@ void nmt_receive_frame(nmt_t *nmt, const char *bits, double quality, double leve
PDEBUG_CHAN(DDSP, DEBUG_INFO, "RX Level: %.0f%% Quality=%.0f\n", level * 100.0, quality * 100.0);
- rc = decode_frame(&frame, bits, (nmt->sender.loopback) ? MTX_TO_XX : XX_TO_MTX, (nmt->state == STATE_MT_PAGING));
+ rc = decode_frame(nmt->sysinfo.system, &frame, bits, (nmt->sender.loopback) ? MTX_TO_XX : XX_TO_MTX, (nmt->state == STATE_MT_PAGING));
if (rc < 0) {
PDEBUG_CHAN(DNMT, (nmt->sender.loopback) ? DEBUG_NOTICE : DEBUG_DEBUG, "Received invalid frame.\n");
return;
@@ -1661,7 +1718,7 @@ const char *nmt_get_frame(nmt_t *nmt)
if (nmt->dsp_mode != DSP_MODE_FRAME)
return NULL;
- bits = encode_frame(&frame, debug);
+ bits = encode_frame(nmt->sysinfo.system, &frame, debug);
PDEBUG_CHAN(DNMT, DEBUG_DEBUG, "Sending frame %s.\n", nmt_frame_name(frame.mt));
return bits;
diff --git a/src/nmt/nmt.h b/src/nmt/nmt.h
index 075c3e9..1ed438e 100644
--- a/src/nmt/nmt.h
+++ b/src/nmt/nmt.h
@@ -17,7 +17,10 @@ enum dsp_mode {
enum nmt_chan_type {
CHAN_TYPE_CC, /* calling channel */
+ CHAN_TYPE_CCA, /* calling channel type A mobiles */
+ CHAN_TYPE_CCB, /* calling channel type B mobiles */
CHAN_TYPE_TC, /* traffic channel */
+ CHAN_TYPE_AC_TC, /* combined AC + TC */
CHAN_TYPE_CC_TC, /* combined CC + TC */
CHAN_TYPE_TEST, /* test channel */
};
@@ -59,6 +62,7 @@ enum nmt_direction {
};
typedef struct nmt_sysinfo {
+ int system; /* 450 or 900 */
enum nmt_chan_type chan_type; /* channel type */
int ms_power; /* ms power level 3 = full */
uint8_t traffic_area; /* two digits traffic area, encoded as YY */
@@ -137,12 +141,12 @@ typedef struct nmt {
struct timer sms_timer;
} nmt_t;
-void nmt_channel_list(void);
-int nmt_channel_by_short_name(const char *short_name);
-const char *chan_type_short_name(enum nmt_chan_type chan_type);
-const char *chan_type_long_name(enum nmt_chan_type chan_type);
-int nmt_create(const char *country, int channel, enum nmt_chan_type chan_type, const char *audiodev, int use_sdr, int samplerate, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compandor, int supervisory, const char *smsc_number, int send_callerid, int loopback);
-void nmt_check_channels(void);
+void nmt_channel_list(int nmt_system);
+int nmt_channel_by_short_name(int nmt_system, const char *short_name);
+const char *chan_type_short_name(int nmt_system, enum nmt_chan_type chan_type);
+const char *chan_type_long_name(int nmt_system, enum nmt_chan_type chan_type);
+int nmt_create(int nmt_system, const char *country, int channel, enum nmt_chan_type chan_type, const char *audiodev, int use_sdr, int samplerate, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, const char *read_tx_wave, uint8_t ms_power, uint8_t traffic_area, uint8_t area_no, int compandor, int supervisory, const char *smsc_number, int send_callerid, int loopback);
+void nmt_check_channels(int nmt_system);
void nmt_destroy(sender_t *sender);
void nmt_go_idle(nmt_t *nmt);
void nmt_receive_frame(nmt_t *nmt, const char *bits, double quality, double level, double frames_elapsed);