From 3abd0979d8151dfe3475c69eaab7b900411681d7 Mon Sep 17 00:00:00 2001 From: DL1YCF Date: Mon, 5 Dec 2022 19:49:52 +0100 Subject: [PATCH] Switched to a new sine generator for side tone --- old_protocol.c | 34 +---------------- old_protocol.h | 3 +- transmitter.c | 100 +++++++++++++++++++++++++++++++++---------------- transmitter.h | 2 + 4 files changed, 72 insertions(+), 67 deletions(-) diff --git a/old_protocol.c b/old_protocol.c index 9d67947..548b1bb 100644 --- a/old_protocol.c +++ b/old_protocol.c @@ -1297,14 +1297,7 @@ void old_protocol_audio_samples(RECEIVER *rx,short left_audio_sample,short right } } -// -// This is a copy of old_protocol_iq_samples, -// but it includes the possibility to send a side tone -// We use it to provide a side-tone for CW/TUNE, in -// all other cases side==0 and this routine then is -// fully equivalent to old_protocol_iq_samples. -// -void old_protocol_iq_samples_with_sidetone(int isample, int qsample, int side) { +void old_protocol_iq_samples(int isample, int qsample, int side) { if(isTransmitting()) { pthread_mutex_lock(&send_audio_mutex); if (!txring_flag) { @@ -1336,31 +1329,6 @@ void old_protocol_iq_samples_with_sidetone(int isample, int qsample, int side) { } } -void old_protocol_iq_samples(int isample,int qsample) { - if(isTransmitting()) { - pthread_mutex_lock(&send_audio_mutex); - if (!txring_flag) { - // - // First time we arrive here after a RX->TX transition: - // Clear TX IQ ring buffer so the amples will be sent - // as soon as possible. - // - txring_flag=1; - txring_inptr = txring_outptr = 0; - } - TXRINGBUF[txring_inptr++]=0; - TXRINGBUF[txring_inptr++]=0; - TXRINGBUF[txring_inptr++]=0; - TXRINGBUF[txring_inptr++]=0; - TXRINGBUF[txring_inptr++]=isample >> 8; - TXRINGBUF[txring_inptr++]=isample; - TXRINGBUF[txring_inptr++]=qsample >> 8; - TXRINGBUF[txring_inptr++]=qsample; - if (txring_inptr >= TXRINGBUFLEN) txring_inptr=0; - pthread_mutex_unlock(&send_audio_mutex); - } -} - void ozy_send_buffer() { diff --git a/old_protocol.h b/old_protocol.h index 92832a0..23feedb 100644 --- a/old_protocol.h +++ b/old_protocol.h @@ -27,6 +27,5 @@ extern void old_protocol_init(int rx,int pixels,int rate); extern void old_protocol_set_mic_sample_rate(int rate); extern void old_protocol_audio_samples(RECEIVER *rx,short left_audio_sample,short right_audio_sample); -extern void old_protocol_iq_samples(int isample,int qsample); -extern void old_protocol_iq_samples_with_sidetone(int isample,int qsample,int side); +extern void old_protocol_iq_samples(int isample,int qsample,int side); #endif diff --git a/transmitter.c b/transmitter.c index 84ac13e..e3a4ba0 100644 --- a/transmitter.c +++ b/transmitter.c @@ -51,9 +51,7 @@ #include "audio.h" #include "ext.h" #include "sliders.h" - -double getNextSideToneSample(); -double getNextInternalSideToneSample(); +#include "sintab.h" #define min(x,y) (xiq_output_buffer is not used. Therefore, all the // SetTXAPostGen functions are not needed for CW! - // - // In the new protocol, we just put "silence" into the TX IQ buffer + // + // "Side tone to radio" treatment: + // old protocol: done HERE + // new protocol: already done in add_mic_sample + // soapy : no audio to radio // switch (protocol) { case ORIGINAL_PROTOCOL: @@ -1284,8 +1290,8 @@ static void full_tx_buffer(TRANSMITTER *tx) { for(j=0;joutput_samples;j++) { ramp=cw_shape_buffer48[j]; // between 0.0 and 1.0 qsample=floor(gain*ramp+0.5); // always non-negative, isample is just the pulse envelope - sidetone=sidevol * ramp * getNextInternalSideToneSample(); - old_protocol_iq_samples_with_sidetone(isample,qsample,sidetone); + sidetone=sidevol * ramp * sine_generator(&p1radio, &p2radio, cw_keyer_sidetone_frequency); + old_protocol_iq_samples(isample,qsample,sidetone); } break; case NEW_PROTOCOL: @@ -1325,7 +1331,7 @@ static void full_tx_buffer(TRANSMITTER *tx) { qsample=qs>=0.0?(long)floor(qs*gain+0.5):(long)ceil(qs*gain-0.5); switch(protocol) { case ORIGINAL_PROTOCOL: - old_protocol_iq_samples(isample,qsample); + old_protocol_iq_samples(isample,qsample,0); break; case NEW_PROTOCOL: new_protocol_iq_samples(isample,qsample); @@ -1403,7 +1409,7 @@ void add_mic_sample(TRANSMITTER *tx,float mic_sample) { // store the ramp value in cw_shape_buffer, but also use it for shaping the "local" // side tone ramp=cwramp48[cw_shape]; - cwsample=0.00197 * getNextSideToneSample() * cw_keyer_sidetone_volume * ramp; + cwsample=0.00197 * cw_keyer_sidetone_volume * ramp * sine_generator(&p1local, &p2local, cw_keyer_sidetone_frequency); if(active_receiver->local_audio && cw_keyer_sidetone_volume > 0) cw_audio_write(active_receiver,cwsample); cw_shape_buffer48[tx->samples]=ramp; // @@ -1419,7 +1425,9 @@ void add_mic_sample(TRANSMITTER *tx,float mic_sample) { // if (protocol == NEW_PROTOCOL) { s=0; - if (!cw_keyer_internal || CAT_cw_is_active) s=(int) (cwsample * 32767.0); + // cwsample is in the range 0.0 - 0.25. For my Anan-7000, the following scaling + // produces the same volume as "internal CW". + if (!cw_keyer_internal || CAT_cw_is_active) s=(int) (cwsample * 65535.0); new_protocol_cw_audio_samples(s, s); s=4*cw_shape; i=4*tx->samples; @@ -1665,24 +1673,52 @@ void tx_set_ps_sample_rate(TRANSMITTER *tx,int rate) { #endif } -// Sine tone generator: -// somewhat improved, and provided two siblings -// for generating side tones simultaneously on the -// HPSDR board and local audio. - -#define TWOPIOVERSAMPLERATE 0.0001308996938995747; // 2 Pi / 48000 - -static long asteps = 0; -static long bsteps = 0; - -double getNextSideToneSample() { - double angle = (asteps*cw_keyer_sidetone_frequency)*TWOPIOVERSAMPLERATE; - if (++asteps == 48000) asteps = 0; - return sin(angle); -} - -double getNextInternalSideToneSample() { - double angle = (bsteps*cw_keyer_sidetone_frequency)*TWOPIOVERSAMPLERATE; - if (++bsteps == 48000) bsteps = 0; - return sin(angle); +/////////////////////////////////////////////////////////////////////////// +// Sine tone generator based on phase words that are +// passed as an argument. The phase (value 0-256) is encoded in +// two integers (in the range 0-255) as +// +// phase = p1 + p2/256 +// +// and the sine value is obtained from the table by linear +// interpolateion +// +// sine := sintab[p1] + p2*(sintab(p1+1)-sintab(p2))/256.0 +// +// and the phase word is updated, depending on the frequency f, as +// +// p1 := p1 + (256*f)/48000 +// p2 := p2 + (256*f)%48000 +// +/////////////////////////////////////////////////////////////////////////// +// The idea of this sine generator is +// - it does not depend on an external sin function +// - it does not do much floating point +// - many sine generators can run in parallel, with their "own" +// phase words and frequency +// - the phase is always continuous, even if there are frequency jumps +/////////////////////////////////////////////////////////////////////////// + +float sine_generator(int *phase1, int *phase2, int freq) { + register float val,s,d; + register int p1=*phase1; + register int p2=*phase2; + register int32_t f256=freq*256; // so we know 256*freq won't overflow + s=sintab[p1]; + d=sintab[p1+1]-s; + val = s + p2*d*0.00390625; // 1/256 + p1 += f256 / 48000; + p2 += ((f256 % 48000)*256)/48000; + // correct overflows in fractional and integer phase to keep + // p1,p2 within bounds + if (p2 > 255) { + p2 -= 256; + p1++; + } + if (p1 > 255) { + p1 -=256; + } + *phase1=p1; + *phase2=p2; + return val; } diff --git a/transmitter.h b/transmitter.h index fb5fc90..897f23b 100644 --- a/transmitter.h +++ b/transmitter.h @@ -153,6 +153,8 @@ extern void tx_set_ps_sample_rate(TRANSMITTER *tx,int rate); extern void add_ps_iq_samples(TRANSMITTER *tx, double i_sample_0,double q_sample_0, double i_sample_1, double q_sample_1); extern void cw_hold_key(int state); + +extern float sine_generator(int *p1, int *p2, int freq); #endif -- 2.45.2