mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-30 03:38:55 -05:00
868 lines
28 KiB
C++
868 lines
28 KiB
C++
|
/*---------------------------------------------------------------------------*\
|
||
|
|
||
|
FILE........: fsk.c
|
||
|
AUTHOR......: Brady O'Brien
|
||
|
DATE CREATED: 11 February 2016
|
||
|
|
||
|
Framer and deframer for VHF FreeDV modes 'A' and 'B'
|
||
|
Currently designed for-
|
||
|
* 40ms ota modem frames
|
||
|
* 40ms Codec2 1300 frames
|
||
|
* 52 bits of Codec2 per frame
|
||
|
* 16 bits of unique word per frame
|
||
|
* 28 'spare' bits per frame
|
||
|
* - 4 spare bits at front and end of frame (8 total) for padding
|
||
|
* - 20 'protocol' bits, either for higher layers of 'protocol' or
|
||
|
* - 18 'protocol' bits and 2 vericode sidechannel bits
|
||
|
|
||
|
\*---------------------------------------------------------------------------*/
|
||
|
|
||
|
/*
|
||
|
Copyright (C) 2016 David Rowe
|
||
|
|
||
|
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 <stdint.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <assert.h>
|
||
|
#include "freedv_vhf_framing.h"
|
||
|
|
||
|
namespace FreeDV
|
||
|
{
|
||
|
|
||
|
/* The voice UW of the VHF type A frame */
|
||
|
static const uint8_t A_uw_v[] = {0,1,1,0,0,1,1,1,
|
||
|
1,0,1,0,1,1,0,1};
|
||
|
|
||
|
/* The data UW of the VHF type A frame */
|
||
|
static const uint8_t A_uw_d[] = {1,1,1,1,0,0,0,1,
|
||
|
1,1,1,1,1,1,0,0};
|
||
|
|
||
|
/* Blank VHF type A frame */
|
||
|
static const uint8_t A_blank[] = {1,0,1,0,0,1,1,1, /* Padding[0:3] Proto[0:3] */
|
||
|
1,0,1,0,0,1,1,1, /* Proto[4:11] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice[0:7] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice[8:15] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice[16:23] */
|
||
|
0,1,1,0,0,1,1,1, /* UW[0:7] */
|
||
|
1,0,1,0,1,1,0,1, /* UW[8:15] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice[24:31] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice[32:39] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice[40:47] */
|
||
|
0,0,0,0,0,0,1,0, /* Voice[48:51] Proto[12:15] */
|
||
|
0,1,1,1,0,0,1,0};/* Proto[16:19] Padding[4:7] */
|
||
|
|
||
|
/* Blank VHF type AT (A for TDMA; padding bits not transmitted) frame */
|
||
|
static const uint8_t AT_blank[] = { 0,1,1,1, /* Proto[0:3] */
|
||
|
1,0,1,0,0,1,1,1, /* Proto[4:11] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice[0:7] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice[8:15] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice[16:23] */
|
||
|
0,1,1,0,0,1,1,1, /* UW[0:7] */
|
||
|
1,0,1,0,1,1,0,1, /* UW[8:15] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice[24:31] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice[32:39] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice[40:47] */
|
||
|
0,0,0,0,0,0,1,0, /* Voice[48:51] Proto[12:15] */
|
||
|
0,1,1,1 };/* Proto[16:19] */
|
||
|
|
||
|
/* HF Type B voice UW */
|
||
|
static const uint8_t B_uw_v[] = {0,1,1,0,0,1,1,1};
|
||
|
|
||
|
/* HF Type B data UW */
|
||
|
static const uint8_t B_uw_d[] = {1,1,1,1,0,0,1,0};
|
||
|
|
||
|
/* Blank HF type B frame */
|
||
|
static const uint8_t B_blank[] = {0,1,1,0,0,1,1,1, /* UW[0:7] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice1[0:7] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice1[8:15] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice1[16:23] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice1[24:28] Voice2[0:3] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice2[4:11] */
|
||
|
0,0,0,0,0,0,0,0, /* Voice2[12:19] */
|
||
|
0,0,0,0,0,0,0,0};/* Voice2[20:28] */
|
||
|
|
||
|
/* States */
|
||
|
#define ST_NOSYNC 0 /* Not synchronized */
|
||
|
#define ST_SYNC 1 /* Synchronized */
|
||
|
|
||
|
/* Get a single bit out of an MSB-first packed byte array */
|
||
|
#define UNPACK_BIT_MSBFIRST(bytes,bitidx) ((bytes)[(bitidx)>>3]>>(7-((bitidx)&0x7)))&0x1
|
||
|
|
||
|
enum frame_payload_type {
|
||
|
FRAME_PAYLOAD_TYPE_VOICE,
|
||
|
FRAME_PAYLOAD_TYPE_DATA,
|
||
|
};
|
||
|
|
||
|
/* Place codec and other bits into a frame */
|
||
|
void fvhff_frame_bits( int frame_type,
|
||
|
uint8_t bits_out[],
|
||
|
uint8_t codec2_in[],
|
||
|
uint8_t proto_in[],
|
||
|
uint8_t vc_in[]){
|
||
|
int i,ibit;
|
||
|
if(frame_type == FREEDV_VHF_FRAME_A){
|
||
|
/* Fill out frame with blank frame prototype */
|
||
|
for(i=0; i<96; i++)
|
||
|
bits_out[i] = A_blank[i];
|
||
|
|
||
|
/* Fill in protocol bits, if present */
|
||
|
if(proto_in!=NULL){
|
||
|
ibit = 0;
|
||
|
/* First half of protocol bits */
|
||
|
/* Extract and place in frame, MSB first */
|
||
|
for(i=4 ; i<16; i++){
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(proto_in,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
/* Last set of protocol bits */
|
||
|
for(i=84; i<92; i++){
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(proto_in,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Fill in varicode bits, if present */
|
||
|
if(vc_in!=NULL){
|
||
|
bits_out[90] = vc_in[0];
|
||
|
bits_out[91] = vc_in[1];
|
||
|
}
|
||
|
|
||
|
/* Fill in codec2 bits, present or not */
|
||
|
ibit = 0;
|
||
|
for(i=16; i<40; i++){ /* First half */
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(codec2_in,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
for(i=56; i<84; i++){ /* Second half */
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(codec2_in,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
}else if(frame_type == FREEDV_HF_FRAME_B){
|
||
|
/* Pointers to both c2 frames so the bit unpack macro works */
|
||
|
uint8_t * codec2_in1 = &codec2_in[0];
|
||
|
uint8_t * codec2_in2 = &codec2_in[4];
|
||
|
/* Fill out frame with blank prototype */
|
||
|
for(i=0; i<64; i++)
|
||
|
bits_out[i] = B_blank[i];
|
||
|
|
||
|
/* Fill out first codec2 block */
|
||
|
ibit=0;
|
||
|
for(i=8; i<36; i++){
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(codec2_in1,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
/* Fill out second codec2 block */
|
||
|
ibit=0;
|
||
|
for(i=36; i<64; i++){
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(codec2_in2,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
}else if(frame_type == FREEDV_VHF_FRAME_AT){
|
||
|
/* Fill out frame with blank frame prototype */
|
||
|
for(i=0; i<88; i++)
|
||
|
bits_out[i] = AT_blank[i];
|
||
|
|
||
|
/* Fill in protocol bits, if present */
|
||
|
if(proto_in!=NULL){
|
||
|
ibit = 0;
|
||
|
/* First half of protocol bits */
|
||
|
/* Extract and place in frame, MSB first */
|
||
|
for(i=0 ; i<12; i++){
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(proto_in,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
/* Last set of protocol bits */
|
||
|
for(i=80; i<88; i++){
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(proto_in,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Fill in varicode bits, if present */
|
||
|
if(vc_in!=NULL){
|
||
|
bits_out[86] = vc_in[0];
|
||
|
bits_out[87] = vc_in[1];
|
||
|
}
|
||
|
|
||
|
/* Fill in codec2 bits, present or not */
|
||
|
ibit = 0;
|
||
|
for(i=12; i<36; i++){ /* First half */
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(codec2_in,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
for(i=52; i<80; i++){ /* Second half */
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(codec2_in,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Place data and other bits into a frame */
|
||
|
void fvhff_frame_data_bits(struct freedv_vhf_deframer * def, int frame_type,
|
||
|
uint8_t bits_out[]){
|
||
|
int i,ibit;
|
||
|
if(frame_type == FREEDV_VHF_FRAME_A){
|
||
|
uint8_t data[8];
|
||
|
int end_bits;
|
||
|
int from_bit;
|
||
|
int bcast_bit;
|
||
|
int crc_bit;
|
||
|
|
||
|
/* Fill out frame with blank frame prototype */
|
||
|
for(i=0; i<4; i++)
|
||
|
bits_out[i] = A_blank[i];
|
||
|
for(i=92; i<96; i++)
|
||
|
bits_out[i] = A_blank[i];
|
||
|
|
||
|
/* UW data */
|
||
|
for (i=0; i < 16; i++)
|
||
|
bits_out[40 + i] = A_uw_d[i];
|
||
|
|
||
|
if (def->fdc)
|
||
|
freedv_data_channel_tx_frame(def->fdc, data, 8, &from_bit, &bcast_bit, &crc_bit, &end_bits);
|
||
|
else
|
||
|
return;
|
||
|
|
||
|
bits_out[4] = from_bit;
|
||
|
bits_out[5] = bcast_bit;
|
||
|
bits_out[6] = 0; /* unused */
|
||
|
bits_out[7] = 0; /* unused */
|
||
|
|
||
|
/* Fill in data bits */
|
||
|
ibit = 0;
|
||
|
for(i=8; i<40; i++){ /* First half */
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(data,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
for(i=56; i<88; i++){ /* Second half */
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(data,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 4; i++)
|
||
|
bits_out[88 + i] = (end_bits >> (3-i)) & 0x1;
|
||
|
} else if (frame_type == FREEDV_HF_FRAME_B){
|
||
|
uint8_t data[6];
|
||
|
int end_bits;
|
||
|
int from_bit;
|
||
|
int bcast_bit;
|
||
|
int crc_bit;
|
||
|
|
||
|
/* Fill out frame with blank prototype */
|
||
|
for(i=0; i<64; i++)
|
||
|
bits_out[i] = B_blank[i];
|
||
|
|
||
|
/* UW data */
|
||
|
for (i=0; i < 8; i++)
|
||
|
bits_out[0 + i] = B_uw_d[i];
|
||
|
|
||
|
if (def->fdc)
|
||
|
freedv_data_channel_tx_frame(def->fdc, data, 6, &from_bit, &bcast_bit, &crc_bit, &end_bits);
|
||
|
else
|
||
|
return;
|
||
|
|
||
|
bits_out[56] = from_bit;
|
||
|
bits_out[57] = bcast_bit;
|
||
|
bits_out[58] = crc_bit;
|
||
|
bits_out[59] = 0; /* unused */
|
||
|
|
||
|
/* Fill in data bits */
|
||
|
ibit = 0;
|
||
|
for(i=8; i<56; i++){ /* First half */
|
||
|
bits_out[i] = UNPACK_BIT_MSBFIRST(data,ibit);
|
||
|
ibit++;
|
||
|
}
|
||
|
for (i = 0; i < 4; i++)
|
||
|
bits_out[60 + i] = (end_bits >> (3-i)) & 0x1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Init and allocate memory for a freedv-vhf framer/deframer */
|
||
|
struct freedv_vhf_deframer * fvhff_create_deframer(uint8_t frame_type, int enable_bit_flip){
|
||
|
struct freedv_vhf_deframer * deframer;
|
||
|
uint8_t *bits,*invbits;
|
||
|
int frame_size;
|
||
|
int uw_size;
|
||
|
|
||
|
assert( (frame_type == FREEDV_VHF_FRAME_A) || (frame_type == FREEDV_HF_FRAME_B) );
|
||
|
|
||
|
/* It's a Type A frame */
|
||
|
if(frame_type == FREEDV_VHF_FRAME_A){
|
||
|
frame_size = 96;
|
||
|
uw_size = 16;
|
||
|
}else if(frame_type == FREEDV_HF_FRAME_B){
|
||
|
frame_size = 64;
|
||
|
uw_size = 8;
|
||
|
}else{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Allocate memory for the thing */
|
||
|
deframer = (freedv_vhf_deframer*) malloc(sizeof(struct freedv_vhf_deframer));
|
||
|
if(deframer == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
/* Allocate the not-bit buffer */
|
||
|
if(enable_bit_flip){
|
||
|
invbits = (uint8_t*) malloc(sizeof(uint8_t)*frame_size);
|
||
|
if(invbits == NULL) {
|
||
|
free(deframer);
|
||
|
return NULL;
|
||
|
}
|
||
|
}else{
|
||
|
invbits = NULL;
|
||
|
}
|
||
|
|
||
|
/* Allocate the bit buffer */
|
||
|
bits = (uint8_t*) malloc(sizeof(uint8_t)*frame_size);
|
||
|
if(bits == NULL) {
|
||
|
free(deframer);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
deframer->bits = bits;
|
||
|
deframer->invbits = invbits;
|
||
|
deframer->ftype = frame_type;
|
||
|
deframer->state = ST_NOSYNC;
|
||
|
deframer->bitptr = 0;
|
||
|
deframer->last_uw = 0;
|
||
|
deframer->miss_cnt = 0;
|
||
|
deframer->frame_size = frame_size;
|
||
|
deframer->uw_size = uw_size;
|
||
|
deframer->on_inv_bits = 0;
|
||
|
deframer->sym_size = 1;
|
||
|
|
||
|
deframer->ber_est = 0;
|
||
|
deframer->total_uw_bits = 0;
|
||
|
deframer->total_uw_err = 0;
|
||
|
|
||
|
deframer->fdc = NULL;
|
||
|
|
||
|
return deframer;
|
||
|
}
|
||
|
|
||
|
/* Get size of frame in bits */
|
||
|
int fvhff_get_frame_size(struct freedv_vhf_deframer * def){
|
||
|
return def->frame_size;
|
||
|
}
|
||
|
|
||
|
/* Codec2 size in bytes */
|
||
|
int fvhff_get_codec2_size(struct freedv_vhf_deframer * def){
|
||
|
if(def->ftype == FREEDV_VHF_FRAME_A){
|
||
|
return 7;
|
||
|
} else if(def->ftype == FREEDV_HF_FRAME_B){
|
||
|
return 8;
|
||
|
} else{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Protocol bits in bits */
|
||
|
int fvhff_get_proto_size(struct freedv_vhf_deframer * def){
|
||
|
if(def->ftype == FREEDV_VHF_FRAME_A){
|
||
|
return 20;
|
||
|
} else if(def->ftype == FREEDV_HF_FRAME_B){
|
||
|
return 0;
|
||
|
} else{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Varicode bits in bits */
|
||
|
int fvhff_get_varicode_size(struct freedv_vhf_deframer * def){
|
||
|
if(def->ftype == FREEDV_VHF_FRAME_A){
|
||
|
return 2;
|
||
|
} else if(def->ftype == FREEDV_HF_FRAME_B){
|
||
|
return 0;
|
||
|
} else{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void fvhff_destroy_deframer(struct freedv_vhf_deframer * def){
|
||
|
freedv_data_channel_destroy(def->fdc);
|
||
|
free(def->bits);
|
||
|
free(def);
|
||
|
}
|
||
|
|
||
|
int fvhff_synchronized(struct freedv_vhf_deframer * def){
|
||
|
return (def->state) == ST_SYNC;
|
||
|
}
|
||
|
|
||
|
/* Search for a complete UW in a buffer of bits */
|
||
|
std::size_t fvhff_search_uw(const uint8_t bits[], std::size_t nbits,
|
||
|
const uint8_t uw[], std::size_t uw_len,
|
||
|
std::size_t * delta_out, std::size_t bits_per_sym){
|
||
|
|
||
|
std::size_t ibits,iuw;
|
||
|
std::size_t delta_min = uw_len;
|
||
|
std::size_t delta;
|
||
|
std::size_t offset_min = 0;
|
||
|
/* Walk through buffer bits */
|
||
|
for(ibits = 0; ibits < nbits-uw_len; ibits+=bits_per_sym){
|
||
|
delta = 0;
|
||
|
for(iuw = 0; iuw < uw_len; iuw++){
|
||
|
if(bits[ibits+iuw] != uw[iuw]) delta++;
|
||
|
}
|
||
|
if( delta < delta_min ){
|
||
|
delta_min = delta;
|
||
|
offset_min = ibits;
|
||
|
}
|
||
|
}
|
||
|
if(delta_out != NULL) *delta_out = delta_min;
|
||
|
return offset_min;
|
||
|
}
|
||
|
|
||
|
/* See if the UW is where it should be, to within a tolerance, in a bit buffer */
|
||
|
static int fvhff_match_uw(struct freedv_vhf_deframer * def,uint8_t bits[],int tol,int *rdiff, enum frame_payload_type *pt){
|
||
|
int frame_type = def->ftype;
|
||
|
int bitptr = def->bitptr;
|
||
|
int frame_size = def->frame_size;
|
||
|
int uw_len = def->uw_size;
|
||
|
int iuw,ibit;
|
||
|
const uint8_t * uw[2];
|
||
|
int uw_offset;
|
||
|
int diff[2] = { 0, 0 };
|
||
|
int i;
|
||
|
int match[2];
|
||
|
int r;
|
||
|
|
||
|
/* defaults to make compiler happy on -O3 */
|
||
|
|
||
|
*pt = FRAME_PAYLOAD_TYPE_VOICE;
|
||
|
*rdiff = 0;
|
||
|
|
||
|
/* Set up parameters for the standard type of frame */
|
||
|
if(frame_type == FREEDV_VHF_FRAME_A){
|
||
|
uw[0] = A_uw_v;
|
||
|
uw[1] = A_uw_d;
|
||
|
uw_len = 16;
|
||
|
uw_offset = 40;
|
||
|
} else if(frame_type == FREEDV_HF_FRAME_B){
|
||
|
uw[0] = B_uw_v;
|
||
|
uw[1] = B_uw_d;
|
||
|
uw_len = 8;
|
||
|
uw_offset = 0;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Check both the voice and data UWs */
|
||
|
for (i = 0; i < 2; i++) {
|
||
|
/* Start bit pointer where UW should be */
|
||
|
ibit = bitptr + uw_offset;
|
||
|
if(ibit >= frame_size) ibit -= frame_size;
|
||
|
/* Walk through and match bits in frame with bits of UW */
|
||
|
for(iuw=0; iuw<uw_len; iuw++){
|
||
|
if(bits[ibit] != uw[i][iuw]) diff[i]++;
|
||
|
ibit++;
|
||
|
if(ibit >= frame_size) ibit = 0;
|
||
|
}
|
||
|
match[i] = diff[i] <= tol;
|
||
|
}
|
||
|
/* Pick the best matching UW */
|
||
|
|
||
|
if (diff[0] < diff[1]) {
|
||
|
r = match[0];
|
||
|
*rdiff = diff[0];
|
||
|
*pt = FRAME_PAYLOAD_TYPE_VOICE;
|
||
|
} else {
|
||
|
r = match[1];
|
||
|
*rdiff = diff[1];
|
||
|
*pt = FRAME_PAYLOAD_TYPE_DATA;
|
||
|
}
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
static void fvhff_extract_frame_voice(struct freedv_vhf_deframer * def,uint8_t bits[],
|
||
|
uint8_t codec2_out[],uint8_t proto_out[],uint8_t vc_out[]){
|
||
|
int frame_type = def->ftype;
|
||
|
int bitptr = def->bitptr;
|
||
|
int frame_size = def->frame_size;
|
||
|
int iframe,ibit;
|
||
|
|
||
|
if(frame_type == FREEDV_VHF_FRAME_A){
|
||
|
/* Extract codec2 bits */
|
||
|
memset(codec2_out,0,7);
|
||
|
ibit = 0;
|
||
|
/* Extract and pack first half, MSB first */
|
||
|
iframe = bitptr+16;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<24;ibit++){
|
||
|
codec2_out[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
|
||
|
/* Extract and pack last half, MSB first */
|
||
|
iframe = bitptr+56;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<52;ibit++){
|
||
|
codec2_out[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
/* Extract varicode bits, if wanted */
|
||
|
if(vc_out!=NULL){
|
||
|
iframe = bitptr+90;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
vc_out[0] = bits[iframe];
|
||
|
iframe++;
|
||
|
vc_out[1] = bits[iframe];
|
||
|
}
|
||
|
/* Extract protocol bits, if proto is passed through */
|
||
|
if(proto_out!=NULL){
|
||
|
/* Clear protocol bit array */
|
||
|
memset(proto_out,0,3);
|
||
|
ibit = 0;
|
||
|
/* Extract and pack first half, MSB first */
|
||
|
iframe = bitptr+4;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<12;ibit++){
|
||
|
proto_out[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
|
||
|
/* Extract and pack last half, MSB first */
|
||
|
iframe = bitptr+84;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<20;ibit++){
|
||
|
proto_out[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}else if(frame_type == FREEDV_HF_FRAME_B){
|
||
|
/* Pointers to both c2 frames */
|
||
|
uint8_t * codec2_out1 = &codec2_out[0];
|
||
|
uint8_t * codec2_out2 = &codec2_out[4];
|
||
|
|
||
|
/* Extract codec2 bits */
|
||
|
memset(codec2_out,0,8);
|
||
|
ibit = 0;
|
||
|
|
||
|
/* Extract and pack first c2 frame, MSB first */
|
||
|
iframe = bitptr+8;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<28;ibit++){
|
||
|
codec2_out1[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
|
||
|
/* Extract and pack second c2 frame, MSB first */
|
||
|
iframe = bitptr+36;
|
||
|
ibit = 0;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<28;ibit++){
|
||
|
codec2_out2[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
}else if(frame_type == FREEDV_VHF_FRAME_AT){
|
||
|
/* Extract codec2 bits */
|
||
|
memset(codec2_out,0,7);
|
||
|
ibit = 0;
|
||
|
/* Extract and pack first half, MSB first */
|
||
|
iframe = bitptr+12;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<24;ibit++){
|
||
|
codec2_out[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
|
||
|
/* Extract and pack last half, MSB first */
|
||
|
iframe = bitptr+52;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<52;ibit++){
|
||
|
codec2_out[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
/* Extract varicode bits, if wanted */
|
||
|
if(vc_out!=NULL){
|
||
|
iframe = bitptr+86;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
vc_out[0] = bits[iframe];
|
||
|
iframe++;
|
||
|
vc_out[1] = bits[iframe];
|
||
|
}
|
||
|
/* Extract protocol bits, if proto is passed through */
|
||
|
if(proto_out!=NULL){
|
||
|
/* Clear protocol bit array */
|
||
|
memset(proto_out,0,3);
|
||
|
ibit = 0;
|
||
|
/* Extract and pack first half, MSB first */
|
||
|
iframe = bitptr+4;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<12;ibit++){
|
||
|
proto_out[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
|
||
|
/* Extract and pack last half, MSB first */
|
||
|
iframe = bitptr+84;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<20;ibit++){
|
||
|
proto_out[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void fvhff_extract_frame_data(struct freedv_vhf_deframer * def,uint8_t bits[]){
|
||
|
int frame_type = def->ftype;
|
||
|
int bitptr = def->bitptr;
|
||
|
int frame_size = def->frame_size;
|
||
|
int iframe,ibit;
|
||
|
|
||
|
if(frame_type == FREEDV_VHF_FRAME_A){
|
||
|
uint8_t data[8];
|
||
|
int end_bits = 0;
|
||
|
int from_bit;
|
||
|
int bcast_bit;
|
||
|
|
||
|
iframe = bitptr+4;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
from_bit = bits[iframe];
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
bcast_bit = bits[iframe];
|
||
|
|
||
|
/* Extract data bits */
|
||
|
memset(data,0,8);
|
||
|
ibit = 0;
|
||
|
/* Extract and pack first half, MSB first */
|
||
|
iframe = bitptr+8;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<32;ibit++){
|
||
|
data[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
|
||
|
/* Extract and pack last half, MSB first */
|
||
|
iframe = bitptr+56;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<64;ibit++){
|
||
|
data[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
|
||
|
/* Extract endbits value, MSB first*/
|
||
|
iframe = bitptr+88;
|
||
|
ibit = 0;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<4;ibit++){
|
||
|
end_bits |= (bits[iframe]&0x1)<<(3-(ibit));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
|
||
|
if (def->fdc) {
|
||
|
freedv_data_channel_rx_frame(def->fdc, data, 8, from_bit, bcast_bit, 0, end_bits);
|
||
|
}
|
||
|
} else if(frame_type == FREEDV_HF_FRAME_B){
|
||
|
uint8_t data[6];
|
||
|
int end_bits = 0;
|
||
|
int from_bit;
|
||
|
int bcast_bit;
|
||
|
int crc_bit;
|
||
|
|
||
|
ibit = 0;
|
||
|
memset(data,0,6);
|
||
|
|
||
|
/* Extract and pack first c2 frame, MSB first */
|
||
|
iframe = bitptr+8;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<48;ibit++){
|
||
|
data[ibit>>3] |= (bits[iframe]&0x1)<<(7-(ibit&0x7));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
|
||
|
iframe = bitptr+56;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
from_bit = bits[iframe];
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
bcast_bit = bits[iframe];
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
crc_bit = bits[iframe];
|
||
|
|
||
|
/* Extract endbits value, MSB first*/
|
||
|
iframe = bitptr+60;
|
||
|
ibit = 0;
|
||
|
if(iframe >= frame_size) iframe-=frame_size;
|
||
|
for(;ibit<4;ibit++){
|
||
|
end_bits |= (bits[iframe]&0x1)<<(3-(ibit));
|
||
|
iframe++;
|
||
|
if(iframe >= frame_size) iframe=0;
|
||
|
}
|
||
|
|
||
|
if (def->fdc) {
|
||
|
freedv_data_channel_rx_frame(def->fdc, data, 6, from_bit, bcast_bit, crc_bit, end_bits);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void fvhff_extract_frame(struct freedv_vhf_deframer * def,uint8_t bits[],uint8_t codec2_out[],
|
||
|
uint8_t proto_out[],uint8_t vc_out[],enum frame_payload_type pt){
|
||
|
switch (pt) {
|
||
|
case FRAME_PAYLOAD_TYPE_VOICE:
|
||
|
fvhff_extract_frame_voice(def, bits, codec2_out, proto_out, vc_out);
|
||
|
break;
|
||
|
case FRAME_PAYLOAD_TYPE_DATA:
|
||
|
fvhff_extract_frame_data(def, bits);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Try to find the UW and extract codec/proto/vc bits in def->frame_size bits
|
||
|
*/
|
||
|
int fvhff_deframe_bits(struct freedv_vhf_deframer * def,uint8_t codec2_out[],uint8_t proto_out[],
|
||
|
uint8_t vc_out[],uint8_t bits_in[]){
|
||
|
uint8_t * strbits = def->bits;
|
||
|
uint8_t * invbits = def->invbits;
|
||
|
uint8_t * bits;
|
||
|
int on_inv_bits = def->on_inv_bits;
|
||
|
int frame_type = def->ftype;
|
||
|
int state = def->state;
|
||
|
int bitptr = def->bitptr;
|
||
|
int last_uw = def->last_uw;
|
||
|
int miss_cnt = def->miss_cnt;
|
||
|
int frame_size = def->frame_size;
|
||
|
int uw_size = def->uw_size;
|
||
|
int uw_diff;
|
||
|
int i;
|
||
|
int uw_first_tol;
|
||
|
int uw_sync_tol;
|
||
|
int miss_tol;
|
||
|
int extracted_frame = 0;
|
||
|
enum frame_payload_type pt = FRAME_PAYLOAD_TYPE_VOICE;
|
||
|
|
||
|
/* Possibly set up frame-specific params here */
|
||
|
if(frame_type == FREEDV_VHF_FRAME_A){
|
||
|
uw_first_tol = 1; /* The UW bit-error tolerance for the first frame */
|
||
|
uw_sync_tol = 3; /* The UW bit error tolerance for frames after sync */
|
||
|
miss_tol = 4; /* How many UWs may be missed before going into the de-synced state */
|
||
|
}else if(frame_type == FREEDV_HF_FRAME_B){
|
||
|
uw_first_tol = 0; /* The UW bit-error tolerance for the first frame */
|
||
|
uw_sync_tol = 1; /* The UW bit error tolerance for frames after sync */
|
||
|
miss_tol = 3; /* How many UWs may be missed before going into the de-synced state */
|
||
|
}else{
|
||
|
return 0;
|
||
|
}
|
||
|
/* Skip N bits for multi-bit symbol modems */
|
||
|
for(i=0; i<frame_size; i++){
|
||
|
/* Put a bit in the buffer */
|
||
|
strbits[bitptr] = bits_in[i];
|
||
|
/* If we're checking the inverted bitstream, put a bit in it */
|
||
|
if(invbits!=NULL)
|
||
|
invbits[bitptr] = bits_in[i]?0:1;
|
||
|
|
||
|
bitptr++;
|
||
|
if(bitptr >= frame_size) bitptr -= frame_size;
|
||
|
def->bitptr = bitptr;
|
||
|
/* Enter state machine */
|
||
|
if(state==ST_SYNC){
|
||
|
/* Already synchronized, just wait till UW is back where it should be */
|
||
|
last_uw++;
|
||
|
if(invbits!=NULL){
|
||
|
if(on_inv_bits)
|
||
|
bits = invbits;
|
||
|
else
|
||
|
bits = strbits;
|
||
|
}else{
|
||
|
bits=strbits;
|
||
|
}
|
||
|
/* UW should be here. We're sunk, so deframe anyway */
|
||
|
if(last_uw == frame_size){
|
||
|
last_uw = 0;
|
||
|
|
||
|
if(!fvhff_match_uw(def,bits,uw_sync_tol,&uw_diff, &pt))
|
||
|
miss_cnt++;
|
||
|
else
|
||
|
miss_cnt=0;
|
||
|
|
||
|
/* If we go over the miss tolerance, go into no-sync */
|
||
|
if(miss_cnt>miss_tol){
|
||
|
state = ST_NOSYNC;
|
||
|
}
|
||
|
/* Extract the bits */
|
||
|
extracted_frame = 1;
|
||
|
fvhff_extract_frame(def,bits,codec2_out,proto_out,vc_out,pt);
|
||
|
|
||
|
/* Update BER estimate */
|
||
|
def->ber_est = (.995*def->ber_est) + (.005*((float)uw_diff)/((float)uw_size));
|
||
|
def->total_uw_bits += uw_size;
|
||
|
def->total_uw_err += uw_diff;
|
||
|
}
|
||
|
/* Not yet sunk */
|
||
|
}else{
|
||
|
/* It's a sync!*/
|
||
|
if(invbits!=NULL){
|
||
|
if(fvhff_match_uw(def,invbits,uw_first_tol, &uw_diff, &pt)){
|
||
|
state = ST_SYNC;
|
||
|
last_uw = 0;
|
||
|
miss_cnt = 0;
|
||
|
extracted_frame = 1;
|
||
|
on_inv_bits = 1;
|
||
|
fvhff_extract_frame(def,invbits,codec2_out,proto_out,vc_out,pt);
|
||
|
/* Update BER estimate */
|
||
|
def->ber_est = (.995*def->ber_est) + (.005*((float)uw_diff)/((float)uw_size));
|
||
|
def->total_uw_bits += uw_size;
|
||
|
def->total_uw_err += uw_diff;
|
||
|
}
|
||
|
}
|
||
|
if(fvhff_match_uw(def,strbits,uw_first_tol, &uw_diff, &pt)){
|
||
|
state = ST_SYNC;
|
||
|
last_uw = 0;
|
||
|
miss_cnt = 0;
|
||
|
extracted_frame = 1;
|
||
|
on_inv_bits = 0;
|
||
|
fvhff_extract_frame(def,strbits,codec2_out,proto_out,vc_out,pt);
|
||
|
/* Update BER estimate */
|
||
|
def->ber_est = (.995*def->ber_est) + (.005*((float)uw_diff)/((float)uw_size));
|
||
|
def->total_uw_bits += uw_size;
|
||
|
def->total_uw_err += uw_diff;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
def->state = state;
|
||
|
def->last_uw = last_uw;
|
||
|
def->miss_cnt = miss_cnt;
|
||
|
def->on_inv_bits = on_inv_bits;
|
||
|
/* return zero for data frames, they are already handled by callback */
|
||
|
return extracted_frame && pt == FRAME_PAYLOAD_TYPE_VOICE;
|
||
|
}
|
||
|
|
||
|
} // FreeDV
|