You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

263 lines
6.3 KiB

/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2016, 2020
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 "freedv_eth_rx.h"
#include "freedv_eth.h"
#include "interface.h"
#include "sound.h"
#include <string.h>
#include <stdio.h>
#include <codec2/codec2.h>
static bool cdc_voice = false;
static int nr_rx;
static float rx_sync = 0;
static bool cdc;
static int16_t *samples_rx = NULL;
static int bytes_per_freedv_frame;
static int bytes_per_codec2_frame;
static uint16_t eth_type_rx;
static void *silence_packet = NULL;
static struct sound_resample *sr = NULL;
struct freedv *freedv;
static uint8_t transmission = 128;
static double level_dbm = -80.0;
static uint8_t rx_add[6], mac[6];
#define RX_SYNC_ZERO 15.0
#define RX_SYNC_DATABONUS 40.0
#define RX_SYNC_THRESHOLD 90.0
static uint8_t bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
bool freedv_eth_rx_cdc(void)
{
return cdc;
}
static void freedv_eth_rx_rawdata(int ret, unsigned char *packed_codec_bits)
{
bool old_cdc = cdc;
/* Don't 'detect' a voice signal to soon.
*/
int sync;
float snr_est;
freedv_get_modem_stats(freedv, &sync, &snr_est);
if (!sync) {
if (cdc)
printf("RX sync lost\n");
rx_sync = RX_SYNC_ZERO;
cdc = false;
} else {
rx_sync += snr_est - RX_SYNC_ZERO;
}
cdc |= (ret && rx_sync > RX_SYNC_THRESHOLD);
if (ret && cdc) {
freedv_eth_voice_rx(
bcast, rx_add, eth_type_rx,
packed_codec_bits,
bytes_per_freedv_frame, true,
transmission, level_dbm);
printf(".");
fflush(NULL);
cdc_voice = true;
} else if (cdc) {
int i;
/* Data frame between voice data? */
printf("*");
fflush(NULL);
if (cdc_voice) {
for (i = 0; i < bytes_per_freedv_frame/bytes_per_codec2_frame; i++) {
freedv_eth_voice_rx(
bcast, rx_add, eth_type_rx,
silence_packet,
bytes_per_codec2_frame, true,
transmission, level_dbm);
}
}
}
// if (sync)
// printf(" %f\t%f\t%f\n", snr_est, rx_sync, snr_est-RX_SYNC_ZERO);
/* Reset rx address for voice to our own mac */
if (!cdc && old_cdc) {
printf("Reset RX add\n");
memcpy(rx_add, mac, 6);
cdc_voice = false;
queue_voice_end(transmission);
transmission++;
}
}
void freedv_eth_rx(int16_t *hw_samples, int hw_nr)
{
int nr;
int16_t *samples;
if (sr) {
nr = sound_resample_nr_out(sr, hw_nr);
samples = alloca(sizeof(int16_t) * nr);
sound_resample_perform(sr, samples, hw_samples, nr, hw_nr);
} else {
nr = hw_nr;
samples = hw_samples;
}
while (nr) {
int nin = freedv_nin(freedv);
int copy = nin - nr_rx;
if (copy > nr)
copy = nr;
memcpy(samples_rx + nr_rx, samples, copy * sizeof(int16_t));
samples += copy;
nr -= copy;
nr_rx += copy;
if (nr_rx == nin) {
unsigned char packed_codec_bits[bytes_per_freedv_frame];
int ret = freedv_rawdatarx(freedv, packed_codec_bits, samples_rx);
freedv_eth_rx_rawdata(ret, packed_codec_bits);
nr_rx = 0;
}
}
}
void freedv_eth_symrx(signed char *rxsym)
{
unsigned char packed_codec_bits[bytes_per_freedv_frame];
#if defined(FREEDV_MODE_6000)
int ret = freedv_rawdatasymrx(freedv, packed_codec_bits, rxsym);
#endif
freedv_eth_rx_rawdata(ret, packed_codec_bits);
}
void freedv_eth_rx_cb_datarx(void *arg, unsigned char *packet, size_t size)
{
if (rx_sync < RX_SYNC_DATABONUS)
rx_sync += RX_SYNC_DATABONUS;
if (size == 12) {
if (memcmp(rx_add, packet + 6, 6)) {
char callstr[9];
int ssid;
bool multicast;
memcpy(rx_add, packet + 6, 6);
eth_ar_mac2call(callstr, &ssid, &multicast, rx_add);
printf("Voice RX add: %s-%d%s\n", callstr, ssid, multicast ? "" : "*");
}
} else if (size > 14) {
/* Filter out our own packets if they come back */
if (memcmp(packet+6, mac, 6)) {
uint16_t type = (packet[12] << 8) | packet[13];
interface_rx_raw(packet, packet+6, type, packet + 14, size - 14);
printf("^\n");
}
}
}
void freedv_eth_rx_vc_callback(void *arg, char c)
{
uint8_t msg[2];
/* Ignore if not receiving */
if (!freedv_eth_rx_cdc())
return;
if (c)
printf("VC RX: 0x%x %c\n", c, c);
msg[0] = c;
msg[1] = 0;
interface_rx(bcast, rx_add, ETH_P_AR_CONTROL, msg, 1, 0, 1);
}
static void create_silence_packet(struct CODEC2 *c2)
{
int nr = codec2_samples_per_frame(c2);
int16_t samples[nr];
free(silence_packet);
silence_packet = calloc(1, bytes_per_codec2_frame);
memset(samples, 0, nr * sizeof(int16_t));
int i;
unsigned char *sp = silence_packet;
for (i = 0; i < bytes_per_freedv_frame/bytes_per_codec2_frame; i++) {
codec2_encode(c2, sp, samples);
sp += bytes_per_freedv_frame;
}
}
int freedv_eth_rx_init(struct freedv *init_freedv, uint8_t init_mac[6], int hw_rate)
{
freedv = init_freedv;
int nr_samples;
int f_rate = freedv_get_modem_sample_rate(freedv);
cdc = false;
sound_resample_destroy(sr);
if (f_rate != hw_rate) {
sr = sound_resample_create(f_rate, hw_rate);
} else {
sr = NULL;
}
bytes_per_codec2_frame = freedv_get_bits_per_codec_frame(freedv);
bytes_per_codec2_frame += 7;
bytes_per_codec2_frame /= 8;
printf("RX bytes per codec2 frame: %d\n", bytes_per_codec2_frame);
int rat = freedv_get_bits_per_modem_frame(freedv) / freedv_get_bits_per_codec_frame(freedv);
printf("RX codec2 frames per freedv frame: %d\n", rat);
bytes_per_freedv_frame = bytes_per_codec2_frame * rat;
printf("RX bytes per freedv frame: %d\n", bytes_per_freedv_frame);
nr_samples = freedv_get_n_max_modem_samples(freedv);
create_silence_packet(freedv_get_codec2(freedv));
free(samples_rx);
samples_rx = calloc(nr_samples, sizeof(samples_rx[0]));
int mode = freedv_get_mode(freedv);
eth_type_rx = freedv_eth_mode2type(mode);
memcpy(mac, init_mac, 6);
memcpy(rx_add, init_mac, 6);
return 0;
}