diff --git a/include/dsd.h b/include/dsd.h index 9a14d6e..e9d751f 100644 --- a/include/dsd.h +++ b/include/dsd.h @@ -338,6 +338,12 @@ typedef struct int udp_portno; char udp_hostname[1024]; + //M17 UDP for IP frame output + int m17_use_ip; //if enabled, open UDP and broadcast IP frame + int m17_portno; //default is 17000 + int m17_udp_sock; //actual UDP socket for M17 to send to + char m17_hostname[1024]; + //tcp socket for SDR++, etc int tcp_sockfd; int tcp_portno; @@ -1275,6 +1281,8 @@ int udp_socket_connect(dsd_opts * opts, dsd_state * state); int udp_socket_connectA(dsd_opts * opts, dsd_state * state); int udp_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data); int udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void * data); +int udp_socket_connectM17(dsd_opts * opts, dsd_state * state); +int m17_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data); #ifdef __cplusplus extern "C" { diff --git a/src/dsd_main.c b/src/dsd_main.c index 6b6b1b1..cca2d2c 100644 --- a/src/dsd_main.c +++ b/src/dsd_main.c @@ -702,6 +702,12 @@ initOpts (dsd_opts * opts) opts->udp_portno = 23456; //default port, same os OP25's sockaudio.py sprintf (opts->udp_hostname, "%s", "127.0.0.1"); + //M17 UDP Port and hostname + opts->m17_use_ip = 0; //if enabled, open UDP and broadcast IP frame + opts->m17_portno = 17000; //default is 17000 + opts->m17_udp_sock = 0; //actual UDP socket for M17 to send to + sprintf (opts->m17_hostname, "%s", "127.0.0.1"); + //tcp input options opts->tcp_sockfd = 0; opts->tcp_portno = 7355; //default favored by SDR++ diff --git a/src/dsd_rigctl.c b/src/dsd_rigctl.c index 63667eb..410cb4d 100644 --- a/src/dsd_rigctl.c +++ b/src/dsd_rigctl.c @@ -32,6 +32,7 @@ void error(char *msg) { struct sockaddr_in address; struct sockaddr_in addressA; +struct sockaddr_in addressM17; // // Connect @@ -334,6 +335,22 @@ int udp_socket_blasterA(dsd_opts * opts, dsd_state * state, size_t nsam, void * if (err < nsam) fprintf (stderr, "\n UDP Underflow %ld", err); //I'm not even sure if this is possible } +int m17_socket_blaster(dsd_opts * opts, dsd_state * state, size_t nsam, void * data) +{ + UNUSED(state); + size_t err = 0; + + //listen with: + + //54 packed bytes + //socat stdio udp-listen:17000 | (decoder) + + //send audio or data to socket + err = sendto(opts->m17_udp_sock, data, nsam, 0, (const struct sockaddr * ) & addressM17, sizeof(struct sockaddr_in)); + if (err < 0) fprintf (stderr, "\n UDP SENDTO ERR %ld", err); //return value here is size_t number of characters sent, or -1 for failure + if (err < nsam) fprintf (stderr, "\n UDP Underflow %ld", err); //I'm not even sure if this is possible +} + int udp_socket_connect(dsd_opts * opts, dsd_state * state) { UNUSED(state); @@ -412,6 +429,45 @@ int udp_socket_connectA(dsd_opts * opts, dsd_state * state) } +int udp_socket_connectM17(dsd_opts * opts, dsd_state * state) +{ + UNUSED(state); + + long int err = 0; + err = opts->m17_udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (err < 0) + { + fprintf (stderr, " UDP Socket Error %ld\n", err); + return (err); + } + + // Don't think this is needed, but doesn't seem to hurt to keep it here either + int broadcastEnable = 1; + err = setsockopt(opts->m17_udp_sock, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable)); + if (err < 0) + { + fprintf (stderr, " UDP Broadcast Set Error %ld\n", err); + return (err); + } + + memset((char * ) & addressM17, 0, sizeof(addressM17)); + addressM17.sin_family = AF_INET; + err = addressM17.sin_addr.s_addr = inet_addr(opts->m17_hostname); + if (err < 0) //error in this context reports back 32-bit inet_addr reversed order byte pairs + { + fprintf (stderr, " UDP inet_addr Error %ld\n", err); + return (err); + } + + addressM17.sin_port = htons(opts->m17_portno); //plus 2 to current port assignment for the analog port value + if (err < 0) + { + fprintf (stderr, " UDP htons Error %ld\n", err); + return (err); + } + +} + void return_to_cc (dsd_opts * opts, dsd_state * state) { //extra safeguards due to sync issues with NXDN diff --git a/src/m17.c b/src/m17.c index b206ef8..91bc315 100644 --- a/src/m17.c +++ b/src/m17.c @@ -885,12 +885,52 @@ const int8_t symbol_map[4] = {+1, +3, -1, -3}; void encodeM17STR(dsd_opts * opts, dsd_state * state) { - //TODO: Standard IP Framing + //TODO: Finish creating audio of the encoded signal + + //Enable frame, TX and Ncurses Printer + opts->frame_m17 = 1; + state->m17encoder_tx = 1; + + if (opts->use_ncurses_terminal == 1) + ncursesOpen(opts, state); + + //User Defined Variables + int use_ip = 0; //1 to enable IP Frame Broadcast over UDP + int udpport = 17000; //port number for M17 IP Frmae (default is 17000) + sprintf (opts->m17_hostname, "%s", "127.0.0.1"); //enter IP address or hostname here + uint8_t can = 7; //channel access number + //numerical representation of dst and src after b40 encoding, or special/reserved value + unsigned long long int dst = 0; + unsigned long long int src = 0; + //DST and SRC Callsign Data (pick up to 9 characters from the b40 char array) + char d40[] = "BROADCAST"; //DST + char s40[] = "DSD-FME "; //SRC + //end User Defined Variables + + int i, j, k, x; //basic utility counters + short sample = 0; //individual audio sample from source + size_t nsam = 160; //number of samples to be read in (default is for codec2 3200 bps) + + //WIP: Open UDP port to 17000 and see if any other config info is necessary for running standard IP frames, etc, + //or perhaps just also configure an input format for that ip streaming method, may need to handle UDP control packets (conn, ackn, nack, ping, pong, disc) + int sock_err; + if (use_ip == 1) + { + opts->m17_portno = udpport; + sock_err = udp_socket_connectM17(opts, state); + if (sock_err < 0) + { + fprintf (stderr, "Error Configuring UDP Socket for M17 IP Frame :( \n"); + use_ip = 0; + } + else opts->m17_use_ip = 1; + } + //TODO: See if we need to send a conn and/or receice any ackn/nack/ping/pong commands later + + //WIP: Standard IP Framing uint8_t magic[4] = {0x4D, 0x31, 0x37, 0x20}; UNUSED(magic); uint8_t sid[2]; memset (sid, 0, sizeof(sid)); UNUSED(sid); - //TODO: Finish creating audio of the encoded signal - //NONCE time_t ts = time(NULL); //timestamp since epoch / "Unix Time" srand(ts); //randomizer seed based on timestamp @@ -921,27 +961,6 @@ void encodeM17STR(dsd_opts * opts, dsd_state * state) nonce[12] = rand() & 0xFF; nonce[13] = rand() & 0xFF; - //Enable frame, TX and Ncurses Printer - opts->frame_m17 = 1; - state->m17encoder_tx = 1; - - if (opts->use_ncurses_terminal == 1) - ncursesOpen(opts, state); - - //User Defined Variables - uint8_t can = 7; //channel access number - //numerical representation of dst and src after b40 encoding, or special/reserved value - unsigned long long int dst = 0; - unsigned long long int src = 0; - //DST and SRC Callsign Data (pick up to 9 characters from the b40 char array) - char d40[] = "BROADCAST"; //DST - char s40[] = "DSD-FME "; //SRC - //end User Defined Variables - - int i, j, k, x; //basic utility counters - short sample = 0; //individual audio sample from source - size_t nsam = 160; //number of samples to be read in (default is for codec2 3200 bps) - //debug print nonce // fprintf (stderr, " nonce:"); // for (i = 0; i < 14; i++) @@ -1347,8 +1366,68 @@ void encodeM17STR(dsd_opts * opts, dsd_state * state) UNUSED(output_symbols); //TODO: symbols to audio + + //Contruct an IP frame using previously created arrays + uint8_t m17_ip_frame[432]; memset (m17_ip_frame, 0, sizeof(m17_ip_frame)); UNUSED(m17_ip_frame); + uint8_t m17_ip_packed[54]; memset (m17_ip_packed, 0, sizeof(m17_ip_packed)); UNUSED(m17_ip_packed); + uint16_t ip_crc = 0; UNUSED(ip_crc); - //TODO: Send via Standard IP Framing + //NOTE: The Manual doesn't say much about this area, so assuming the elements are arranged as per the table + //add MAGIC + k = 0; + for (j = 0; j < 4; j++) + { + for (i = 0; i < 8; i++) + m17_ip_frame[k++] = (magic[j] >> 7-i) &1; + } + + //add StreamID to ip_frame + for (j = 0; j < 2; j++) + { + for (i = 0; i < 8; i++) + m17_ip_frame[k++] = (sid[j] >> 7-i) &1; + } + + //add the current LSF, sans CRC + for (i = 0; i < 224; i++) + m17_ip_frame[k++] = m17_lsf[i]; + + //add current fsn value + for (i = 0; i < 16; i++) + m17_ip_frame[k++] = (fsn >> 15-i)&1; + + //voice and/or data payload + for (i = 0; i < 64; i++) + m17_ip_frame[k++] = v1_bits[i]; + + //voice and/or data payload + for (i = 0; i < 64; i++) + m17_ip_frame[k++] = v2_bits[i]; + + //pack current bit array into a byte array for a CRC check + for (i = 0; i < 52; i++) + m17_ip_packed[i] = (uint8_t)ConvertBitIntoBytes(&m17_ip_frame[i*8], 8); + ip_crc = crc16m17(m17_ip_packed, 52); + + //add CRC value to the ip frame + for (i = 0; i < 16; i++) + m17_ip_frame[k++] = (ip_crc >> 15-i)&1; + + //pack it into the byte array as well + for (i = 52; i < 54; i++) + m17_ip_packed[i] = (uint8_t)ConvertBitIntoBytes(&m17_ip_frame[i*8], 8); + + //debug print packed byte output for IP frame + // fprintf (stderr, "\n IP: "); + // for (i = 0; i < 54; i++) + // { + // if ( (i%12)==0) fprintf (stderr, "\n"); + // fprintf (stderr, " %02X", m17_ip_packed[i]); + // } + + //WIP: Send packed IP frame to UDP port 17000 if opened successfully + if (opts->m17_use_ip == 1) + m17_socket_blaster (opts, state, 54, m17_ip_packed); //increment lich_cnt, reset on 6 lich_cnt++;