/**************************************************************************** * Signal Sink Module for the Real-time BBC Codec/Modem * **************************************************************************** * William L. Bahn * * Academy Center for Information Security * * Department of Computer Science * * United States Air Force Academy * * USAFA, CO 80840 * **************************************************************************** * FILE:............ sink.c * * DATE CREATED:.... 08 SEP 07 * * DATE MODIFIED:... 08 SEP 07 * **************************************************************************** * * REVISION HISTORY * **************************************************************************** * * DESCRIPTION * * This module supports the signal sink for both the TX and the RX * */ //------------------------------------------------------------------------ // REQUIRED INCLUDES //------------------------------------------------------------------------ #include // malloc(), free() #include // memmove() #include "sink.h" #include "bbcftp.h" #include "dirtyd.h" //------------------------------------------------------------------------ // STRUCTURE DEFINITIONS //------------------------------------------------------------------------ // NOTE: Normally the structure definition would be in the *.c file to make // the structure members inaccessible to outside functions except through // public function calls. But for the real-time code it has been decided // to make the structure members directly visible to the functions that // manipulate them. //------------------------------------------------------------------------ // PUBLIC FUNCTION DEFINITIONS //------------------------------------------------------------------------ SINK *SINK_Del(SINK *p) { if (p) { if (p->fp) if (stdout != p->fp) { fclose(p->fp); p->fp = NULL; } if (p->v) { free(p->v); p->v = NULL; } free(p); p = NULL; } return p; } // Sufficient memory is allocated up front // to handle a maximum amount of data. However, the present // contents of the buffer can be purged using SINK_Purge(). SINK *SINK_New(CONFIG *c, DWORD *errcode) { SINK *p; DWORD err; p = NULL; err = 0; p = (SINK *) malloc(sizeof(SINK)); if (!p) err |= 1 << 0; // Open Data Sink file if (!err) { p->fp = NULL; if (c->sink_name) { p->fp = fopen(c->sink_name, "wb"); if (!p->fp) err |= 1 << 7; } else p->fp = stdout; } // Initialize state information if (!err) { p->samples = 0; p->streaming = TRUE; if (c->sink_sample_limit) p->sample_limit = c->sink_sample_limit; else { if (c->scheduler_TX_notRX) { p->sample_limit = 4*c->modem_samples_per_bit*c->packet_bits; } else { p->sample_limit = 1000; } } if (c->sink_sample_size_bytes) p->sample_size_bytes = c->sink_sample_size_bytes; else { if (c->scheduler_TX_notRX) { p->sample_size_bytes = 2*sizeof(float); } else { // One byte for each eight full bits of message p->sample_size_bytes = c->codec_message_bits / 8; // Add a final byte, if necessary, to hold leftover bits if (c->codec_message_bits % 8) p->sample_size_bytes++; // Add one byte for terminating NUL character p->sample_size_bytes++; } } } // Allocate Memory for sink data if (!err) { p->buffer_size = p->sample_limit * p->sample_size_bytes; p->v = malloc(p->buffer_size); if (!p->v) err |= 1 << 1; } #ifdef DIAGNOSTICS // Diagnostic Report printf("------------------------------------------\n"); printf("SINK\n"); printf(" Creation:............... %s\n", ((err)? "FAILED":"SUCCEEDED")); printf(" Location:............... %p\n", (void *) p); printf(" Sample size:............ %lu bytes\n", (unsigned long) p->sample_size_bytes); printf(" Sample limit:........... %lu\n", (unsigned long) p->sample_limit); printf(" Buffer size:............ %lu bytes\n", (unsigned long) p->buffer_size); printf(" Buffer location:........ %p\n", (void *) p->v); printf("------------------------------------------\n"); #endif if (err) SINK_Del(p); *errcode = err; return p; } void SINK_Purge(CONFIG *c, SINK *p) { DWORD i, seq, missing, distinct; BYTE *base; int found, complete; WORD id, stream_id, last_stream_id; char filename[256]; int filenamelen; FILE *fp; // Transmitter if (c->scheduler_TX_notRX) { // Leading cushion for (i = 0; i < c->cushion_bits*c->modem_samples_per_bit; i++) fwrite(&c->bitptr[0], sizeof(float), 1, p->fp); // Buffer dump fwrite(p->v, p->sample_size_bytes, p->samples, p->fp); // Trailing cushion for (i = 0; i < c->cushion_bits*c->modem_samples_per_bit; i++) fwrite(&c->bitptr[0], sizeof(float), 1, p->fp); } // Receiver else { if (c->diagnostics) { for (i = 0; i < p->samples; i++) { base = p->v + i * p->sample_size_bytes; PrintMessage(base); } } // The assumption is that there are multiple message streams contained // in the data. So as to operate in fixed-memory, the message streams // are processed one at a time, starting with the lowest ID. This is // not an approach that is very consistent with the notion of a streaming // real-time system, but it is a start. // Stream ID's of zero will be ignored. They are used to push messages // that the decoder must receive and process and are assumed to be // discriminated against at the decoder level. stream_id = 0; fp = NULL; do { // Find next larger sequence ID that has a sequence number of zero. last_stream_id = stream_id; for (i = 0; i < p->samples; i++) { base = p->v + i * p->sample_size_bytes; if (0 == GetMessageSeq(base)) { id = GetMessageID(base); if (id > last_stream_id) if ((id < stream_id)||(stream_id == last_stream_id)) stream_id = id; } } // Process the next stream (if one was found) if (stream_id > last_stream_id) { if (c->diagnostics) printf("Stream ID: %lu.\n", (unsigned int) stream_id); missing = 0; distinct = 0; complete = FALSE; for (seq = 0; (!complete) && (seq < p->samples); seq++) { found = FALSE; for (i = 0; (!found) && (i < p->samples); i++) { base = p->v + i * p->sample_size_bytes; if ( (seq == GetMessageSeq(base))&&(stream_id == GetMessageID(base)) ) { found = TRUE; distinct++; } } if (found) { // Extract file name from header message and open file if (0 == seq) { filenamelen = GetMessageLoadBits(base)/8; if (filenamelen < 255) { memmove(filename, GetMessagePayload(base), filenamelen); filename[filenamelen] = NUL; fp = fopen(filename, "wb"); } } // Process non-header messages else { // Check for terminal message if (0 == GetMessageLoadBits(base)) complete = TRUE; // Transfer next data fragment to file else if (fp) fwrite(GetMessagePayload(base), 1, GetMessageLoadBits(base)/8, fp); } } else { if (c->diagnostics) printf("*** Missing Sequence #: %lu\n", (unsigned int) seq); missing++; } } if (c->diagnostics) { if (!complete) printf("Terminal message not found.\n"); printf("Total Missing Sequences: %lu\n", (unsigned int) missing); printf("Total Distinct Messages: %lu\n", (unsigned int) distinct); } } if (fp) { fclose(fp); fp = NULL; } } while (stream_id > last_stream_id); } p->samples = 0; }