#include #include #include #include "globdef.h" #include "uidef.h" #include "fft1def.h" #include "fft2def.h" #include "fft3def.h" #include "sigdef.h" #include "screendef.h" #include "seldef.h" #include "thrdef.h" #include "txdef.h" #include "vernr.h" #include "hwaredef.h" #include "sdrdef.h" #include "keyboard_def.h" #define TX_DA_MARGIN 0.97 #define PILOT_TONE_CONTROL 0.00000000001 float tx_pilot_tone; float tx_output_amplitude; int txout_pa; float txout_fx; float *txout_waveform; int max_tx_cw_waveform_points; int tx_cw_waveform_points; int old_tone_key, old_hand_key, old_tot_key; int tone_key, tot_key; int tx_waveform_pointer; int old_rxtx_state; int rxtx_state; int rxtx_wait; void do_cw_keying(void) { int i, nn; float amplitude, pilot_ampl; float t1, r1, r2; tx_resample_ratio=new_tx_resample_ratio; // Find out how many points we should place in the output buffer. nn=(mic_key_pa-mic_key_px+mic_key_bufsiz)&mic_key_mask; r2=2/(tx_resample_ratio*tx_pre_resample); r1=nn/r2+txout_fx; nn=r1; txout_fx=r1-nn; t1=0; for(i=0; i 1) { t1-=1; if(tx_mic_key[mic_key_px] > 1) { tone_key=1; } else { tone_key=0; } mic_key_px=(mic_key_px+1)&mic_key_mask; } amplitude=txout_waveform[tx_waveform_pointer]; tx_pilot_tone*=-1; if(amplitude == 0) { pilot_ampl=0; } else { pilot_ampl=tx_pilot_tone; } if( ((hand_key&txcw.enable_hand_key)| (tone_key&txcw.enable_tone_key)) ) { if(tx_waveform_pointer < tx_cw_waveform_points-1)tx_waveform_pointer++; } else { // if(amplitude < PILOT_TONE_CONTROL)pilot_ampl=0; pilot_ampl=0; if(tx_waveform_pointer > 0)tx_waveform_pointer--; } tx_output_phase+=tx_output_phstep; if(tx_output_phase > PI_L)tx_output_phase-=2*PI_L; if(tx_output_phase < -PI_L)tx_output_phase+=2*PI_L; amplitude*=tx_output_amplitude; txout[2*txout_pa ]=amplitude*cos(tx_output_phase)+pilot_ampl; txout[2*txout_pa+1]=-amplitude*sin(tx_output_phase)-pilot_ampl; txout_pa++; if(txout_pa >= txout_sizhalf) { if( (hand_key|tone_key) ) { rxtx_state=TRUE; } else { rxtx_state=FALSE; } if(old_rxtx_state == FALSE && rxtx_state == TRUE) { hware_set_rxtx(TRUE); old_rxtx_state=TRUE; } if(rxtx_state == TRUE) rxtx_wait=16+lir_tx_output_samples()/txout_sizhalf; if(old_rxtx_state == TRUE && rxtx_state == FALSE) { rxtx_wait--; if(rxtx_wait == 0) { old_rxtx_state=FALSE; hware_set_rxtx(FALSE); } } tx_wsamps=lir_tx_input_samples()/2; /* k=(mictimf_pa-mictimf_px+mictimf_bufsiz)&mictimf_mask; k+=(micfft_pa-micfft_px+micfft_bufsiz)&micfft_mask; k+=(cliptimf_pa-cliptimf_px+micfft_bufsiz)&micfft_mask; j=(clipfft_pa-clipfft_px+alc_bufsiz)&alc_mask; j+=(alctimf_pa-((int)(alctimf_fx))+4+alc_bufsiz)&alc_mask; j+=alc_fftsize; k+=j/tx_pre_resample; */ tx_send_to_da(); txout_pa=0; } } } void tx_send_to_da(void) { int i; float t1, t2; switch(tx_output_mode) { case 0: for(i=0; ir2)r2=t2; } if(r2 > 0.01*MAX_DYNRANGE) { r2=1/sqrt(r2); } else { r2=0; } } else { r2=0; } tx_ssb_step4(r2,&t1,&prat); tx_ssb_step5(); while(clipfft_px != clipfft_pa) { tx_ssb_step6(&prat); } while( ((alctimf_pa-alctimf_pb+alc_bufsiz)&alc_mask) > 3*alc_fftsize) { tx_ssb_step7(&prat); } tx_ssb_step8(); } break; case MODE_WCW: case MODE_NCW: case MODE_HSMS: case MODE_QRSS: for(i=0; i<4; i++) lir_sem_wait(SEM_TXINPUT); while(!kill_all_flag && thread_command_flag[THREAD_TX_OUTPUT] == THRFLAG_ACTIVE) { lir_sem_wait(SEM_TXINPUT); do_cw_keying(); } break; } thread_status_flag[THREAD_TX_OUTPUT]=THRFLAG_RETURNED; while(thread_command_flag[THREAD_TX_OUTPUT] != THRFLAG_NOT_ACTIVE) { lir_sleep(1000); } } void cwproc_setup(void) { char color; char *onoff[]={"Disabled", "Enabled"}; char s[256]; int i, k, pa, default_cwproc_no; float t1, t2, t3; rx_mode=MODE_NCW; default_cwproc_no=tg.spproc_no; if(read_txpar_file()==FALSE) { set_default_cwproc_parms(); } tx_setup_flag=TRUE; open_tx_output(); open_tx_input(); if(kill_all_flag)return; init_txmem_cwproc(); if(kill_all_flag)return; lir_mutex_init(); lir_sleep(50000); linrad_thread_create(THREAD_TX_INPUT); restart:; if(txcw.rise_time < 1000*CW_MIN_TIME_CONSTANT) txcw.rise_time=1000*CW_MIN_TIME_CONSTANT; if(txcw.rise_time > 1000*CW_MAX_TIME_CONSTANT) txcw.rise_time=1000*CW_MAX_TIME_CONSTANT; old_tone_key=0; old_hand_key=0; old_tot_key=0; clear_screen(); lir_text(20,0,"Setup for CW keying."); lir_text(0,2,"There are three ways to produce Morse code in Linrad:"); if(txcw.enable_tone_key == 0)settextcolor(7); else settextcolor(14); sprintf(s,"Tone into the microphone input. (Fast keying) [%s]", onoff[txcw.enable_tone_key]); lir_text(0,4,s); if(txcw.enable_hand_key == 0)settextcolor(7); else settextcolor(14); sprintf(s,"Parallel port pin 13. (Hand keying) [%s]", onoff[txcw.enable_hand_key]); lir_text(0,5,s); if(txcw.enable_ascii == 0)settextcolor(7); else settextcolor(14); sprintf(s,"Computer generated from ascii on keyboard. [%s]", onoff[txcw.enable_ascii]); lir_text(0,6,s); lir_text(0,7,"Press 'T', 'P' or 'C' to toggle Enable/Disable"); sprintf(s,"Rise time = %.2f ms. ('A' to change)",txcw.rise_time*0.001); lir_text(0,9,s); make_txproc_filename(); sprintf(s,"Press 'S' to save %s, 'R' to read. '+' or '-' to change file no.", txproc_filename); lir_text(0,10,s); k=screen_last_col; if(k>255)k=255; for(i=0; i 10.0 and Max(key up) < 0.1."); sprintf(s, "Your Nyquist frequency is %.1f kHz so you should use a keying tone", 0.0005*ui.tx_ad_speed); lir_text(0,MIC_KEY_LINE+6,s); lir_text(0,MIC_KEY_LINE+7,"that is a little lower than this."); while(!kill_all_flag) { lir_sem_wait(SEM_TXINPUT); t1=0; t2=0; t3=BIG; i=0; pa=mic_key_px; while(pa != mic_key_pa) { i++; t1+=tx_mic_key[pa]; if(t2 < tx_mic_key[pa])t2=tx_mic_key[pa]; if(t3 > tx_mic_key[pa])t3=tx_mic_key[pa]; pa=(pa+1)&mic_key_mask; } if(i > 0) { t1/=i; sprintf(s,"%.3f",t1); lir_text(1,MIC_KEY_LINE+2,s); sprintf(s,"%.3f",t2); lir_text(12,MIC_KEY_LINE+2,s); sprintf(s,"%.3f",t3); lir_text(23,MIC_KEY_LINE+2,s); if(t1 < 1) { tone_key=0; } else { tone_key=1; } //#define KEY_INDICATOR_LINE 22 if(tone_key != old_tone_key) { old_tone_key=tone_key; if(tone_key == 0) { color=7; } else { color=12; } lir_fillbox(5*text_width, KEY_INDICATOR_LINE*text_height, 10*text_width, 3*text_height,color); lir_text(8,KEY_INDICATOR_LINE+1,"TONE"); } } hware_hand_key(); if(hand_key != old_hand_key) { old_hand_key=hand_key; if(hand_key == 0) { color=7; } else { color=12; } lir_fillbox(20*text_width, KEY_INDICATOR_LINE*text_height, 10*text_width, 3*text_height,color); lir_text(22,KEY_INDICATOR_LINE+1,"HAND"); } do_cw_keying(); test_keyboard(); if(lir_inkey != 0) { process_current_lir_inkey(); if(lir_inkey=='X')goto end_tx_setup; switch (lir_inkey) { case 'A': lir_text(0,TXCW_INPUT_LINE,"Rise time (ms):"); t1=lir_get_float(15,TXCW_INPUT_LINE,6, CW_MIN_TIME_CONSTANT,CW_MAX_TIME_CONSTANT); txcw.rise_time=1000*t1; break; case 'T': txcw.enable_tone_key^=1; txcw.enable_tone_key&=1; break; case 'H': txcw.enable_hand_key^=1; txcw.enable_hand_key&=1; break; case 'C': txcw.enable_ascii^=1; txcw.enable_ascii&=1; break; case 'S': clear_screen(); save_tx_parms(TRUE); break; case 'R': clear_screen(); read_txpar_file(); break; case '+': tg.spproc_no++; if(tg.spproc_no > MAX_CWPROC_FILES)tg.spproc_no=MAX_CWPROC_FILES; break; case '-': tg.spproc_no--; if(tg.spproc_no < 0)tg.spproc_no=0; break; } goto restart; } } end_tx_setup:; close_tx_output(); linrad_thread_stop_and_join(THREAD_TX_INPUT); close_tx_input(); free(txmem_handle); lir_mutex_destroy(); tg.spproc_no=default_cwproc_no; } void make_tx_phstep(void) { tx_output_phstep=2*PI_L*(tg_basebfreq*1000000+tx_ssbproc_fqshift)/ui.tx_da_speed; } void init_txmem_cwproc(void) { int i; micsize=ui.tx_ad_speed*.01; make_power_of_two(&micsize); micn=0; i=micsize; while(i>1) { i>>=1; micn++; } mic_sizhalf=micsize/2; mictimf_block=ui.tx_ad_channels*micsize; // Allow the input buffer to hold 80 milliseconds of data (or more) mictimf_bufsiz=16*mictimf_block; mictimf_mask=mictimf_bufsiz-1; tx_read_bytes=ui.tx_ad_bytes*mictimf_block; // Keying with a tone into the microphone input should // be done with a tone frequency near, but below the Nyquist frequency // which is ui.tx_ad_speed/2. // We do not want faster rise times than 1 ms for a dot length // of 2 ms minimum or a dot rate of 250 Hz maximum. // With at least 4 points on the rise time we need a sampling // rate of 4kHz. tx_pre_resample=1; i=1.05*ui.tx_ad_speed/2; mic_key_block=mictimf_block; while(i > 4000) { i/=2; mic_key_block/=2; tx_pre_resample*=2; } tx_resample_ratio=(float)(2*ui.tx_da_speed)/ui.tx_ad_speed; new_tx_resample_ratio=tx_resample_ratio; tx_output_upsample=1; mic_key_bufsiz=16*mic_key_block; mic_key_mask=mic_key_bufsiz-1; txout_fftsize=0.7*mic_key_block*tx_resample_ratio; make_power_of_two(&txout_fftsize); txout_sizhalf=txout_fftsize/2; max_tx_cw_waveform_points=ui.tx_da_speed*(0.0022*CW_MAX_TIME_CONSTANT); init_memalloc(txmem, MAX_TXMEM_ARRAYS); if(ui.tx_ad_bytes == 2) { mem(51,&mictimf_shi,mictimf_bufsiz*sizeof(short int),0); } else { mem(51,&mictimf_int,mictimf_bufsiz*sizeof(int),0); } mem(52,&tx_mic_key,mic_key_bufsiz*sizeof(float),0); mem(53,&txout,2*txout_fftsize*sizeof(float),0); mem(54,&txout_waveform,max_tx_cw_waveform_points*sizeof(float),0); if(ui.tx_da_bytes==4) { mem(54,&tx_out_int,ui.tx_da_channels*txout_fftsize*sizeof(int)/2,0); } else { mem(55,&tx_out_shi,ui.tx_da_channels*txout_fftsize*sizeof(int)/2,0); } txmem_size=memalloc((int*)(&txmem_handle),"txmem"); if(txmem_size == 0) { lirerr(1261); return; } tx_wrblock_no=0; tx_wttim_ref=0; tx_wttim_sum=0; tx_wttim_diff=0; tx_ssbproc_fqshift=0; make_tx_phstep(); tx_output_phase=0; make_tx_modes(); tx_output_totsamp=0; tx_output_totsamp=-lir_tx_output_samples(); make_tx_cw_waveform(); mic_key_pa=0; mic_key_px=0; mictimf_pa=0; mictimf_px=0; txout_pa=0; txout_fx=0; old_tone_key=-1; old_hand_key=-1; old_tot_key=-1; tx_waveform_pointer=0; tx_pilot_tone=pow(10.0,-0.05*ui.tx_pilot_tone_db); tx_output_amplitude=pow(10.0,-0.05*txcw.level_db); } void init_txmem_spproc(void) { int i, k; float t1; // Speech processing is done in both the time domain and in the // frequency domain. // The lowest frequency of interest is 150 Hz so it will be // sufficient to use FFTs that span 10 milliseconds. // With sin squared windows we use 50% overlap and make a read // every 5 millisecond. // There is a factor of two because the soundcard samples a real- // valued signal but micsize is for complex transforms. micsize=ui.tx_ad_speed*.01; make_power_of_two(&micsize); micn=0; i=micsize; while(i>1) { i>>=1; micn++; } mic_sizhalf=micsize/2; mictimf_block=ui.tx_ad_channels*micsize; // Allow the input buffer to hold 40 milliseconds of data (or more) mictimf_bufsiz=8*mictimf_block; mictimf_mask=mictimf_bufsiz-1; tx_read_bytes=ui.tx_ad_bytes*mictimf_block; txad_hz_per_bin=(0.5*ui.tx_ad_speed)/micsize; tx_lowest_bin=txssb.minfreq/txad_hz_per_bin; if(tx_lowest_bin < 1)tx_lowest_bin=1; if(tx_lowest_bin > micsize/3)tx_lowest_bin=micsize/3; tx_highest_bin=txssb.maxfreq/txad_hz_per_bin; if(tx_highest_bin > micsize/2-2)tx_highest_bin=micsize/2-2; if(tx_highest_bin < tx_lowest_bin+1)tx_highest_bin=tx_lowest_bin+1; t1=(tx_highest_bin-tx_lowest_bin+1)*txad_hz_per_bin; if(ui.tx_da_channels == 1)t1*=2; if(t1 > 0.75*ui.tx_da_speed) { t1=ui.tx_da_speed; if(ui.tx_da_channels == 1)t1/=2; t1/=txad_hz_per_bin; tx_highest_bin=tx_lowest_bin+t1; } mic_fftsize=micsize; mic_fftn=micn; k=1.2*(tx_highest_bin-tx_lowest_bin); k*=2; while(k < mic_fftsize) { mic_fftsize/=2; mic_fftn--; } mic_sizhalf=mic_fftsize/2; micfft_block=2*mic_fftsize; micfft_bufsiz=8*mic_fftsize; micfft_mask=micfft_bufsiz-1; tx_filter_ia1=mic_fftsize+(tx_lowest_bin/2-tx_highest_bin/2-1); tx_filter_ib1=tx_filter_ia1+tx_highest_bin-tx_lowest_bin-mic_fftsize; k=mic_fftsize/tx_filter_ib1; alc_fftn=mic_fftn; alc_fftsize=mic_fftsize; tx_pre_resample=1; k++; while(k<8) { tx_pre_resample*=2; k*=2; alc_fftn++; alc_fftsize*=2; } alc_bufsiz=16*alc_fftsize; alc_mask=alc_bufsiz-1; alc_sizhalf=alc_fftsize/2; alc_block=2*alc_fftsize; tx_resample_ratio=(float)(2*ui.tx_da_speed)/ui.tx_ad_speed; tx_output_upsample=1; txout_fftsize=micsize; txout_fftn=micn; while(tx_resample_ratio > 1.5) { tx_resample_ratio/=2; tx_output_upsample*=2; txout_fftsize*=2; txout_fftn++; } new_tx_resample_ratio=tx_resample_ratio; txout_sizhalf=txout_fftsize/2; init_memalloc(txmem, MAX_TXMEM_ARRAYS); if(ui.tx_ad_bytes == 2) { mem(1,&mictimf_shi,mictimf_bufsiz*sizeof(short int),0); } else { mem(1,&mictimf_int,mictimf_bufsiz*sizeof(int),0); } mem(2,&micfft,micfft_bufsiz*sizeof(float),0); mem(3,&cliptimf,micfft_bufsiz*sizeof(float),0); mem(4,&mic_tmp,2*micsize*sizeof(float),0); mem(5,&mic_table,micsize*sizeof(COSIN_TABLE),0); mem(6,&mic_permute,2*micsize*sizeof(short int),0); mem(7,&mic_win,(1+micsize)*sizeof(float),0); mem(8,&mic_filter,micsize*sizeof(float),0); mem(9,&micfft_table,micsize*sizeof(COSIN_TABLE)/2,0); mem(10,&micfft_permute,micsize*sizeof(short int),0); mem(11,&micfft_win,(1+mic_sizhalf)*sizeof(float),0); mem(12,&clipfft,alc_bufsiz*sizeof(float),0); mem(13,&alctimf,alc_bufsiz*sizeof(float),0); mem(14,&alctimf_pwrf,alc_bufsiz*sizeof(float)/2,0); mem(15,&alctimf_pwrd,alc_bufsiz*sizeof(float)/2,0); mem(16,&tx_resamp,2*txout_fftsize*sizeof(float),0); mem(17,&resamp_tmp,alc_fftsize*sizeof(float),0); mem(18,&alc_permute,alc_fftsize*sizeof(short int),0); mem(19,&alc_table,alc_sizhalf*sizeof(COSIN_TABLE),0); mem(20,&alc_win,(1+alc_sizhalf)*sizeof(float),0); mem(21,&txout,2*txout_fftsize*sizeof(float),0); mem(22,&txout_permute,txout_fftsize*sizeof(short int),0); mem(23,&txout_table,txout_sizhalf*sizeof(COSIN_TABLE),0); mem(24,&txout_tmp,txout_fftsize*sizeof(float),0); if(ui.tx_da_bytes==4) { mem(25,&tx_out_int,ui.tx_da_channels*txout_fftsize*sizeof(int)/2,0); } else { mem(25,&tx_out_shi,ui.tx_da_channels*txout_fftsize*sizeof(int)/2,0); } mem(26,&cliptimf_mute,micfft_bufsiz*sizeof(char)/mic_fftsize,0); mem(27,&clipfft_mute,alc_bufsiz*sizeof(char)/alc_block,0); mem(28,&alctimf_mute,alc_bufsiz*sizeof(char)/alc_fftsize,0); txmem_size=memalloc((int*)(&txmem_handle),"txmem"); if(txmem_size == 0) { lirerr(1261); return; } make_permute(2, micn, micsize, mic_permute); make_window(2,micsize, 2, mic_win); if(ui.tx_ad_bytes != 2) { for(i=0; i=tx_lowest_bin; i--) { t1+=0.001*txssb.bass*txad_hz_per_bin; mic_filter[i]+=t1; } t1=0; for(i=tx_highest_bin-k; i<=tx_highest_bin; i++) { t1+=0.001*txssb.treble*txad_hz_per_bin; mic_filter[i]+=t1; } // Convert from dB to linear scale (amplitude) and normalize // the filter curve. t1=0; for(i=tx_lowest_bin; i<=tx_highest_bin; i++) { mic_filter[i]=pow(10.,0.05*mic_filter[i]); t1+=mic_filter[i]*mic_filter[i]; } t1=1/sqrt(t1/(tx_highest_bin-tx_lowest_bin+1)); for(i=tx_lowest_bin; i<=tx_highest_bin; i++) { mic_filter[i]*=t1; } mic_filter[tx_lowest_bin]*=0.5; mic_filter[tx_highest_bin]*=0.5; micfft_bin_minpower_ref=pow(10.,0.1*(txssb.mic_f_threshold-80)); micfft_minpower_ref=pow(10.,0.1*(txssb.mic_t_threshold+txssb.mic_f_threshold-70)); tx_agc_factor=1; tx_agc_decay=pow(2.718,-100.0/(txssb.mic_agc_time*txad_hz_per_bin)); txpwr_decay=pow(2.718,-1/(ui.tx_ad_speed*0.025)); tx_forwardpwr=0; tx_backpwr=0; tx_ph1=1; tx_wrblock_no=0; tx_wttim_ref=0; tx_wttim_sum=0; tx_wttim_diff=0; memcheck(1,txmem,(int*)&txmem_handle); make_tx_modes(); //lir_sleep(3000); tx_output_totsamp=0; tx_output_totsamp=-lir_tx_output_samples(); make_tx_phstep(); tx_output_phase=0; tx_resamp_ampfac1=1; tx_resamp_ampfac2=1; micfft_pa=0; micfft_px=0; mictimf_pa=0; mictimf_px=0; cliptimf_pa=0; cliptimf_px=0; clipfft_pa=0; clipfft_px=0; alctimf_pa=0; alctimf_pb=0; alctimf_fx=0; tx_output_flag=0; tx_output_amplitude=pow(10.0,-0.05*txssb.level_db); } void spproc_setup(void) { char s[80]; int i, k; int setup_mode; int ad_pix1; int ad_pix2; int mute_pix1; int mute_pix2; int micagc_pix1; int micagc_pix2; int rf1agc_pix1; int rf1agc_pix2; int rf1clip_pix1; int rf1clip_pix2; int alc_pix1; int alc_pix2; float t1, t2, r2; float prat, pmax; int old_display_ptr; int old_admax[DISPLAY_HOLD_SIZE]; float old_rf1agc[DISPLAY_HOLD_SIZE]; float old_rf1clip[DISPLAY_HOLD_SIZE]; float old_prat1[DISPLAY_HOLD_SIZE]; float old_pmax1[DISPLAY_HOLD_SIZE]; float old_prat2[DISPLAY_HOLD_SIZE]; float old_pmax2[DISPLAY_HOLD_SIZE]; float old_prat3[DISPLAY_HOLD_SIZE]; float old_pmax3[DISPLAY_HOLD_SIZE]; int default_spproc_no; rx_mode=MODE_SSB; default_spproc_no=tg.spproc_no; tx_setup_flag=TRUE; setup_mode=TX_SETUP_AD; if(read_txpar_file()==FALSE) { set_default_spproc_parms(); } restart:; screen_count=0; if(txssb.minfreq < SSB_MINFQ_LOW)txssb.minfreq=SSB_MINFQ_LOW; if(txssb.minfreq > SSB_MINFQ_HIGH)txssb.minfreq=SSB_MINFQ_HIGH; if(txssb.maxfreq < SSB_MAXFQ_LOW)txssb.maxfreq=SSB_MAXFQ_LOW; if(txssb.maxfreq > SSB_MAXFQ_HIGH)txssb.maxfreq=SSB_MAXFQ_HIGH; if(txssb.slope > SSB_MAXSLOPE)txssb.slope=SSB_MAXSLOPE; if(txssb.slope < SSB_MINSLOPE)txssb.slope=SSB_MINSLOPE; if(txssb.bass > SSB_MAXBASS)txssb.bass=SSB_MAXBASS; if(txssb.bass < SSB_MINBASS)txssb.bass=SSB_MINBASS; if(txssb.treble > SSB_MAXTREBLE)txssb.treble=SSB_MAXTREBLE; if(txssb.treble < SSB_MINTREBLE)txssb.treble=SSB_MINTREBLE; if(txssb.mic_f_threshold < 0)txssb.mic_f_threshold=0; if(txssb.mic_f_threshold > SSB_MAX_MICF)txssb.mic_f_threshold=SSB_MAX_MICF; if(txssb.mic_t_threshold < 0)txssb.mic_t_threshold=0; if(txssb.mic_t_threshold > SSB_MAX_MICT)txssb.mic_t_threshold=SSB_MAX_MICT; if(txssb.mic_gain < 0)txssb.mic_gain=0; if(txssb.mic_gain > SSB_MAX_MICGAIN)txssb.mic_gain=SSB_MAX_MICGAIN; if(txssb.mic_agc_time < 0)txssb.mic_agc_time=0; if(txssb.mic_agc_time > SSB_MAX_MICAGC_TIME) txssb.mic_agc_time=SSB_MAX_MICAGC_TIME; if(txssb.mic_out_gain < 0)txssb.mic_out_gain=0; if(txssb.mic_out_gain > SSB_MAX_MICOUT_GAIN) txssb.mic_out_gain=SSB_MAX_MICOUT_GAIN; if(txssb.rf1_gain < SSB_MIN_RF1_GAIN)txssb.rf1_gain=SSB_MIN_RF1_GAIN; if(txssb.rf1_gain > SSB_MAX_RF1_GAIN)txssb.rf1_gain=SSB_MAX_RF1_GAIN; open_tx_output(); open_tx_input(); if(kill_all_flag)return; init_txmem_spproc(); if(kill_all_flag)return; tx_show_siz=micsize; if(tx_show_siz < alc_fftsize)tx_show_siz=alc_fftsize; if(tx_show_siz < txout_fftsize)tx_show_siz=txout_fftsize; txtrace=malloc(MAX_DISPLAY_TYPES*tx_show_siz*sizeof(short int)); if(txtrace == NULL) { lirerr(778543); return; } lir_mutex_init(); for(i=0; i=MAX_SCREENCOUNT)screen_count=0; screen_count++; old_display_ptr++; if(old_display_ptr>=DISPLAY_HOLD_SIZE)old_display_ptr=0; // Show the microphone input level. ad_pix2=tx_ad_maxamp; if(ui.tx_ad_bytes != 2) { ad_pix2/=0x10000; } old_admax[old_display_ptr]=ad_pix2; t1=0; for(i=0; i=MAX_SCREENCOUNT) { show_txfft(&micfft[micfft_px],micfft_bin_minpower,0,mic_fftsize); } k=tx_ssb_step2(&t1); if(k==0) { sprintf(s,"MUTED"); } else { sprintf(s," "); tx_ssb_step3(&t1); } mute_pix2=(k*TX_BAR_SIZE)/(tx_highest_bin-tx_lowest_bin+1); tx_bar(16,TX_BAR_MUTE_LINE,mute_pix1,mute_pix2); mute_pix1=mute_pix2; settextcolor(15); lir_text(18+TX_BAR_SIZE/text_width,TX_BAR_MUTE_LINE,s); settextcolor(7); // In case AGC has reduced the gain by more than 20 dB, set the // AGC factor to -20 dB immediately because voice should never // be as loud. This is to avoid the agc release time constant // for impulse sounds caused by hitting the microphone etc. t1=20*log10(tx_agc_factor); sprintf(s,"%.1fdB ",t1); if(tx_agc_factor < 0.1)tx_agc_factor=0.1; lir_text(18+TX_BAR_SIZE/text_width, TX_BAR_MICAGC_LINE, s); micagc_pix2=-log10(tx_agc_factor)*TX_BAR_SIZE; tx_bar(16,TX_BAR_MICAGC_LINE,micagc_pix1,micagc_pix2); micagc_pix1=micagc_pix2; // ************* Step 4. ************* // Go back to the time domain and store the signal in cliptimf // Remember that we use sin squared windows and that // the transforms overlap with 50%. // // Ideally, the peak amplitude should be 1.0, the audio AGC // should be active and keep the power level nearly constant. // For a voice signal the waveform will differ from time to time // and therefore the peak to average power ratio will vary. // Compute the peak amplitude for the current transform // and save it for the next time we arrive here. // Use the peak amplitude for an AGC in the time domain (Hilbert space). // The Hilbert space AGC is a constant that may vary from one // FFT block to the next one. It is equivalent with an AM modulator // with a bandwidth corresponding to a single bin in the FFT so this // AM modulation will not increase the bandwidth notably, but it // will bring the RF amplitude near unity always. // // Finally, apply an amplitude limiter to the complex time domain signal. // It will work as an RF limiter on the SSB signal and cause a lot of // signal outside the passband. if(tx_agc_factor < 0.1)tx_agc_factor=0.1; if(k!=0) { fftback(mic_fftsize, mic_fftn, &micfft[micfft_px], micfft_table, micfft_permute); lir_sched_yield(); r2=0; for(i=0; ir2)r2=t2; } if(r2 > 0.01*MAX_DYNRANGE) { t1=sqrt(r2); r2=1/t1; old_rf1agc[old_display_ptr]=t1; if(t1>10)t1=10; rf1agc_pix2=log10(t1)*TX_BAR_SIZE; if(r2>10/tx_agc_factor)r2=10/tx_agc_factor; if(rf1agc_pix2 < 0)rf1agc_pix2=0; } else { r2=0; rf1agc_pix2=0; old_rf1agc[old_display_ptr]=0; } } else { r2=0; rf1agc_pix2=0; old_rf1agc[old_display_ptr]=0; } t1=0; for(i=0; i 0)t1=20*log10(t1); sprintf(s,"%.1fdB ",t1); lir_text(18+TX_BAR_SIZE/text_width, TX_BAR_RF1AGC_LINE, s); tx_bar(16,TX_BAR_RF1AGC_LINE,rf1agc_pix1,rf1agc_pix2); rf1agc_pix1=rf1agc_pix2; tx_ssb_step4(r2,&pmax,&prat); prat/=mic_sizhalf; old_prat1[old_display_ptr]=prat; old_pmax1[old_display_ptr]=pmax; prat=0; for(i=0; i0.000000001 && pmax > prat) { prat=10*log10(pmax/prat); } else { prat=99; } sprintf(s,"[%.1f] ",prat); lir_text(28+TX_BAR_SIZE/text_width, TX_BAR_MICAGC_LINE, s); old_rf1clip[old_display_ptr]=pmax; if(pmax > 1000)pmax=1000; if(pmax > 1) { rf1clip_pix2=log10(pmax)*TX_BAR_SIZE/3; } else { rf1clip_pix2=0; } t1=0; for(i=0; i 1) { t1=10*log10(t1); } else { t1=0; } sprintf(s,"%.1fdB ",t1); lir_text(18+TX_BAR_SIZE/text_width, TX_BAR_RF1CLIP_LINE, s); tx_bar(16,TX_BAR_RF1CLIP_LINE,rf1clip_pix1,rf1clip_pix2); rf1clip_pix1=rf1clip_pix2; // ************* Step 5. ************* // At this point we have applied an RF clipper by limiting power in // Hilbert space. As a consequence we have produced intermodulation // products outside the desired passband. // Go to the frequency domain and remove undesired frequencies. tx_ssb_step5(); // ************* Step 6. ************* // Go back to the time domain and store the signal in alctimf. // Remember that we use sin squared windows and that // the transforms overlap with 50%. // Compute power and store the peak power with // an exponential decay corresponding to a time constant // of 50 milliseconds. // We expand the fft size from mic_fftsize to tx_fftsiz2 because // the fractional resampling that will follow this step needs // the signal to be oversampled by a factor of four to avoid // attenuation at high frequencies. The polynomial fitting // works as a low pass filter and we do not want any attenuation // at the high side of our passband. while(clipfft_px != clipfft_pa) { pmax=tx_ssb_step6(&prat); lir_sched_yield(); prat/=alc_sizhalf; old_prat2[old_display_ptr]=prat; old_pmax2[old_display_ptr]=pmax; prat=0; for(i=0; i0.000000001 && pmax > prat) { prat=10*log10(pmax/prat); } else { prat=99; } sprintf(s,"[%.1f] ",prat); lir_text(28+TX_BAR_SIZE/text_width, TX_BAR_RF1CLIP_LINE, s); if(pmax > 1) { pmax=2*log10(pmax); if(pmax>1)pmax=1; alc_pix2=pmax*TX_BAR_SIZE; } else { alc_pix2=0; } tx_bar(16,TX_BAR_ALC_LINE,alc_pix1,alc_pix2); alc_pix1=alc_pix2; } // ************* Step 7. ************* // Use the slowly decaying bi-directional peak power as an ALC // but make sure we allow at least two data blocks un-processed // to allow the backwards fall-off to become re-calculated. // Using the slowly varying function for an ALC (= AM modulation) // will not increase the bandwidth by more than the bandwidth // of the modulation signal (50 ms or 20 Hz) while( ((alctimf_pa-alctimf_pb+alc_bufsiz)&alc_mask) > 3*alc_fftsize) { pmax=tx_ssb_step7(&prat); prat/=alc_sizhalf; old_prat3[old_display_ptr]=prat; old_pmax3[old_display_ptr]=pmax; prat=0; for(i=0; i0.000000001 && pmax > prat) { prat=10*log10(pmax/prat); } else { prat=99; } sprintf(s,"[%.1f] ",prat); lir_text(28+TX_BAR_SIZE/text_width, TX_BAR_ALC_LINE, s); } // ************* Step 8. ************* // In case we have enough signal in the buffer, start the output. tx_ssb_step8(); test_keyboard(); if(lir_inkey != 0) { process_current_lir_inkey(); if(lir_inkey=='X')goto end_tx_setup; if(lir_inkey==ARROW_UP_KEY) { txtrace_gain*=1.33; goto continue_setup; } if(lir_inkey==ARROW_DOWN_KEY) { txtrace_gain/=1.25; goto continue_setup; } close_tx_output(); linrad_thread_stop_and_join(THREAD_TX_INPUT); close_tx_input(); free(txmem_handle); lir_mutex_destroy(); if(lir_inkey=='S') { clear_screen(); save_tx_parms(TRUE); } if(lir_inkey=='R') { clear_screen(); read_txpar_file(); } switch (setup_mode) { case TX_SETUP_AD: if(lir_inkey=='L') { lir_text(0,TXPAR_INPUT_LINE,"Low:"); txssb.minfreq=lir_get_integer(5,TXPAR_INPUT_LINE,4,1,SSB_MINFQ_HIGH); } if(lir_inkey=='H') { lir_text(0,TXPAR_INPUT_LINE,"High:"); txssb.maxfreq=lir_get_integer(6,TXPAR_INPUT_LINE,5, SSB_MAXFQ_LOW,ui.tx_ad_speed/2); } if(lir_inkey=='F') { lir_text(0,TXPAR_INPUT_LINE,"Slope:"); txssb.slope=lir_get_integer(7,TXPAR_INPUT_LINE,4,SSB_MINSLOPE, SSB_MAXSLOPE); } if(lir_inkey=='B') { lir_text(0,TXPAR_INPUT_LINE,"Bass:"); txssb.bass=lir_get_integer(6, TXPAR_INPUT_LINE,4,SSB_MINBASS,SSB_MAXBASS); } if(lir_inkey=='T') { lir_text(0,TXPAR_INPUT_LINE,"Treble:"); txssb.treble=lir_get_integer(8, TXPAR_INPUT_LINE,4,SSB_MINTREBLE,SSB_MAXTREBLE); } break; case TX_SETUP_MUTE: if(lir_inkey=='F') { lir_text(0,TXPAR_INPUT_LINE,"Freq domain:"); txssb.mic_f_threshold=lir_get_integer(13, TXPAR_INPUT_LINE,3,0,SSB_MAX_MICF); } if(lir_inkey=='T') { lir_text(0,TXPAR_INPUT_LINE,"Time domain:"); txssb.mic_t_threshold=lir_get_integer(13, TXPAR_INPUT_LINE,3,0,SSB_MAX_MICT); } break; case TX_SETUP_MICAGC: if(lir_inkey=='V') { lir_text(0,TXPAR_INPUT_LINE,"Volume:"); txssb.mic_gain=lir_get_integer(8, TXPAR_INPUT_LINE,3,0,SSB_MAX_MICGAIN); } if(lir_inkey=='T') { lir_text(0,TXPAR_INPUT_LINE,"Time constant:"); t1=lir_get_float(15,TXPAR_INPUT_LINE,6,0,SSB_MAX_MICAGC_TIME); txssb.mic_agc_time=100*t1; } break; case TX_SETUP_RF1AGC: if(lir_inkey=='B') { lir_text(0,TXPAR_INPUT_LINE,"Gain:"); txssb.mic_out_gain=lir_get_integer(15, TXPAR_INPUT_LINE,2,0,SSB_MAX_MICOUT_GAIN); } break; case TX_SETUP_RF1CLIP: if(lir_inkey=='B') { lir_text(0,TXPAR_INPUT_LINE,"Clipper gain:"); txssb.rf1_gain=lir_get_integer(15, TXPAR_INPUT_LINE,3,SSB_MIN_RF1_GAIN,SSB_MAX_RF1_GAIN); } break; } if(lir_inkey=='A')setup_mode=TX_SETUP_MICAGC; if(lir_inkey=='M')setup_mode=TX_SETUP_AD; if(lir_inkey=='Q')setup_mode=TX_SETUP_MUTE; if(lir_inkey=='D')setup_mode=TX_SETUP_RF1AGC; if(lir_inkey=='C')setup_mode=TX_SETUP_RF1CLIP; if(lir_inkey=='+') { tg.spproc_no++; if(tg.spproc_no > MAX_SSBPROC_FILES)tg.spproc_no=MAX_SSBPROC_FILES; } if(lir_inkey=='-') { tg.spproc_no--; if(tg.spproc_no < 0)tg.spproc_no=0; } goto restart; } continue_setup:; } end_tx_setup:; close_tx_output(); linrad_thread_stop_and_join(THREAD_TX_INPUT); close_tx_input(); free(txmem_handle); lir_mutex_destroy(); tg.spproc_no=default_spproc_no; } void tx_input(void) { int i, ia, ib, pa, pb, pc; int k; int twosiz, ssb_bufmin; int nread; float *z; float t1; twosiz=2*micsize; ssb_bufmin=twosiz*ui.tx_ad_channels; pb=mictimf_px; thread_status_flag[THREAD_TX_INPUT]=THRFLAG_ACTIVE; while(!kill_all_flag && thread_command_flag[THREAD_TX_INPUT] == THRFLAG_ACTIVE) { if(ui.tx_ad_bytes == 2) { nread=read(tx_audio_in,&mictimf_shi[mictimf_pa],tx_read_bytes); } else { nread=read(tx_audio_in,&mictimf_int[mictimf_pa],tx_read_bytes); } if(nread != tx_read_bytes) { lirerr(1283); goto tx_input_x; } mictimf_pa=(mictimf_pa+mictimf_block)&mictimf_mask; switch (rx_mode) { case MODE_SSB: if( ((mictimf_pa-mictimf_px+mictimf_bufsiz)&mictimf_mask) >= ssb_bufmin) { // Copy the input to micfft while multiplying with the window function. ib=twosiz-1; pa=mictimf_px; switch(tx_input_mode) { case 0: pb=(pa+ib)&mictimf_mask; for( ia=0; ia tx_ad_maxamp) { tx_ad_maxamp=abs(mictimf_shi[pa]); } mic_tmp[mic_permute[ib]]=(float)(mictimf_shi[pb])*mic_win[ia]; if(abs(mictimf_shi[pb]) > tx_ad_maxamp) { tx_ad_maxamp=abs(mictimf_shi[pb]); } ib--; pa=(pa+1)&mictimf_mask; pb=(pb+mictimf_mask)&mictimf_mask; } break; case 1: // In case there are two channels for the microphone, just use one of them!! pb=(pa+2*ib)&mictimf_mask; for( ia=0; ia tx_ad_maxamp) { tx_ad_maxamp=abs(mictimf_shi[pa]); } mic_tmp[mic_permute[ib]]=mictimf_shi[pb]*mic_win[ia]; if(abs(mictimf_shi[pb]) > tx_ad_maxamp) { tx_ad_maxamp=abs(mictimf_shi[pb]); } ib--; pa=(pa+2)&mictimf_mask; pb=(pb+mictimf_mask-1)&mictimf_mask; } break; case 2: pb=(pa+ib)&mictimf_mask; for( ia=0; ia tx_ad_maxamp) { tx_ad_maxamp=abs(mictimf_int[pa]); } mic_tmp[mic_permute[ib]]=mictimf_int[pb]*mic_win[ia]; if(abs(mictimf_int[pb]) > tx_ad_maxamp) { tx_ad_maxamp=abs(mictimf_int[pb]); } ib--; pa=(pa+1)&mictimf_mask; pb=(pb+mictimf_mask)&mictimf_mask; } break; case 3: // In case there are two channels for the microphone, just use one of them!! pb=(pa+2*ib)&mictimf_mask; for( ia=0; ia tx_ad_maxamp) { tx_ad_maxamp=abs(mictimf_int[pa]); } mic_tmp[mic_permute[ib]]=mictimf_int[pb]*mic_win[ia]; if(abs(mictimf_int[pb]) > tx_ad_maxamp) { tx_ad_maxamp=abs(mictimf_int[pb]); } ib--; pa=(pa+2)&mictimf_mask; pb=(pb+mictimf_mask-1)&mictimf_mask; } } // The microphone signal should be maximum 0x7fff or 0x7fffffffffff // depending on whether 16 or 32 bit format is selected. mictimf_px=(mictimf_px+mictimf_block)&mictimf_mask; fft_real_to_hermitian( mic_tmp, twosiz, micn, mic_table); // Output is {Re(z^[0]),...,Re(z^[n/2),Im(z^[n/2-1]),...,Im(z^[1]). // Clear those parts of the spectrum that we do not want and // transfer the rest of the points to micfft while multiplying // with our filter function. // Place the spectrum center at frequency zero so we can use // the lowest possible sampling rate in further processing steps. z=&micfft[micfft_pa]; pc=2*(tx_highest_bin/2-tx_lowest_bin/2); k=micfft_block-pc-2; while(pc < k) { z[pc ]=0; z[pc+1]=0; pc+=2; } k=tx_lowest_bin; pc=2*tx_filter_ia1; while(pc < 2*mic_fftsize) { z[pc+1]=mic_tmp[k ]*mic_filter[k]; z[pc ]=mic_tmp[twosiz-k]*mic_filter[k]; pc+=2; k++; } pc=0; while(k <= tx_highest_bin) { z[pc+1]=mic_tmp[k ]*mic_filter[k]; z[pc ]=mic_tmp[twosiz-k]*mic_filter[k]; pc+=2; k++; } micfft_pa=(micfft_pa+micfft_block)&micfft_mask; } break; case MODE_WCW: case MODE_NCW: case MODE_HSMS: case MODE_QRSS: switch(tx_input_mode) { case 0: while(mictimf_pa != mictimf_px) { t1=0; for(i=0; i=max_tx_cw_waveform_points)lirerr(93528); // Set a very small non-zero amplitude for use by the pilot tone. for(i=1; i<=n1; i++) txout_waveform[i]=0.9*PILOT_TONE_CONTROL; dt1=3.3; dt2=6.6/(n2-3); for(i=n1+1; i