/* FSK audio processing (coherent FSK modem)
*
* ( C ) 2017 by Andreas Eversberg < jolly @ eversberg . eu >
* All Rights Reserved
*
* 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 <stdio.h>
# include <stdint.h>
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include <math.h>
# include "../libsample/sample.h"
# include "../libdebug/debug.h"
# include "fsk.h"
# define PI M_PI
/*
* fsk = instance of fsk modem
* inst = instance of user
* send_bit ( ) = function to be called whenever a new bit has to be sent
* receive_bit ( ) = function to be called whenever a new bit was received
* samplerate = samplerate
* bitrate = bits per second
* f0 , f1 = two frequencies for bit 0 and bit 1
* level = level to modulate the frequencies
* coherent = use coherent modulation ( FFSK )
* bitadjust = how much to adjust the sample clock when a bitchange was detected . ( 0 = nothing , don ' t use this , 0.5 full adjustment )
*/
int fsk_init ( fsk_t * fsk , void * inst , int ( * send_bit ) ( void * inst ) , void ( * receive_bit ) ( void * inst , int bit , double quality , double level ) , int samplerate , double bitrate , double f0 , double f1 , double level , int coherent , double bitadjust )
{
double bandwidth ;
int i ;
int rc ;
PDEBUG ( DDSP , DEBUG_DEBUG , " Setup FSK for Transceiver. (F0 = %.1f, F1 = %.1f, peak = %.1f) \n " , f0 , f1 , level ) ;
memset ( fsk , 0 , sizeof ( * fsk ) ) ;
/* gen sine table with deviation */
fsk - > sin_tab = calloc ( 65536 + 16384 , sizeof ( * fsk - > sin_tab ) ) ;
if ( ! fsk - > sin_tab ) {
fprintf ( stderr , " No mem! \n " ) ;
rc = - ENOMEM ;
goto error ;
}
for ( i = 0 ; i < 65536 ; i + + )
fsk - > sin_tab [ i ] = sin ( ( double ) i / 65536.0 * 2.0 * PI ) * level ;
fsk - > inst = inst ;
fsk - > rx_bit = - 1 ;
fsk - > rx_bitadjust = bitadjust ;
fsk - > receive_bit = receive_bit ;
fsk - > tx_bit = - 1 ;
fsk - > level = level ;
fsk - > send_bit = send_bit ;
fsk - > f0_deviation = ( f0 - f1 ) / 2.0 ;
fsk - > f1_deviation = ( f1 - f0 ) / 2.0 ;
if ( f0 < f1 ) {
fsk - > low_bit = 0 ;
fsk - > high_bit = 1 ;
} else {
fsk - > low_bit = 1 ;
fsk - > high_bit = 0 ;
}
/* calculate bandwidth */
bandwidth = fabs ( f0 - f1 ) * 2.0 ;
/* init fm demodulator */
rc = fm_demod_init ( & fsk - > demod , ( double ) samplerate , ( f0 + f1 ) / 2.0 , bandwidth ) ;
if ( rc < 0 )
goto error ;
fsk - > bits_per_sample = ( double ) bitrate / ( double ) samplerate ;
PDEBUG ( DDSP , DEBUG_DEBUG , " Bitduration of %.4f bits per sample @ %d. \n " , fsk - > bits_per_sample , samplerate ) ;
fsk - > phaseshift65536 [ 0 ] = f0 / ( double ) samplerate * 65536.0 ;
PDEBUG ( DDSP , DEBUG_DEBUG , " phaseshift65536[0] = %.4f \n " , fsk - > phaseshift65536 [ 0 ] ) ;
fsk - > phaseshift65536 [ 1 ] = f1 / ( double ) samplerate * 65536.0 ;
PDEBUG ( DDSP , DEBUG_DEBUG , " phaseshift65536[1] = %.4f \n " , fsk - > phaseshift65536 [ 1 ] ) ;
/* use coherent modulation, i.e. each bit has an integer number of
* half waves and starts / ends at zero crossing
*/
if ( coherent ) {
double waves ;
fsk - > coherent = 1 ;
waves = ( f0 / bitrate ) ;
if ( fabs ( round ( waves * 2 ) - ( waves * 2 ) ) > 0.001 ) {
fprintf ( stderr , " Failed to set coherent mode, half waves of F0 does not fit exactly into one bit, please fix! \n " ) ;
abort ( ) ;
}
fsk - > cycles_per_bit65536 [ 0 ] = waves * 65536.0 ;
waves = ( f1 / bitrate ) ;
if ( fabs ( round ( waves * 2 ) - ( waves * 2 ) ) > 0.001 ) {
fprintf ( stderr , " Failed to set coherent mode, half waves of F1 does not fit exactly into one bit, please fix! \n " ) ;
abort ( ) ;
}
fsk - > cycles_per_bit65536 [ 1 ] = waves * 65536.0 ;
}
return 0 ;
error :
fsk_cleanup ( fsk ) ;
return rc ;
}
/* Cleanup transceiver instance. */
void fsk_cleanup ( fsk_t * fsk )
{
PDEBUG ( DDSP , DEBUG_DEBUG , " Cleanup FSK for Transceiver. \n " ) ;
if ( fsk - > sin_tab ) {
free ( fsk - > sin_tab ) ;
fsk - > sin_tab = NULL ;
}
fm_demod_exit ( & fsk - > demod ) ;
}
//#define DEBUG_MODULATOR
//#define DEBUG_FILTER
/* Demodulates bits
*
* If bit is received , callback function send_bit ( ) is called .
*
* We sample each bit 0.5 bits after polarity change .
*
* If we have a bit change , adjust sample counter towards one half bit duration .
* We may have noise , so the bit change may be wrong or not at the correct place .
* This can cause bit slips .
* Therefore we change the sample counter only slightly , so bit slips may not
* happen so quickly .
*/
void fsk_receive ( fsk_t * fsk , sample_t * sample , int length )
{
sample_t I [ length ] , Q [ length ] , frequency [ length ] , f ;
int i ;
int bit ;
double level , quality ;
/* demod samples to offset around center frequency */
fm_demodulate_real ( & fsk - > demod , frequency , length , sample , I , Q ) ;
for ( i = 0 ; i < length ; i + + ) {
f = frequency [ i ] ;
if ( f < 0 )
bit = fsk - > low_bit ;
else
bit = fsk - > high_bit ;
# ifdef DEBUG_FILTER
printf ( " |%s| %.3f \n " , debug_amplitude ( f / fabs ( fsk - > f0_deviation ) ) , f / fabs ( fsk - > f0_deviation ) ) ;
# endif
if ( fsk - > rx_bit ! = bit ) {
# ifdef DEBUG_FILTER
puts ( " bit change " ) ;
# endif
fsk - > rx_bit = bit ;
if ( fsk - > rx_bitpos < 0.5 ) {
fsk - > rx_bitpos + = fsk - > rx_bitadjust ;
if ( fsk - > rx_bitpos > 0.5 )
fsk - > rx_bitpos = 0.5 ;
} else
if ( fsk - > rx_bitpos > 0.5 ) {
fsk - > rx_bitpos - = fsk - > rx_bitadjust ;
if ( fsk - > rx_bitpos < 0.5 )
fsk - > rx_bitpos = 0.5 ;
}
}
/* if bit counter reaches 1, we subtract 1 and sample the bit */
if ( fsk - > rx_bitpos > = 1.0 ) {
/* peak level is the length of I/Q vector
* since we filter out the unwanted modulation product , the vector is only half of length */
level = sqrt ( I [ i ] * I [ i ] + Q [ i ] * Q [ i ] ) * 2.0 ;
/* quality is defined on how accurat the target frequency it hit
* if it is hit close to the center or close to double deviation from center , quality is close to 0 */
if ( bit = = 0 )
quality = 1.0 - fabs ( ( f - fsk - > f0_deviation ) / fsk - > f0_deviation ) ;
else
quality = 1.0 - fabs ( ( f - fsk - > f1_deviation ) / fsk - > f1_deviation ) ;
if ( quality < 0 )
quality = 0 ;
# ifdef DEBUG_FILTER
printf ( " sample (level=%.3f, quality=%.3f) \n " , level / fsk - > level , quality ) ;
# endif
/* adjust the values, because this is best we can get from fm demodulator */
fsk - > receive_bit ( fsk - > inst , bit , quality / 0.95 , level ) ;
fsk - > rx_bitpos - = 1.0 ;
}
fsk - > rx_bitpos + = fsk - > bits_per_sample ;
}
}
/* modulate bits
*
* If first / next bit is required , callback function send_bit ( ) is called .
* If there is no ( more ) data to be transmitted , the callback functions shall
* return - 1. In this case , this function stops and returns the number of
* samples that have been rendered so far , if any .
*
* For coherent mode ( FSK ) , we round the phase on every bit change to the
* next zero crossing . This prevents phase shifts due to rounding errors .
*/
int fsk_send ( fsk_t * fsk , sample_t * sample , int length , int add )
{
int count = 0 ;
double phase , phaseshift ;
phase = fsk - > tx_phase65536 ;
/* get next bit */
if ( fsk - > tx_bit < 0 ) {
next_bit :
fsk - > tx_bit = fsk - > send_bit ( fsk - > inst ) ;
# ifdef DEBUG_MODULATOR
printf ( " bit change to %d \n " , fsk - > tx_bit ) ;
# endif
if ( fsk - > tx_bit < 0 )
goto done ;
/* correct phase when changing bit */
if ( fsk - > coherent ) {
/* round phase to nearest zero crossing */
if ( phase > 16384.0 & & phase < 49152.0 )
phase = 32768.0 ;
else
phase = 0 ;
/* set phase according to current position in bit */
phase + = fsk - > tx_bitpos * fsk - > cycles_per_bit65536 [ fsk - > tx_bit & 1 ] ;
# ifdef DEBUG_MODULATOR
printf ( " phase %.3f bitpos=%.6f \n " , phase , fsk - > tx_bitpos ) ;
# endif
}
}
/* modulate bit */
phaseshift = fsk - > phaseshift65536 [ fsk - > tx_bit & 1 ] ;
while ( count < length & & fsk - > tx_bitpos < 1.0 ) {
if ( add )
sample [ count + + ] + = fsk - > sin_tab [ ( uint16_t ) phase ] ;
else
sample [ count + + ] = fsk - > sin_tab [ ( uint16_t ) phase ] ;
# ifdef DEBUG_MODULATOR
printf ( " |%s| \n " , debug_amplitude ( fsk - > sin_tab [ ( uint16_t ) phase ] / fsk - > level ) ) ;
# endif
phase + = phaseshift ;
if ( phase > = 65536.0 )
phase - = 65536.0 ;
fsk - > tx_bitpos + = fsk - > bits_per_sample ;
}
if ( fsk - > tx_bitpos > = 1.0 ) {
fsk - > tx_bitpos - = 1.0 ;
goto next_bit ;
}
done :
fsk - > tx_phase65536 = phase ;
return count ;
}
/* reset transmitter state, so we get a clean start */
void fsk_tx_reset ( fsk_t * fsk )
{
fsk - > tx_phase65536 = 0 ;
fsk - > tx_bitpos = 0 ;
fsk - > tx_bit = - 1 ;
}