NMT / DMS: User data facility support (required for SMS)
This commit is contained in:
parent
62b3712ce5
commit
64c829909b
|
@ -27,3 +27,4 @@ src/nmt/nmt
|
|||
src/amps/amps
|
||||
src/test/test_compandor
|
||||
src/test/test_emphasis
|
||||
src/test/test_dms
|
||||
|
|
|
@ -50,6 +50,7 @@ struct debug_cat {
|
|||
{ "mncc", "\033[1;32m" },
|
||||
{ "database", "\033[0;33m" },
|
||||
{ "transaction", "\033[0;32m" },
|
||||
{ "dms", "\033[0;33m" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#define DMNCC 10
|
||||
#define DDB 11
|
||||
#define DTRANS 12
|
||||
#define DDMS 13
|
||||
|
||||
#define PDEBUG(cat, level, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, cat, level, fmt, ## arg)
|
||||
void _printdebug(const char *file, const char *function, int line, int cat, int level, const char *fmt, ...);
|
||||
|
|
|
@ -7,6 +7,7 @@ nmt_SOURCES = \
|
|||
nmt.c \
|
||||
dsp.c \
|
||||
frame.c \
|
||||
dms.c \
|
||||
image.c \
|
||||
tones.c \
|
||||
announcement.c \
|
||||
|
|
|
@ -0,0 +1,869 @@
|
|||
/* NMT DMS (data service) processing
|
||||
*
|
||||
* (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 "../common/debug.h"
|
||||
#include "../common/timer.h"
|
||||
#include "nmt.h"
|
||||
#include "dsp.h"
|
||||
|
||||
#define MUTE_DURATION 0.300 /* 200ms, and about 95ms for the frame itself */
|
||||
|
||||
#define DMS_DOTTING "101010101010101"
|
||||
#define DMS_SYNC "00101000111"
|
||||
|
||||
int dms_allow_loopback = 0;
|
||||
|
||||
/*
|
||||
* support
|
||||
*/
|
||||
|
||||
/* calculate CRC from the bits of label and data and 16 zeroes.
|
||||
* the result is the remainder of the polynomial division and
|
||||
* conforms to DMS standard.
|
||||
*/
|
||||
static uint16_t crc16(uint8_t *bits, int len)
|
||||
{
|
||||
uint16_t generator = 0x1021;
|
||||
uint16_t crc = 0; /* init crc register with 0 */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
/* check if MSB is set */
|
||||
if ((crc & 0x8000)) { /* MSB set, shift it out of the register */
|
||||
/* shift in next bit of input stream */
|
||||
crc = (crc << 1) | bits[i];
|
||||
/* Perform the 'division' by XORing the crc register with the generator polynomial */
|
||||
crc = crc ^ generator;
|
||||
} else { /* MSB not set, shift it out and shift in next bit of input stream. Same as above, just no division */
|
||||
crc = (crc << 1) | bits[i];
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/*
|
||||
* frame handling
|
||||
*/
|
||||
|
||||
/* print CT/DT frame in 8-bit or 7-bit mode. */
|
||||
static const char *print_ct_dt(uint8_t s, uint8_t n, uint8_t *data, int eight_bits)
|
||||
{
|
||||
static char text[128], *ct;
|
||||
|
||||
if (s)
|
||||
ct = " ";
|
||||
else switch (data[0]) {
|
||||
case 0:
|
||||
ct = "IDLE";
|
||||
break;
|
||||
case 73:
|
||||
ct = "ID ";
|
||||
break;
|
||||
case 82:
|
||||
ct = "RAND";
|
||||
break;
|
||||
case 84:
|
||||
ct = "CT84";
|
||||
break;
|
||||
default:
|
||||
ct = "????";
|
||||
}
|
||||
|
||||
if (!eight_bits || s == 0)
|
||||
sprintf(text, "%cT(%d) = %s %3d %3d %3d %3d %3d %3d %3d %3d", 'C' + s, n, ct, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
|
||||
else
|
||||
sprintf(text, "%cT(%d) = %s 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", 'C' + s, n, ct,
|
||||
((data[0] << 1) & 0x80) | data[1],
|
||||
((data[0] << 2) & 0x80) | data[2],
|
||||
((data[0] << 3) & 0x80) | data[3],
|
||||
((data[0] << 4) & 0x80) | data[4],
|
||||
((data[0] << 5) & 0x80) | data[5],
|
||||
((data[0] << 6) & 0x80) | data[6],
|
||||
((data[0] << 7) & 0x80) | data[7]);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/* link DMS frame to list of TX frames */
|
||||
void link_dms_frame(nmt_t *nmt, struct dms_frame *frame)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
struct dms_frame **framep;
|
||||
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "link DMS frame\n");
|
||||
|
||||
/* attach to end of list */
|
||||
framep = &dms->state.frame_list;
|
||||
while (*framep)
|
||||
framep = &((*framep)->next);
|
||||
*framep = frame;
|
||||
}
|
||||
|
||||
/* unlink DMS frame from list of TX frames */
|
||||
void unlink_dms_frame(nmt_t *nmt, struct dms_frame *frame)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
struct dms_frame **framep;
|
||||
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "unlink DMS frame\n");
|
||||
|
||||
/* unlink */
|
||||
framep = &dms->state.frame_list;
|
||||
while (*framep && *framep != frame)
|
||||
framep = &((*framep)->next);
|
||||
if (!(*framep)) {
|
||||
PDEBUG(DTRANS, DEBUG_ERROR, "Frame not in list, please fix!!\n");
|
||||
abort();
|
||||
}
|
||||
*framep = frame->next;
|
||||
}
|
||||
|
||||
/* add DMS frame to list of TX frames */
|
||||
static void dms_frame_add(nmt_t *nmt, int s, const uint8_t *data)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
struct dms_frame *dms_frame;
|
||||
|
||||
dms_frame = calloc(1, sizeof(*dms_frame));
|
||||
if (!dms_frame) {
|
||||
PDEBUG(DDMS, DEBUG_ERROR, "No memory!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dms_frame->s = s;
|
||||
dms_frame->n = dms->state.n_count;
|
||||
dms->state.n_count = (dms->state.n_count + 1) & 7;
|
||||
memcpy(dms_frame->data, data, 8);
|
||||
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "add DMS %cT(%d) frame to queue\n", dms_frame->s + 'C', dms_frame->n);
|
||||
|
||||
link_dms_frame(nmt, dms_frame);
|
||||
}
|
||||
|
||||
/* delete DMS frame from list of TX frames */
|
||||
static void dms_frame_delete(nmt_t *nmt, struct dms_frame *dms_frame)
|
||||
{
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "delete DMS frame %cT(%d) from queue\n", dms_frame->s + 'C', dms_frame->n);
|
||||
|
||||
unlink_dms_frame(nmt, dms_frame);
|
||||
|
||||
free(dms_frame);
|
||||
}
|
||||
|
||||
/* add DT frame */
|
||||
static void dms_frame_add_dt(nmt_t *nmt, const uint8_t *data)
|
||||
{
|
||||
dms_frame_add(nmt, 1, data);
|
||||
}
|
||||
|
||||
/* add CT frame */
|
||||
static void dms_frame_add_ct(nmt_t *nmt, const uint8_t *data)
|
||||
{
|
||||
dms_frame_add(nmt, 0, data);
|
||||
}
|
||||
|
||||
/* add ID frame */
|
||||
static void dms_frame_add_id(nmt_t *nmt)
|
||||
{
|
||||
uint8_t frame[8];
|
||||
|
||||
frame[0] = 73; /* ID */
|
||||
frame[1] = 3; // FIXME: add real id
|
||||
frame[2] = 0;
|
||||
frame[3] = 0;
|
||||
frame[4] = 0;
|
||||
frame[5] = 0;
|
||||
frame[6] = 0;
|
||||
frame[7] = 0;
|
||||
dms_frame_add_ct(nmt, frame);
|
||||
}
|
||||
|
||||
/* add RAND frame */
|
||||
static void dms_frame_add_rand(nmt_t *nmt, int eight_bits)
|
||||
{
|
||||
uint32_t rand = random();
|
||||
uint8_t frame[8];
|
||||
|
||||
frame[0] = 82; /* RAND */
|
||||
frame[1] = (rand >> 17) & 0x40;
|
||||
frame[2] = (rand >> 16) & 0x7f;
|
||||
frame[3] = (rand >> 9) & 0x40;
|
||||
frame[4] = (rand >> 8) & 0x7f;
|
||||
frame[5] = (rand >> 1) & 0x40;
|
||||
frame[6] = rand & 0x7f;
|
||||
frame[7] = eight_bits ? '8' : '7';
|
||||
dms_frame_add_ct(nmt, frame);
|
||||
}
|
||||
|
||||
/*
|
||||
* init and exit
|
||||
*/
|
||||
|
||||
/* init instance */
|
||||
int dms_init_sender(nmt_t *nmt)
|
||||
{
|
||||
/* we need some simple random */
|
||||
srandom((unsigned int)(get_time() * 1000));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cleanup transceiver instance. */
|
||||
void dms_cleanup_sender(nmt_t *nmt)
|
||||
{
|
||||
dms_reset(nmt);
|
||||
}
|
||||
|
||||
/*
|
||||
* transmission of frames
|
||||
*/
|
||||
|
||||
/* encode DT frame and schedule for next transmission */
|
||||
static void dms_encode_dt(nmt_t *nmt, uint8_t d, uint8_t s, uint8_t n, uint8_t *_data)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
char frame[127];
|
||||
uint8_t data[12];
|
||||
uint8_t bits[63 + 16];
|
||||
uint16_t crc;
|
||||
int i, j;
|
||||
|
||||
PDEBUG(DDMS, DEBUG_INFO, "Sending DMS frame: %s\n", print_ct_dt(s, n, _data, dms->state.eight_bits));
|
||||
|
||||
/* generate label */
|
||||
data[0] = (d << 6) | (s << 5) | (3 << 3) | n;
|
||||
memcpy(data + 1, _data, 8);
|
||||
for (i = 0; i < 9; i++) {
|
||||
for (j = 0; j < 7; j++)
|
||||
bits[i * 7 + j] = (data[i] >> (6 - j)) & 1;
|
||||
}
|
||||
for (i = 0; i < 16; i++)
|
||||
bits[63 + i] = 0;
|
||||
crc = crc16(bits, 63 + 16);
|
||||
data[9] = crc >> 9;
|
||||
data[10] = crc >> 2;
|
||||
data[11] = crc & 0x3;
|
||||
|
||||
/* create RR frame */
|
||||
// FIXME: no dotting on consecutive frames
|
||||
memcpy(frame, DMS_DOTTING, 15);
|
||||
memcpy(frame + 15, DMS_SYNC, 11);
|
||||
for (i = 0; i < 11; i++) {
|
||||
for (j = 0; j < 7; j++)
|
||||
frame[26 + j + i*9] = ((data[i] >> (6 - j)) & 1) | '0';
|
||||
frame[26 + 7 + i*9] = '1';
|
||||
frame[26 + 8 + i*9] = '1';
|
||||
}
|
||||
frame[125] = ((data[11] >> 1) & 1) | '0';
|
||||
frame[126] = (data[11] & 1) | '0';
|
||||
#if 0
|
||||
for (i = 0; i < 127; i++) {
|
||||
if (i == 15 || i == 26 || (i - 26) % 9 == 6 || (i - 26) % 9 == 8)
|
||||
printf(" ");
|
||||
printf("%c", frame[i]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
/* render wave form */
|
||||
fsk_render_frame(nmt, frame, 127, dms->frame_spl);
|
||||
dms->frame_valid = 1;
|
||||
dms->frame_pos = 0;
|
||||
dms->frame_length = nmt->samples_per_bit * 127;
|
||||
}
|
||||
|
||||
/* encode RR frame and schedule for next transmission */
|
||||
static void dms_encode_rr(nmt_t *nmt, uint8_t d, uint8_t s, uint8_t n)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
uint8_t data;
|
||||
char frame[77], label[9];
|
||||
int parity, i;
|
||||
|
||||
/* generate label */
|
||||
data = (d << 6) | (s << 5) | (1 << 3) | n;
|
||||
parity = '0';
|
||||
for (i = 0; i < 7; i++) {
|
||||
label[i] = ((data >> (6 - i)) & 1) | '0';
|
||||
if (label[i] == '1')
|
||||
parity ^= 1;
|
||||
}
|
||||
label[7] = '1';
|
||||
label[8] = '1';
|
||||
|
||||
/* create RR frame */
|
||||
memcpy(frame, DMS_DOTTING, 15);
|
||||
memcpy(frame + 15, DMS_SYNC, 11);
|
||||
memcpy(frame + 26, label, 9);
|
||||
memcpy(frame + 35, label, 9);
|
||||
frame[44] = parity; frame[45] = parity;
|
||||
memcpy(frame + 46, frame + 15, 31);
|
||||
#if 0
|
||||
for (i = 0; i < 77; i++) {
|
||||
if (i == 15 || i == 26 || i == 33 || i == 35
|
||||
|| i == 42 || i == 44 || i == 46 || i == 57
|
||||
|| i == 64 || i == 66 || i == 73 || i == 75)
|
||||
printf(" ");
|
||||
printf("%c", frame[i]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
/* render wave form */
|
||||
fsk_render_frame(nmt, frame, 77, dms->frame_spl);
|
||||
dms->frame_valid = 1;
|
||||
dms->frame_pos = 0;
|
||||
dms->frame_length = nmt->samples_per_bit * 77;
|
||||
}
|
||||
|
||||
/* check if we have to transmit a frame and render it
|
||||
* also do nothing until a currently transmitted frame is completely
|
||||
* transmitted.
|
||||
*/
|
||||
static void trigger_frame_transmission(nmt_t *nmt)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
struct dms_frame *dms_frame;
|
||||
int i;
|
||||
|
||||
/* ongoing transmission, so we wait */
|
||||
if (dms->frame_valid)
|
||||
return;
|
||||
|
||||
/* check for RR first, because high priority */
|
||||
if (dms->state.send_rr) {
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Found pending RR(%d) frame, sending.\n", dms->state.n_r);
|
||||
dms->state.send_rr = 0;
|
||||
dms_encode_rr(nmt, dms->state.dir ^ 1, 1, dms->state.n_r);
|
||||
return;
|
||||
}
|
||||
|
||||
/* get next frame to send */
|
||||
/* loop 4 times, because only 4 unacked frames may be transmitted */
|
||||
dms_frame = dms->state.frame_list;
|
||||
for (i = 0; i < 4 && dms_frame; i++) {
|
||||
/* stop before DT frame, if RAND was not acked */
|
||||
if (dms_frame->next && dms_frame->next->s == 1 && !dms->state.established)
|
||||
break;
|
||||
if (dms_frame->n == dms->state.n_s)
|
||||
break;
|
||||
dms_frame = dms_frame->next;
|
||||
}
|
||||
|
||||
/* check if outstanding frame */
|
||||
if (!dms_frame) {
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "No pending RR/CT/DT frame found.\n");
|
||||
if (dms->state.tx_pending) {
|
||||
dms->state.tx_pending = 0;
|
||||
dms_all_sent(nmt);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Found pending %cT(%d) frame, sending.\n", dms_frame->s + 'C', dms_frame->n);
|
||||
|
||||
/* sent next send state to next frame in buffer.
|
||||
* if there is no next frame, set it to the first frame (cycle).
|
||||
* also if RAND was not acked, but next frame is DT, send first frame.
|
||||
*/
|
||||
if (!dms_frame->next) {
|
||||
dms->state.n_s = dms->state.frame_list->n;
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, " -> Next sequence number is %d, because this was the last frame in queue.\n", dms->state.n_s);
|
||||
} else if (!dms->state.established && dms_frame->next->s == 1) {
|
||||
dms->state.n_s = dms->state.frame_list->n;
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, " -> Next sequence number is %d, because this was the last frame before DT queue, and RAND has not been acked yet.\n", dms->state.n_s);
|
||||
} else if (i == 3) {
|
||||
dms->state.n_s = dms->state.frame_list->n;
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, " -> Next sequence number is %d, because we reached max number of unacknowledged frames.\n", dms->state.n_s);
|
||||
} else if (!dms->state.established && dms_frame->next->s == 0) {
|
||||
dms->state.n_s = dms_frame->next->n;
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, " -> Next sequence number is %d, because this is the next CT frame in queue.\n", dms->state.n_s);
|
||||
} else {
|
||||
dms->state.n_s = dms_frame->next->n;
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, " -> Next sequence number is %d, because this is the next frame in queue.\n", dms->state.n_s);
|
||||
}
|
||||
|
||||
dms_encode_dt(nmt, dms->state.dir ^ 1, dms_frame->s, dms_frame->n, dms_frame->data);
|
||||
}
|
||||
|
||||
/* send data using FSK */
|
||||
int fsk_dms_frame(nmt_t *nmt, int16_t *samples, int length)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
int16_t *spl;
|
||||
int i;
|
||||
int count, max;
|
||||
|
||||
next_frame:
|
||||
/* check if no frame is currently transmitted */
|
||||
if (dms->frame_length == 0) {
|
||||
dms->frame_valid = 0;
|
||||
trigger_frame_transmission(nmt);
|
||||
if (!dms->frame_valid)
|
||||
return length;
|
||||
}
|
||||
/* send audio from frame */
|
||||
max = dms->frame_length;
|
||||
count = max - dms->frame_pos;
|
||||
//printf("length = %d count=%d\n", length, count);
|
||||
if (count > length)
|
||||
count = length;
|
||||
spl = dms->frame_spl + dms->frame_pos;
|
||||
for (i = 0; i < count; i++) {
|
||||
*samples++ = *spl++;
|
||||
}
|
||||
dms->frame_pos += count;
|
||||
/* check for end of frame and stop */
|
||||
if (dms->frame_pos == max) {
|
||||
dms->frame_length = 0;
|
||||
/* we need more ? */
|
||||
if (length)
|
||||
goto next_frame;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/*
|
||||
* reception of frames
|
||||
*/
|
||||
|
||||
/* decode DT frame from mobile */
|
||||
static void dms_rx_dt(nmt_t *nmt, uint8_t d, uint8_t s, uint8_t n, uint8_t *data)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
int length;
|
||||
|
||||
/* start transfer */
|
||||
if (!dms->state.started) {
|
||||
PDEBUG(DDMS, DEBUG_INFO, "Starting DMS transfer (mobile originated)\n");
|
||||
dms->state.started = 1;
|
||||
dms->state.established = 0;
|
||||
dms->state.dir = d;
|
||||
dms->state.n_r = 0;
|
||||
dms->state.n_s = 0;
|
||||
dms->state.n_a = 0;
|
||||
dms->state.n_count = 0;
|
||||
dms->state.rand_sent = 0;
|
||||
}
|
||||
|
||||
if (dms->state.dir != d && !dms_allow_loopback) {
|
||||
/* drop frames with wrong direction indicator */
|
||||
PDEBUG(DDMS, DEBUG_INFO, "DMS frame ignored, direction indicator missmatch!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dms->state.n_r != n) {
|
||||
/* ignore out of sequence frames */
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "DMS frame number missmatch (due to resending)\n");
|
||||
} else {
|
||||
PDEBUG(DDMS, DEBUG_INFO, "Received valid DMS frame: %s\n", print_ct_dt(s, n, data, dms->state.eight_bits));
|
||||
|
||||
/* cycle sequence */
|
||||
dms->state.n_r = (n + 1) % 8;
|
||||
|
||||
/* CT frames */
|
||||
if (s == 0) {
|
||||
switch (data[0]) {
|
||||
case 73: /* ID */
|
||||
break;
|
||||
case 82: /* RAND */
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "RAND frame has been received, so we can send/receive DT frame\n");
|
||||
/* when we sent RAND, we do not resend it again, this would be wrong */
|
||||
if (!dms->state.rand_sent) {
|
||||
dms_frame_add_rand(nmt, data[7]);
|
||||
dms->state.rand_sent = 1;
|
||||
}
|
||||
dms->state.established = 1;
|
||||
dms->state.eight_bits = (data[7] == '8') ? 1 : 0;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
} else {
|
||||
if (!dms->state.established)
|
||||
PDEBUG(DDMS, DEBUG_NOTICE, "Received DT frame, but RAND frame has not been received yet\n");
|
||||
else {
|
||||
if (!dms->state.eight_bits)
|
||||
length = 8;
|
||||
else {
|
||||
int i;
|
||||
|
||||
for (i = 1; i < 8; i++)
|
||||
data[i] |= ((data[0] << i) & 0x80);
|
||||
length = 7;
|
||||
data++;
|
||||
}
|
||||
/* according to NMT Doc 450-3 10.8 remove trailing zeroes */
|
||||
while (length > 1) {
|
||||
if (data[length - 1] == 0)
|
||||
length--;
|
||||
else
|
||||
break;
|
||||
}
|
||||
dms_receive(nmt, data, length, dms->state.eight_bits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* schedule sending of RR frame */
|
||||
dms->state.send_rr = 1;
|
||||
/* now trigger frame transmission */
|
||||
trigger_frame_transmission(nmt);
|
||||
}
|
||||
|
||||
/* decode RR frame from mobile */
|
||||
static void dms_rx_rr(nmt_t *nmt, uint8_t d, uint8_t s, uint8_t n)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
struct dms_frame *dms_frame, *dms_frame_next;
|
||||
int i, j;
|
||||
|
||||
if (!dms->state.started)
|
||||
return;
|
||||
|
||||
if (dms->state.dir != d && !dms_allow_loopback) {
|
||||
/* drop frames with wrong direction indicator */
|
||||
PDEBUG(DDMS, DEBUG_INFO, "DMS frame ignored, direction indicator missmatch!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* check to which entry in the list of frames this ack belongs to */
|
||||
/* loop 4 times, because only 4 unacked frames may have been transmitted */
|
||||
dms_frame = dms->state.frame_list;
|
||||
for (i = 0; i < 4 && dms_frame; i++) {
|
||||
if (dms_frame->n == ((n - 1) & 7))
|
||||
break;
|
||||
dms_frame = dms_frame->next;
|
||||
}
|
||||
|
||||
/* if we don't find a frame, it must have been already acked, so we igore RR */
|
||||
if (!dms_frame || i == 4) {
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Received already acked DMS frame: RR(%d) (s = %d), ignoring\n", n, s);
|
||||
return;
|
||||
}
|
||||
|
||||
PDEBUG(DDMS, DEBUG_INFO, "Received valid DMS frame: RR(%d) (s = %d)\n", n, s);
|
||||
|
||||
/* flush all acked frames. */
|
||||
dms_frame = dms->state.frame_list;
|
||||
for (j = 0; j <= i; j++) {
|
||||
if (dms_frame->data[0] == 82) { /* RAND */
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "RAND frame has been acknowledged, so we can continue to send DT frame\n");
|
||||
dms->state.established = 1;
|
||||
}
|
||||
/* increment ack counter */
|
||||
dms->state.n_a = (dms_frame->n + 1) & 7;
|
||||
/* raise send counter if required */
|
||||
if (dms->state.n_s == dms_frame->n) {
|
||||
dms->state.n_s = dms->state.n_a;
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Raising next frame to send to #%d\n", dms->state.n_s);
|
||||
}
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Removing acked frame #%d\n", dms_frame->n);
|
||||
dms_frame_next = dms_frame->next;
|
||||
dms_frame_delete(nmt, dms_frame);
|
||||
dms_frame = dms_frame_next;
|
||||
}
|
||||
|
||||
/* now trigger frame transmission */
|
||||
trigger_frame_transmission(nmt);
|
||||
}
|
||||
|
||||
/* decode NR frame from mobile */
|
||||
static void dms_rx_nr(nmt_t *nmt, uint8_t d, uint8_t s, uint8_t n)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
|
||||
if (!dms->state.started)
|
||||
return;
|
||||
|
||||
if (dms->state.dir != d && !dms_allow_loopback) {
|
||||
/* drop frames with wrong direction indicator */
|
||||
PDEBUG(DDMS, DEBUG_INFO, "DMS frame ignored, direction indicator missmatch!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
PDEBUG(DDMS, DEBUG_INFO, "Received valid DMS frame: NR(%d) (s = %d)\n", n, s);
|
||||
|
||||
// FIXME: support NR
|
||||
|
||||
/* now trigger frame transmission */
|
||||
trigger_frame_transmission(nmt);
|
||||
}
|
||||
|
||||
/* Check for DMS SYNC bits, then collect data bits */
|
||||
void fsk_receive_bit_dms(nmt_t *nmt, int bit, double quality, double level)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
// double frames_elapsed;
|
||||
int i;
|
||||
|
||||
// printf("bit=%d quality=%.4f\n", bit, quality);
|
||||
/* we always search for sync, because the sync cannot show up inside the message itself */
|
||||
dms->rx_sync = (dms->rx_sync << 1) | bit;
|
||||
|
||||
/* sync level and quality */
|
||||
dms->rx_sync_level[dms->rx_sync_count & 0xff] = level;
|
||||
dms->rx_sync_quality[dms->rx_sync_count & 0xff] = quality;
|
||||
dms->rx_sync_count++;
|
||||
|
||||
/* check if pattern 00101000111 matches */
|
||||
if ((dms->rx_sync & 0x07ff) == 0x0147) {
|
||||
/* average level and quality */
|
||||
level = quality = 0;
|
||||
for (i = 0; i < 16; i++) {
|
||||
level += dms->rx_sync_level[(dms->rx_sync_count - 1 - i) & 0xff];
|
||||
quality += dms->rx_sync_quality[(dms->rx_sync_count - 1 - i) & 0xff];
|
||||
}
|
||||
level /= 16.0; quality /= 16.0;
|
||||
// printf("DMS sync (level = %.2f, quality = %.2f\n", level, quality);
|
||||
|
||||
/* do not accept garbage */
|
||||
if (quality < 0.65)
|
||||
return;
|
||||
|
||||
PDEBUG(DDSP, DEBUG_DEBUG, "DMS sync RX Level: %.0f%% Quality=%.0f\n", level * 100.0 + 0.5, quality * 100.0 + 0.5);
|
||||
|
||||
/* rest sync register */
|
||||
dms->rx_sync = 0;
|
||||
dms->rx_in_sync = 1;
|
||||
dms->rx_frame_count = 0;
|
||||
dms->rx_bit_count = 0;
|
||||
memset(dms->rx_frame, 0, sizeof(dms->rx_frame));
|
||||
memset(dms->rx_frame_level, 0, sizeof(dms->rx_frame_level));
|
||||
memset(dms->rx_frame_quality, 0, sizeof(dms->rx_frame_quality));
|
||||
|
||||
/* set muting of receive path */
|
||||
nmt->fsk_filter_mute = (int)((double)nmt->sender.samplerate * MUTE_DURATION);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dms->rx_in_sync)
|
||||
return;
|
||||
|
||||
/* read bits */
|
||||
if (++dms->rx_bit_count <= 7)
|
||||
dms->rx_frame[dms->rx_frame_count] = (dms->rx_frame[dms->rx_frame_count] << 1) | bit;
|
||||
dms->rx_frame_level[dms->rx_frame_count] += level;
|
||||
dms->rx_frame_quality[dms->rx_frame_count] += quality;
|
||||
|
||||
/* check label */
|
||||
if (dms->rx_frame_count == 0) {
|
||||
if (dms->rx_bit_count == 9) {
|
||||
dms->rx_bit_count = 0;
|
||||
dms->rx_label.d = (dms->rx_frame[0] >> 6) & 0x1;
|
||||
dms->rx_label.s = (dms->rx_frame[0] >> 5) & 0x1;
|
||||
dms->rx_label.p = (dms->rx_frame[0] >> 3) & 0x3;
|
||||
dms->rx_label.n = dms->rx_frame[0] & 0x7;
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Got DMS label (d = %d, s = %d, p = %d, n = %d)\n", dms->rx_label.d,dms->rx_label.s,dms->rx_label.p,dms->rx_label.n);
|
||||
dms->rx_frame_count++;
|
||||
if (dms->rx_label.p == 0) {
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Spare prefix '00' ignoring!\n");
|
||||
dms->rx_in_sync = 0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (dms->rx_label.p == 3) {
|
||||
/* read DT frame */
|
||||
if (dms->rx_frame_count <= 8) {
|
||||
if (dms->rx_bit_count == 9) {
|
||||
dms->rx_bit_count = 0;
|
||||
uint8_t c = dms->rx_frame[dms->rx_frame_count];
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Got DMS word 0x%02x (%c)\n", c, (c >= 32 && c <= 126) ? c : '.');
|
||||
dms->rx_frame_count++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (dms->rx_frame_count <= 10) {
|
||||
if (dms->rx_bit_count == 9) {
|
||||
dms->rx_bit_count = 0;
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Got DMS CRC 0x%02x\n", dms->rx_frame[dms->rx_frame_count]);
|
||||
dms->rx_frame_count++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (dms->rx_bit_count == 2) {
|
||||
uint16_t crc_got, crc_calc;
|
||||
uint8_t bits[63 + 16];
|
||||
int i, j;
|
||||
dms->rx_bit_count = 0;
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Got DMS CRC 0x%x\n", dms->rx_frame[dms->rx_frame_count]);
|
||||
crc_got = (dms->rx_frame[9] << 9) | (dms->rx_frame[10] << 2) | dms->rx_frame[11];
|
||||
for (i = 0; i < 9; i++) {
|
||||
for (j = 0; j < 7; j++)
|
||||
bits[i * 7 + j] = (dms->rx_frame[i] >> (6 - j)) & 1;
|
||||
}
|
||||
for (i = 0; i < 16; i++)
|
||||
bits[63 + i] = 0;
|
||||
crc_calc = crc16(bits, 63 + 16);
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "DMS CRC = 0x%04x %s\n", crc_got, (crc_calc == crc_got) ? "(OK)" : "(CRC error)");
|
||||
if (crc_calc == crc_got)
|
||||
dms_rx_dt(nmt, dms->rx_label.d, dms->rx_label.s, dms->rx_label.n, dms->rx_frame + 1);
|
||||
dms->rx_in_sync = 0;
|
||||
return;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
/* read RR/NR frame */
|
||||
if (dms->rx_frame_count <= 1) {
|
||||
if (dms->rx_bit_count == 9) {
|
||||
dms->rx_bit_count = 0;
|
||||
if (dms->rx_frame[0] != dms->rx_frame[1]) {
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Repeated DMS label missmatches!\n");
|
||||
dms->rx_in_sync = 0;
|
||||
return;
|
||||
}
|
||||
dms->rx_frame_count++;
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Repeated label matches\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (dms->rx_bit_count == 2) {
|
||||
uint8_t parity_got, parity_calc = 0, bit;
|
||||
int i;
|
||||
dms->rx_bit_count = 0;
|
||||
parity_got = dms->rx_frame[2];
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Got DMS parity 0x%x\n", dms->rx_frame[dms->rx_frame_count]);
|
||||
for (i = 0; i < 7; i++) {
|
||||
bit = (dms->rx_frame[0] >> i) & 1;
|
||||
if (bit)
|
||||
parity_calc ^= 0x3;
|
||||
}
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "DMS parity %s\n", (parity_calc == parity_got) ? "(OK)" : "(parity error)");
|
||||
if (parity_calc == parity_got) {
|
||||
if (dms->rx_label.p == 1)
|
||||
dms_rx_rr(nmt, dms->rx_label.d, dms->rx_label.s, dms->rx_label.n);
|
||||
else
|
||||
dms_rx_nr(nmt, dms->rx_label.d, dms->rx_label.s, dms->rx_label.n);
|
||||
}
|
||||
dms->rx_in_sync = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* calls from upper layer
|
||||
*/
|
||||
|
||||
/* recive data from upper layer to be sent as DT frames
|
||||
* the DT frames are generated */
|
||||
void dms_send(nmt_t *nmt, const uint8_t *data, int length, int eight_bits)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
uint8_t frame[8];
|
||||
int i, copied;
|
||||
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Received message with %d digits of %d bits\n", length, (eight_bits) ? 8 : 7);
|
||||
|
||||
/* active connection */
|
||||
if (dms->state.started) {
|
||||
if (dms->state.eight_bits != eight_bits) {
|
||||
PDEBUG(DDMS, DEBUG_ERROR, "DMS session active, but upper layer sends wrong bit format!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dms->state.started) {
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Transfer not started, so we send ID + RAND first\n");
|
||||
dms->state.started = 1;
|
||||
dms->state.established = 0;
|
||||
dms->state.eight_bits = eight_bits;
|
||||
dms->state.dir = 1; /* we send 0, we expect 1 */
|
||||
dms->state.n_r = 0;
|
||||
dms->state.n_s = 0;
|
||||
dms->state.n_a = 0;
|
||||
dms->state.n_count = 0;
|
||||
dms_frame_add_id(nmt);
|
||||
dms_frame_add_rand(nmt, eight_bits);
|
||||
dms->state.rand_sent = 1;
|
||||
}
|
||||
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Queueing message data as DT frames...\n");
|
||||
while (length) {
|
||||
if (eight_bits) {
|
||||
/* copy what we have */
|
||||
for (i = 1; i < 8 && length; i++) {
|
||||
frame[i] = *data++;
|
||||
length--;
|
||||
}
|
||||
copied = i - 1;
|
||||
/* padd with 0, if required */
|
||||
for (; i < 8; i++)
|
||||
frame[i] = 0;
|
||||
/* move the 8th bits to first character */
|
||||
frame[0] = 0;
|
||||
for (i = 1; i < 8; i++) {
|
||||
frame[0] |= (frame[i] & 0x80) >> i;
|
||||
frame[i] &= 0x7f;
|
||||
}
|
||||
} else {
|
||||
/* copy what we have */
|
||||
for (i = 0; i < 8 && length; i++) {
|
||||
frame[i] = (*data++) & 0x7f;
|
||||
length--;
|
||||
}
|
||||
copied = i;
|
||||
/* padd with 0, if required */
|
||||
for (; i < 8; i++)
|
||||
frame[i] = 0;
|
||||
}
|
||||
/* according to NMT Doc 450-3 10.8 trailing zeros are ignored.
|
||||
* we put back the trailing zeros to the data buffer.
|
||||
* except for a single zero, we keep it, because all digits
|
||||
* 0 means a single zero is transmitted.
|
||||
*/
|
||||
for (i = 0; i < copied - 1; i++) {
|
||||
if (data[-1] == 0) {
|
||||
/* put back last take byte */
|
||||
data--;
|
||||
length++;
|
||||
}
|
||||
}
|
||||
dms_frame_add_dt(nmt, frame);
|
||||
/* indicate that we have pending data */
|
||||
dms->state.tx_pending = 1;
|
||||
}
|
||||
|
||||
/* now trigger frame transmission */
|
||||
trigger_frame_transmission(nmt);
|
||||
}
|
||||
|
||||
/* reset DMS instance */
|
||||
void dms_reset(nmt_t *nmt)
|
||||
{
|
||||
dms_t *dms = &nmt->dms;
|
||||
PDEBUG(DDMS, DEBUG_DEBUG, "Resetting DMS states\n");
|
||||
|
||||
dms->rx_in_sync = 0;
|
||||
memset(&dms->state, 0, sizeof(dms->state));
|
||||
|
||||
dms->frame_valid = 0;
|
||||
|
||||
while (dms->state.frame_list)
|
||||
dms_frame_delete(nmt, dms->state.frame_list);
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
typedef struct nmt nmt_t;
|
||||
|
||||
struct dms_frame {
|
||||
struct dms_frame *next;
|
||||
uint8_t s; /* CT/DT frame */
|
||||
uint8_t n; /* sequence number */
|
||||
uint8_t data[8]; /* data */
|
||||
};
|
||||
|
||||
struct dms_state {
|
||||
int started; /* did we start conversation ? */
|
||||
int rand_sent; /* if we sent rand as an initiator? */
|
||||
int established; /* DT frames after RAND (ack) */
|
||||
int tx_pending; /* uper layer sent data, but not yet sent and acked */
|
||||
uint8_t n_r; /* next expected frame to be received */
|
||||
uint8_t n_s; /* next frame to be sent */
|
||||
uint8_t n_a; /* next frame to be acked */
|
||||
uint8_t n_count; /* counts frames that are stored in list */
|
||||
uint8_t dir; /* direction */
|
||||
int eight_bits; /* what mode are used for DT frames */
|
||||
struct dms_frame *frame_list; /* list of frames to transmit */
|
||||
int send_rr; /* RR must be sent next */
|
||||
};
|
||||
|
||||
typedef struct dms {
|
||||
/* DMS transmission */
|
||||
int frame_valid; /* set, if there is a valid frame in sample buffer */
|
||||
int16_t *frame_spl; /* 127 * fsk_bit_length */
|
||||
int frame_pos; /* current sample position in frame_spl */
|
||||
int frame_length; /* number of samples in frame_spl */
|
||||
uint16_t rx_sync; /* shift register to detect sync */
|
||||
double rx_sync_level[256]; /* level infos */
|
||||
double rx_sync_quality[256]; /* quality infos */
|
||||
int rx_sync_count; /* next bit to receive */
|
||||
int rx_in_sync; /* if we are in sync and receive bits */
|
||||
uint8_t rx_frame[12]; /* receive frame, including label at the start and 3 words crc */
|
||||
double rx_frame_level[12]; /* level info */
|
||||
double rx_frame_quality[12]; /* quality info */
|
||||
int rx_bit_count; /* next bit to receive */
|
||||
int rx_frame_count; /* next character to receive */
|
||||
struct {
|
||||
uint8_t d; /* direction */
|
||||
uint8_t s; /* selection */
|
||||
uint8_t p; /* prefix */
|
||||
uint8_t n; /* number of DT frame */
|
||||
} rx_label; /* used while receiving frame */
|
||||
|
||||
/* DMS protocol states */
|
||||
struct dms_state state;
|
||||
} dms_t;
|
||||
|
||||
int dms_init_sender(nmt_t *nmt);
|
||||
void dms_cleanup_sender(nmt_t *nmt);
|
||||
int fsk_dms_frame(nmt_t *nmt, int16_t *samples, int length);
|
||||
void fsk_receive_bit_dms(nmt_t *nmt, int bit, double quality, double level);
|
||||
void dms_reset(nmt_t *nmt);
|
||||
|
||||
void dms_send(nmt_t *nmt, const uint8_t *data, int length, int eight_bits);
|
||||
void dms_all_sent(nmt_t *nmt);
|
||||
void dms_receive(nmt_t *nmt, const uint8_t *data, int length, int eight_bits);
|
||||
|
|
@ -139,6 +139,14 @@ int dsp_init_sender(nmt_t *nmt)
|
|||
}
|
||||
nmt->frame_spl = spl;
|
||||
|
||||
/* allocate DMS transmit buffer for a complete frame */
|
||||
spl = calloc(127, nmt->samples_per_bit * sizeof(*spl));
|
||||
if (!spl) {
|
||||
PDEBUG(DDSP, DEBUG_ERROR, "No memory!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
nmt->dms.frame_spl = spl;
|
||||
|
||||
/* allocate ring buffer for supervisory signal detection */
|
||||
nmt->super_samples = (int)((double)nmt->sender.samplerate * SUPER_DURATION + 0.5);
|
||||
spl = calloc(1, nmt->super_samples * sizeof(*spl));
|
||||
|
@ -186,6 +194,10 @@ void dsp_cleanup_sender(nmt_t *nmt)
|
|||
free(nmt->frame_spl);
|
||||
nmt->frame_spl = NULL;
|
||||
}
|
||||
if (nmt->dms.frame_spl) {
|
||||
free(nmt->dms.frame_spl);
|
||||
nmt->dms.frame_spl = NULL;
|
||||
}
|
||||
if (nmt->fsk_filter_spl) {
|
||||
free(nmt->fsk_filter_spl);
|
||||
nmt->fsk_filter_spl = NULL;
|
||||
|
@ -264,7 +276,7 @@ static void fsk_receive_bit(nmt_t *nmt, int bit, double quality, double level)
|
|||
/* send telegramm */
|
||||
frames_elapsed = (double)(nmt->rx_sample_count_current - nmt->rx_sample_count_last) / (double)(nmt->samples_per_bit * 166);
|
||||
/* convert level so that received level at TX_PEAK_FSK results in 1.0 (100%) */
|
||||
nmt_receive_frame(nmt, nmt->fsk_filter_frame, quality, level * 32768.0 / TX_PEAK_FSK, frames_elapsed);
|
||||
nmt_receive_frame(nmt, nmt->fsk_filter_frame, quality, level, frames_elapsed);
|
||||
}
|
||||
|
||||
//#define DEBUG_MODULATOR
|
||||
|
@ -337,7 +349,9 @@ static inline void fsk_decode_step(nmt_t *nmt, int pos)
|
|||
printf("|%s|\n", debug_amplitude(quality));
|
||||
#endif
|
||||
/* adjust level, so a peak level becomes 100% */
|
||||
fsk_receive_bit(nmt, bit, quality, level / 0.63662);
|
||||
fsk_receive_bit(nmt, bit, quality, level / 0.63662 * 32768.0 / TX_PEAK_FSK);
|
||||
if (nmt->dms_call)
|
||||
fsk_receive_bit_dms(nmt, bit, quality, level / 0.63662 * 32768.0 / TX_PEAK_FSK);
|
||||
nmt->fsk_filter_sample = 10;
|
||||
}
|
||||
}
|
||||
|
@ -467,12 +481,29 @@ void sender_receive(sender_t *sender, int16_t *samples, int length)
|
|||
nmt->sender.rxbuf_pos = 0;
|
||||
}
|
||||
|
||||
/* render frame */
|
||||
void fsk_render_frame(nmt_t *nmt, const char *frame, int length, int16_t *sample)
|
||||
{
|
||||
int bit, polarity;
|
||||
int i;
|
||||
|
||||
polarity = nmt->fsk_polarity;
|
||||
for (i = 0; i < length; i++) {
|
||||
bit = (frame[i] == '1');
|
||||
memcpy(sample, nmt->fsk_sine[polarity][bit], nmt->samples_per_bit * sizeof(*sample));
|
||||
sample += nmt->samples_per_bit;
|
||||
/* flip polarity when we have 1.5 sine waves */
|
||||
if (bit == 0)
|
||||
polarity = 1 - polarity;
|
||||
}
|
||||
nmt->fsk_polarity = polarity;
|
||||
}
|
||||
|
||||
static int fsk_frame(nmt_t *nmt, int16_t *samples, int length)
|
||||
{
|
||||
int16_t *spl;
|
||||
const char *frame;
|
||||
int16_t *spl;
|
||||
int i;
|
||||
int bit, polarity;
|
||||
int count, max;
|
||||
|
||||
next_frame:
|
||||
|
@ -485,18 +516,8 @@ next_frame:
|
|||
}
|
||||
nmt->frame = 1;
|
||||
nmt->frame_pos = 0;
|
||||
spl = nmt->frame_spl;
|
||||
/* render frame */
|
||||
polarity = nmt->fsk_polarity;
|
||||
for (i = 0; i < 166; i++) {
|
||||
bit = (frame[i] == '1');
|
||||
memcpy(spl, nmt->fsk_sine[polarity][bit], nmt->samples_per_bit * sizeof(*spl));
|
||||
spl += nmt->samples_per_bit;
|
||||
/* flip polarity when we have 1.5 sine waves */
|
||||
if (bit == 0)
|
||||
polarity = 1 - polarity;
|
||||
}
|
||||
nmt->fsk_polarity = polarity;
|
||||
fsk_render_frame(nmt, frame, 166, nmt->frame_spl);
|
||||
}
|
||||
|
||||
/* send audio from frame */
|
||||
|
@ -577,6 +598,11 @@ again:
|
|||
case DSP_MODE_AUDIO:
|
||||
case DSP_MODE_DTMF:
|
||||
jitter_load(&nmt->sender.audio, samples, length);
|
||||
/* send after dejitter, so audio is flushed */
|
||||
if (nmt->dms.frame_valid) {
|
||||
fsk_dms_frame(nmt, samples, length);
|
||||
break;
|
||||
}
|
||||
if (nmt->supervisory)
|
||||
super_encode(nmt, samples, length);
|
||||
break;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
void dsp_init(void);
|
||||
int dsp_init_sender(nmt_t *nmt);
|
||||
void dsp_cleanup_sender(nmt_t *nmt);
|
||||
void fsk_render_frame(nmt_t *nmt, const char *frame, int length, int16_t *sample);
|
||||
void nmt_set_dsp_mode(nmt_t *nmt, enum dsp_mode mode);
|
||||
void super_reset(nmt_t *nmt);
|
||||
|
||||
|
|
|
@ -337,6 +337,13 @@ int nmt_create(int channel, enum nmt_chan_type chan_type, const char *sounddev,
|
|||
goto error;
|
||||
}
|
||||
|
||||
/* init audio processing */
|
||||
rc = dms_init_sender(nmt);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DNMT, DEBUG_ERROR, "Failed to init DMS processing!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
timer_init(&nmt->timer, nmt_timeout, nmt);
|
||||
nmt->sysinfo.chan_type = chan_type;
|
||||
nmt->sysinfo.ms_power = ms_power;
|
||||
|
@ -363,6 +370,7 @@ void nmt_destroy(sender_t *sender)
|
|||
|
||||
PDEBUG(DNMT, DEBUG_DEBUG, "Destroying 'NMT' instance for channel = %d.\n", sender->kanal);
|
||||
dsp_cleanup_sender(nmt);
|
||||
dms_cleanup_sender(nmt);
|
||||
timer_exit(&nmt->timer);
|
||||
sender_destroy(&nmt->sender);
|
||||
free(nmt);
|
||||
|
@ -373,12 +381,20 @@ static void nmt_go_idle(nmt_t *nmt)
|
|||
{
|
||||
timer_stop(&nmt->timer);
|
||||
nmt->page_for_nmt = NULL;
|
||||
nmt->dms_call = 0;
|
||||
|
||||
PDEBUG(DNMT, DEBUG_INFO, "Entering IDLE state, sending idle frames on %s.\n", chan_type_long_name(nmt->sysinfo.chan_type));
|
||||
nmt_new_state(nmt, STATE_IDLE);
|
||||
nmt_set_dsp_mode(nmt, DSP_MODE_FRAME);
|
||||
memset(&nmt->subscriber, 0, sizeof(nmt->subscriber));
|
||||
memset(&nmt->dialing, 0, sizeof(nmt->dialing));
|
||||
|
||||
#if 0
|
||||
/* go active for loopback tests */
|
||||
nmt_new_state(nmt, STATE_ACTIVE);
|
||||
nmt_set_dsp_mode(nmt, DSP_MODE_AUDIO);
|
||||
nmt->dms_call = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* release an ongoing connection, this is used by roaming update and release initiated by MTX */
|
||||
|
@ -744,7 +760,11 @@ static void rx_mo_dialing(nmt_t *nmt, frame_t *frame)
|
|||
break;
|
||||
PDEBUG(DNMT, DEBUG_INFO, "Dialing complete %s->%s, call established.\n", &nmt->subscriber.country, nmt->dialing);
|
||||
/* setup call */
|
||||
{
|
||||
if (!strcmp(nmt->dialing, "767")) {
|
||||
/* SMS */
|
||||
dms_reset(nmt);
|
||||
nmt->dms_call = 1;
|
||||
} else {
|
||||
int callref = ++new_callref;
|
||||
int rc;
|
||||
PDEBUG(DNMT, DEBUG_INFO, "Setup call to network.\n");
|
||||
|
@ -1163,7 +1183,7 @@ void nmt_receive_frame(nmt_t *nmt, const char *bits, double quality, double leve
|
|||
frame_t frame;
|
||||
int rc;
|
||||
|
||||
PDEBUG(DFRAME, DEBUG_INFO, "RX Level: %.0f%% Quality=%.0f\n", level * 100.0 + 0.5, quality * 100.0 + 0.5);
|
||||
PDEBUG(DDSP, DEBUG_INFO, "RX Level: %.0f%% Quality=%.0f\n", level * 100.0 + 0.5, quality * 100.0 + 0.5);
|
||||
|
||||
rc = decode_frame(&frame, bits, (nmt->sender.loopback) ? MTX_TO_XX : XX_TO_MTX, (nmt->state == STATE_MT_PAGING));
|
||||
if (rc < 0) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "../common/sender.h"
|
||||
#include "../common/compandor.h"
|
||||
#include "../common/dtmf.h"
|
||||
#include "dms.h"
|
||||
|
||||
enum dsp_mode {
|
||||
DSP_MODE_DIALTONE, /* stream dial tone to mobile phone */
|
||||
|
@ -84,6 +85,7 @@ typedef struct nmt {
|
|||
int tx_frame_count; /* transmit frame counter */
|
||||
char dialing[33]; /* dialed digits */
|
||||
int page_try; /* number of paging try */
|
||||
int mft_num; /* counter for digit for MFT */
|
||||
|
||||
/* special state for paging on different CC */
|
||||
struct nmt *page_for_nmt; /* only page and assign channel for nmt instance as set */
|
||||
|
@ -128,7 +130,10 @@ typedef struct nmt {
|
|||
uint64_t rx_sample_count_last; /* sample counter of last frame */
|
||||
int super_detected; /* current detection state flag */
|
||||
int super_detect_count; /* current number of consecutive detections/losses */
|
||||
int mft_num; /* counter for digit */
|
||||
|
||||
/* DMS states */
|
||||
int dms_call; /* indicates that this call is a DMS call */
|
||||
dms_t dms; /* DMS states */
|
||||
} nmt_t;
|
||||
|
||||
void nmt_channel_list(void);
|
||||
|
|
|
@ -2,7 +2,8 @@ AM_CPPFLAGS = -Wall -g $(all_includes)
|
|||
|
||||
noinst_PROGRAMS = \
|
||||
test_compandor \
|
||||
test_emphasis
|
||||
test_emphasis \
|
||||
test_dms
|
||||
|
||||
test_compandor_SOURCES = test_compandor.c
|
||||
|
||||
|
@ -18,3 +19,12 @@ test_emphasis_LDADD = \
|
|||
$(top_builddir)/src/common/libcommon.a \
|
||||
-lm
|
||||
|
||||
test_dms_SOURCES = \
|
||||
$(top_builddir)/src/nmt/dms.c \
|
||||
test_dms.c
|
||||
|
||||
test_dms_LDADD = \
|
||||
$(COMMON_LA) \
|
||||
$(top_builddir)/src/common/libcommon.a \
|
||||
-lm
|
||||
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "../common/debug.h"
|
||||
#include "../common/timer.h"
|
||||
#include "../nmt/nmt.h"
|
||||
|
||||
extern int dms_allow_loopback;
|
||||
|
||||
static void assert(int condition, char *why)
|
||||
{
|
||||
printf("%s = %s\n", why, (condition) ? "TRUE" : "FALSE");
|
||||
|
||||
if (!condition) {
|
||||
printf("\n******************** FAILED ********************\n\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void ok(void)
|
||||
{
|
||||
printf("\n OK ;->\n\n");
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
static const char testsequence[] = "This is a test for DMS protocol layer. It will test the handing of transfer window. Also it will test what happens, if frames get dropped.";
|
||||
static const char *check_sequence;
|
||||
int check_length;
|
||||
|
||||
static const uint8_t test_null[][8] = {
|
||||
{ 0x01, 0x02, 0x02, 0x04, 0x05, 0x06, 0x07, 7 },
|
||||
{ 0x01, 0x02, 0x02, 0x04, 0x05, 0x06, 0x00, 6 },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 5 },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1 },
|
||||
};
|
||||
|
||||
static char current_bits[1024], ack_bits[77];
|
||||
int current_bit_count;
|
||||
|
||||
void dms_receive(nmt_t *nmt, const uint8_t *data, int length, int eight_bits)
|
||||
{
|
||||
printf("(getting %d digits from DMS layer)\n", length);
|
||||
|
||||
assert(!memcmp((const char *)data, check_sequence, length), "Expecting received data to macht");
|
||||
|
||||
check_sequence += length;
|
||||
check_length = length;
|
||||
}
|
||||
|
||||
void dms_all_sent(nmt_t *nmt)
|
||||
{
|
||||
}
|
||||
|
||||
/* receive bits from DMS */
|
||||
void fsk_render_frame(nmt_t *nmt, const char *frame, int length, int16_t *sample)
|
||||
{
|
||||
printf("(getting %d bits from DMS layer)\n", length);
|
||||
|
||||
memcpy(current_bits, frame, length);
|
||||
current_bit_count = length;
|
||||
}
|
||||
|
||||
nmt_t *alloc_nmt(void)
|
||||
{
|
||||
nmt_t *nmt;
|
||||
|
||||
nmt = calloc(sizeof(*nmt), 1);
|
||||
nmt->dms.frame_spl = calloc(1000000, 1);
|
||||
nmt->samples_per_bit = 40;
|
||||
|
||||
dms_reset(nmt);
|
||||
|
||||
return nmt;
|
||||
}
|
||||
|
||||
void free_nmt(nmt_t *nmt)
|
||||
{
|
||||
free(nmt->dms.frame_spl);
|
||||
free(nmt);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
nmt_t *nmt;
|
||||
dms_t *dms;
|
||||
int i, j;
|
||||
int16_t sample = 0;
|
||||
|
||||
debuglevel = DEBUG_DEBUG;
|
||||
dms_allow_loopback = 1;
|
||||
|
||||
nmt = alloc_nmt();
|
||||
dms = &nmt->dms;
|
||||
|
||||
/* test if frame cycles until we send RAND */
|
||||
|
||||
check_sequence = testsequence;
|
||||
dms_send(nmt, (uint8_t *)testsequence, strlen(testsequence) + 1, 1);
|
||||
assert(dms->frame_valid && current_bit_count == 127, "Expecting frame in queue with 127 bits");
|
||||
assert(dms->state.n_s == 1, "Expecting next frame to have sequence number 1");
|
||||
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
|
||||
assert(dms->frame_valid && current_bit_count == 127, "Expecting frame in queue with 127 bits");
|
||||
assert(dms->state.n_s == 0, "Expecting next frame to have sequence number 0 (cycles due to unacked RAND)");
|
||||
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
|
||||
assert(dms->frame_valid && current_bit_count == 127, "Expecting frame in queue with 127 bits");
|
||||
assert(dms->state.n_s == 1, "Expecting next frame to have sequence number 1");
|
||||
|
||||
/* send back ID */
|
||||
|
||||
printf("Sending back ID\n");
|
||||
for (i = 0; i < current_bit_count; i++)
|
||||
fsk_receive_bit_dms(nmt, current_bits[i] & 1, 1.0, 1.0);
|
||||
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
|
||||
assert(dms->frame_valid && current_bit_count == 77, "Expecting frame in queue with 77 bits");
|
||||
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
|
||||
assert(dms->frame_valid && current_bit_count == 127, "Expecting frame in queue with 127 bits");
|
||||
assert(dms->state.n_s == 0, "Expecting next frame to have sequence number 0");
|
||||
|
||||
/* send back RAND */
|
||||
printf("Sending back RAND\n");
|
||||
for (i = 0; i < current_bit_count; i++)
|
||||
fsk_receive_bit_dms(nmt, current_bits[i] & 1, 1.0, 1.0);
|
||||
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
|
||||
assert(dms->frame_valid && current_bit_count == 77, "Expecting frame in queue with 77 bits");
|
||||
memcpy(ack_bits, current_bits, 77);
|
||||
|
||||
/* check if DT frame will be sent now */
|
||||
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
|
||||
assert(dms->frame_valid && current_bit_count == 127, "Expecting frame in queue with 127 bits");
|
||||
assert(dms->state.n_s == 1, "Expecting next frame to have sequence number 1");
|
||||
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
|
||||
assert(dms->frame_valid && current_bit_count == 127, "Expecting frame in queue with 127 bits");
|
||||
assert(dms->state.n_s == 2, "Expecting next frame to have sequence number 2");
|
||||
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
|
||||
assert(dms->frame_valid && current_bit_count == 127, "Expecting frame in queue with 127 bits");
|
||||
assert(dms->state.n_s == 3, "Expecting next frame to have sequence number 3");
|
||||
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
|
||||
assert(dms->frame_valid && current_bit_count == 127, "Expecting frame in queue with 127 bits");
|
||||
assert(dms->state.n_s == 0, "Expecting next frame to have sequence number 0");
|
||||
|
||||
/* send back ack bitss */
|
||||
printf("Sending back RR(2)\n");
|
||||
memcpy(current_bits, ack_bits, 77);
|
||||
current_bit_count = 77;
|
||||
for (i = 0; i < current_bit_count; i++)
|
||||
fsk_receive_bit_dms(nmt, current_bits[i] & 1, 1.0, 1.0);
|
||||
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
|
||||
assert(dms->frame_valid && current_bit_count == 127, "Expecting frame in queue with 127 bits");
|
||||
assert(dms->state.n_s == 3, "Expecting next frame to have sequence number 0");
|
||||
|
||||
ok();
|
||||
|
||||
/* loopback frames */
|
||||
printf("pipe through all data\n");
|
||||
while (check_sequence[0]) {
|
||||
printf("Sending back last received frame\n");
|
||||
for (i = 0; i < current_bit_count; i++)
|
||||
fsk_receive_bit_dms(nmt, current_bits[i] & 1, 1.0, 1.0);
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
}
|
||||
|
||||
ok();
|
||||
|
||||
debuglevel = DEBUG_INFO;
|
||||
|
||||
/* test again with pseudo random packet dropps */
|
||||
srandom(0);
|
||||
free_nmt(nmt);
|
||||
nmt = alloc_nmt();
|
||||
dms = &nmt->dms;
|
||||
|
||||
check_sequence = testsequence;
|
||||
dms_send(nmt, (uint8_t *)testsequence, strlen(testsequence) + 1, 1);
|
||||
|
||||
/* loopback frames */
|
||||
printf("pipe through all data\n");
|
||||
while (check_sequence[0]) {
|
||||
if ((random() & 1)) {
|
||||
printf("Sending back last received frame\n");
|
||||
for (i = 0; i < current_bit_count; i++)
|
||||
fsk_receive_bit_dms(nmt, current_bits[i] & 1, 1.0, 1.0);
|
||||
}
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
}
|
||||
|
||||
ok();
|
||||
|
||||
free_nmt(nmt);
|
||||
nmt = alloc_nmt();
|
||||
dms = &nmt->dms;
|
||||
|
||||
/* test zero termination */
|
||||
for (j = 0; j < 4; j++) {
|
||||
current_bit_count = 0;
|
||||
printf("zero-termination test: %d bytes in frame\n", test_null[j][7]);
|
||||
dms_send(nmt, test_null[j], test_null[j][7], 1);
|
||||
check_sequence = (char *)test_null[j];
|
||||
|
||||
while (current_bit_count) {
|
||||
printf("Sending back last received frame\n");
|
||||
for (i = 0; i < current_bit_count; i++)
|
||||
fsk_receive_bit_dms(nmt, current_bits[i] & 1, 1.0, 1.0);
|
||||
current_bit_count = 0;
|
||||
printf("Pretend that frame has been sent\n");
|
||||
dms->frame_length = 0;
|
||||
fsk_dms_frame(nmt, &sample, 1);
|
||||
}
|
||||
assert(check_length == test_null[j][7], "Expecting received length to match transmitted length");
|
||||
}
|
||||
|
||||
ok();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue