diff options
Diffstat (limited to 'src/freedv_data_tx.c')
| -rw-r--r-- | src/freedv_data_tx.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/freedv_data_tx.c b/src/freedv_data_tx.c new file mode 100644 index 0000000..b37a46a --- /dev/null +++ b/src/freedv_data_tx.c @@ -0,0 +1,285 @@ +/*---------------------------------------------------------------------------*\ + + FILE........: freedv_data_tx.c + AUTHOR......: Jeroen Vreeken + DATE CREATED: May 2020 + + Demo VHF packet data transmit program for FreeDV API functions. + +\*---------------------------------------------------------------------------*/ + +/* + Copyright (C) 2020 Jeroen Vreeken + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. 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 Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdint.h> +#include <stdbool.h> + +#include "freedv_api.h" +#include "codec2.h" + + +/********************************************************** + Encoding an ITU callsign (and 4 bit secondary station ID to a valid MAC address. + http://dmlinking.net/eth_ar.html + */ + +// Lookup table for valid callsign characters +static char alnum2code[37] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 0 +}; + +// Encode a callsign and ssid into a valid MAC address +static int eth_ar_call2mac(uint8_t mac[6], char *callsign, int ssid, bool multicast) +{ + uint64_t add = 0; + int i; + + if (ssid > 15 || ssid < 0) + return -1; + + for (i = 7; i >= 0; i--) { + char c; + + if (i >= strlen(callsign)) { + c = 0; + } else { + c = toupper(callsign[i]); + } + + int j; + + for (j = 0; j < sizeof(alnum2code); j++) { + if (alnum2code[j] == c) + break; + } + if (j == sizeof(alnum2code)) + return -1; + + add *= 37; + add += j; + } + + mac[0] = ((add >> (40 - 6)) & 0xc0) | (ssid << 2) | 0x02 | multicast; + mac[1] = (add >> 32) & 0xff; + mac[2] = (add >> 24) & 0xff; + mac[3] = (add >> 16) & 0xff; + mac[4] = (add >> 8) & 0xff; + mac[5] = add & 0xff; + + return 0; +} + + +/********************************************************** + Data channel callback functions + */ + + +struct my_callback_state { + int calls; + + unsigned char mac[6]; +}; + +/* + Called when a packet has been received + Should not be called in this tx-only test program + */ +void my_datarx(void *callback_state, unsigned char *packet, size_t size) +{ + /* This should not happen while sending... */ + fprintf(stderr, "datarx callback called, this should not happen!\n"); +} + + +/* + Called when a new packet can be send. + + callback_state Private state variable, not touched by freedv. + packet Data array where new packet data is expected + size Available size in packet. On return the actual size of the packet + */ +void my_datatx(void *callback_state, unsigned char *packet, size_t *size) +{ + static int data_type; + struct my_callback_state *my_cb_state = callback_state; + my_cb_state->calls++; + + /* Data could come from a network interface, here we just make up some */ + + if (data_type % 4 == 1) { + /* + Generate a packet with simple test pattern (counting + */ + + /* Send a packet with data */ + int i; + + /* Destination: broadcast */ + memset(packet, 0xff, 6); + /* Source: our eth_ar encoded callsign+ssid */ + memcpy(packet+6, my_cb_state->mac, 6); + /* Ether type: experimental (since this is just a test pattern) */ + packet[12] = 0x01; + packet[13] = 0x01; + + for (i = 0; i < 64; i++) + packet[i + 14] = i; + *size = i + 14; + } else if (data_type % 4 == 2) { + /* + Generate an FPRS position report + */ + + /* Destination: broadcast */ + memset(packet, 0xff, 6); + /* Source: our eth_ar encoded callsign+ssid */ + memcpy(packet+6, my_cb_state->mac, 6); + /* Ether type: FPRS */ + packet[12] = 0x73; + packet[13] = 0x70; + + packet[14] = 0x07; // Position element Lon 86.925026 Lat 27.987850 + packet[15] = 0x3d; // + packet[16] = 0xd0; + packet[17] = 0x37; + packet[18] = 0xd0 | 0x08 | 0x01; + packet[19] = 0x3e; + packet[20] = 0x70; + packet[21] = 0x85; + + *size = 22; + } else { + /* + Set size to zero, the freedv api will insert a header frame + This is useful for identifying ourselves + */ + *size = 0; + } + + data_type++; +} + + +int main(int argc, char *argv[]) { + FILE *fout; + short *mod_out; + struct freedv *freedv; + struct my_callback_state my_cb_state; + int mode; + int n_nom_modem_samples; + int i; + int n_packets = 20; + char *callsign = "NOCALL"; + int ssid = 0; + bool multicast = false; + + if (argc < 3) { + printf("usage: %s 2400A|2400B|800XA OutputModemRawFile\n" + " [--frames nr] [--callsign callsign] [--ssid ssid] [--mac-multicast 0|1]\n", argv[0]); + printf("e.g %s 2400A data_fdmdv.raw\n", argv[0]); + exit(1); + } + + mode = -1; + if (!strcmp(argv[1],"2400A")) + mode = FREEDV_MODE_2400A; + if (!strcmp(argv[1],"2400B")) + mode = FREEDV_MODE_2400B; + if (!strcmp(argv[1],"800XA")) + mode = FREEDV_MODE_800XA; + if (mode == -1) { + fprintf(stderr, "Error in mode: %s\n", argv[1]); + exit(0); + } + + if (strcmp(argv[2], "-") == 0) fout = stdout; + else if ( (fout = fopen(argv[2],"wb")) == NULL ) { + fprintf(stderr, "Error opening output modem sample file: %s: %s.\n", argv[3], strerror(errno)); + exit(1); + } + + if (argc > 3) { + for (i = 3; i < argc; i++) { + if (strcmp(argv[i], "--packets") == 0) { + n_packets = atoi(argv[i+1]); + } + if (strcmp(argv[i], "--callsign") == 0) { + callsign = argv[i+1]; + } + if (strcmp(argv[i], "--ssid") == 0) { + ssid = atoi(argv[i+1]); + } + if (strcmp(argv[i], "--mac-multicast") == 0) { + multicast = atoi(argv[i+1]); + } + } + } + + freedv = freedv_open(mode); + assert(freedv != NULL); + + /* Generate our address */ + eth_ar_call2mac(my_cb_state.mac, callsign, ssid, multicast); + + freedv_set_data_header(freedv, my_cb_state.mac); + + freedv_set_verbose(freedv, 1); + + n_nom_modem_samples = freedv_get_n_nom_modem_samples(freedv); + mod_out = (short*)malloc(sizeof(short)*n_nom_modem_samples); + assert(mod_out != NULL); + + /* set up callback for data packets */ + freedv_set_callback_data(freedv, my_datarx, my_datatx, &my_cb_state); + + /* OK main loop */ + + /* We will loop until the tx callback has been called n_packets times + After that we continue until everything is transmitted, as a data + packet might be transmitted in multiple freedv frames. + */ + while (my_cb_state.calls <= n_packets || freedv_data_ntxframes(freedv)) { + freedv_datatx(freedv, mod_out); + + fwrite(mod_out, sizeof(short), n_nom_modem_samples, fout); + + + /* if this is in a pipeline, we probably don't want the usual + buffering to occur */ + if (fout == stdout) fflush(stdout); + } + + free(mod_out); + freedv_close(freedv); + fclose(fout); + + fclose(stdin); + fclose(stderr); + + return 0; +} + |
