From 64c829909bc5b73a7765272ad07cf6465762aeac Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 4 Jul 2016 07:23:44 +0200 Subject: NMT / DMS: User data facility support (required for SMS) --- .gitignore | 1 + src/common/debug.c | 1 + src/common/debug.h | 1 + src/nmt/Makefile.am | 1 + src/nmt/dms.c | 869 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nmt/dms.h | 61 ++++ src/nmt/dsp.c | 56 +++- src/nmt/dsp.h | 1 + src/nmt/nmt.c | 24 +- src/nmt/nmt.h | 7 +- src/test/Makefile.am | 12 +- src/test/test_dms.c | 262 ++++++++++++++++ 12 files changed, 1277 insertions(+), 19 deletions(-) create mode 100644 src/nmt/dms.c create mode 100644 src/nmt/dms.h create mode 100644 src/test/test_dms.c diff --git a/.gitignore b/.gitignore index 2ea0dd9..0264cfe 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ src/nmt/nmt src/amps/amps src/test/test_compandor src/test/test_emphasis +src/test/test_dms diff --git a/src/common/debug.c b/src/common/debug.c index 296b189..08e8620 100644 --- a/src/common/debug.c +++ b/src/common/debug.c @@ -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 } }; diff --git a/src/common/debug.h b/src/common/debug.h index a6c07b9..8ca9f78 100644 --- a/src/common/debug.h +++ b/src/common/debug.h @@ -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, ...); diff --git a/src/nmt/Makefile.am b/src/nmt/Makefile.am index b5a0eed..dee1ccd 100644 --- a/src/nmt/Makefile.am +++ b/src/nmt/Makefile.am @@ -7,6 +7,7 @@ nmt_SOURCES = \ nmt.c \ dsp.c \ frame.c \ + dms.c \ image.c \ tones.c \ announcement.c \ diff --git a/src/nmt/dms.c b/src/nmt/dms.c new file mode 100644 index 0000000..fa75a76 --- /dev/null +++ b/src/nmt/dms.c @@ -0,0 +1,869 @@ +/* NMT DMS (data service) processing + * + * (C) 2016 by Andreas Eversberg + * 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 . + */ + +#include +#include +#include +#include +#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); +} + diff --git a/src/nmt/dms.h b/src/nmt/dms.h new file mode 100644 index 0000000..d72af8e --- /dev/null +++ b/src/nmt/dms.h @@ -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); + diff --git a/src/nmt/dsp.c b/src/nmt/dsp.c index 810e2a5..603d4c4 100644 --- a/src/nmt/dsp.c +++ b/src/nmt/dsp.c @@ -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; diff --git a/src/nmt/dsp.h b/src/nmt/dsp.h index 1a14d14..b6fbc60 100644 --- a/src/nmt/dsp.h +++ b/src/nmt/dsp.h @@ -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); diff --git a/src/nmt/nmt.c b/src/nmt/nmt.c index b192a60..dbd5ff8 100644 --- a/src/nmt/nmt.c +++ b/src/nmt/nmt.c @@ -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) { diff --git a/src/nmt/nmt.h b/src/nmt/nmt.h index 7efbc9d..7a8c1d7 100644 --- a/src/nmt/nmt.h +++ b/src/nmt/nmt.h @@ -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); diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 9986f68..78e7d7b 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -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 + diff --git a/src/test/test_dms.c b/src/test/test_dms.c new file mode 100644 index 0000000..c773cfc --- /dev/null +++ b/src/test/test_dms.c @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include +#include +#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; +} + -- cgit v1.2.3