From 41962a33caa6c83ba80802e312260ce988aa7124 Mon Sep 17 00:00:00 2001 From: c vw Date: Wed, 11 Jul 2018 11:48:37 +0200 Subject: [PATCH] Measured "spectral pollution" and included the measured data into the comment at the beginning of cwramp.c --- Makefile | 6 +- Makefile.mac | 4 +- cwramp.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++ transmitter.c | 56 +++++++---- 4 files changed, 311 insertions(+), 25 deletions(-) create mode 100644 cwramp.c diff --git a/Makefile b/Makefile index 5c0a4fa..c2b5d51 100644 --- a/Makefile +++ b/Makefile @@ -262,7 +262,8 @@ store_menu.c \ memory.c \ led.c \ ext.c \ -error_handler.c +error_handler.c \ +cwramp.c HEADERS= \ @@ -402,7 +403,8 @@ store_menu.o \ memory.o \ led.o \ ext.o \ -error_handler.o +error_handler.o \ +cwramp.o $(PROGRAM): $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) $(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) $(LIBS) diff --git a/Makefile.mac b/Makefile.mac index 1a16b8c..514ab03 100644 --- a/Makefile.mac +++ b/Makefile.mac @@ -296,7 +296,8 @@ store_menu.c \ memory.c \ led.c \ ext.c \ -error_handler.c +error_handler.c \ +cwramp.c HEADERS= \ @@ -437,6 +438,7 @@ memory.o \ led.o \ ext.o \ error_handler.o \ +cwramp.o \ portaudio.o $(PROGRAM): $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) diff --git a/cwramp.c b/cwramp.c new file mode 100644 index 0000000..dd75afe --- /dev/null +++ b/cwramp.c @@ -0,0 +1,270 @@ +/* + * Contribution of DL1YCF Christoph van W"ullen: + * + * This is the "ramp" with length 200 which is + * the step response derived from a digital filter + * with a Blackman-Harris window. + * It is used to shape the rising and falling + * edge of a CW pulse. The goal is to have + * a pulse whose off-center frequency components + * quickly fall off. + * + * The Blackman-Harris window function is + * + * B(x) = a0 - a1 Cos(2 Pi x) + a2 Cos(4 Pi x) - a3 Cos(6 Pi x) + * + * with coefficients + * a0 = 0.35875 + * a1 = 0.48829 + * a2 = 0.14128 + * a3 = 0.01168 + * + * And the ramp of length 200 is + * + * cwramp[i] = (Integral of B(x) from zero to i/200) / a0 + * + * where a0 is (Integral of (Bx) from zero to one) + * + * such that cwramp[0] = cwramp[200] = 0, and the values rise + * with the shape of a sigmoid. + * + * The values here are calculated using MATHEMATICA with 32-digit accuracy + * (more than we need for double) and copy/pasted into this file. + * + * + * MEASURING SPECTRAL POLLUTION + * ============================ + * + * Using an SDR, a DummyLoad/Attenuator and my Kenwood TRX for receiving + * the signal, I started a 20 wpm CQ call on the SDR at 7010 kHz and + * monitored the S-meter of my Kenwood rig for different frequencies. + * I used a 60dB attenuator which made a S9+50dB signal in my Kenwood + * out of the 10 Watts output of my SDR. + * Three cases were measured: + * a) "hard" CW signal, rectangular CW pulses + * b) CW signal with linear ramps, width 4 msec + * c) CW signal with Blackman-Harris ramp (as defined here), width 4 msec + * + * These are the results: + * + * Freq / kHz S-Meter S-Meter S-Meter + * "hard" "linear" "Blackman-Harris" + * ------------------------------------------------------ + * 7010.0 S9+50 S9+50 S9+50 + * 7009.5 S9+25 S9 S9+10 + * 7009.0 S9+20 S6 S0 + * 7008.5 S9+15 S4 S0 + * 7008.0 S9+10 S3 S0 + * 7005.0 S9+5 S0 S0 + * 7000.0 S8 S0 S0 + * ----------------------------------------------------- + * + * (for frequencies above 7010 kHz, one gets mirror-image results). + * + * The linear ramp is a big improvement, but the Blackman-Harris + * ramp does far better if more than 500 Hz off. + */ + +double cwramp[201] = { +0.0, +9.01187795859706156177643839E-7, +2.194323834526231706516646770E-6, +4.282571576728779171701009811E-6, +7.59152417258436512341254065E-6, +0.00001258041690211947993636267109, +0.00001975333523137922572945668287, +0.00002967041454480472948228913986, +0.00004295902553278649886291894300, +0.00006032493666320024489466741405, +0.00008256344218126450788500686944, +0.00011057044070320856501019611699, +0.00014535344574159831560164368292, +0.00018804250547447493441427381117, +0.00023990100480207670388152058280, +0.00030233631828325189492558643061, +0.00037691027797158203918335035555, +0.00046534941554435985240942810166, +0.00056955493350364783431057938016, +0.00069161235569682109078184134293, +0.00083380080302407864906440039504, +0.00099860183604212891204121277035, +0.00118870780230554127073025982217, +0.00140702962277747793984056753115, +0.00165670394855476385194253550184, +0.00194109961655060504615626150874, +0.00226382333072012476435261017988, +0.00262872449395329839902468237813, +0.00303989911494590614436084016036, +0.00350169271423530270458824302775, +0.00401870215419155535384463676037, +0.00459577631911668629751740793823, +0.00523801557374924620039546203446, +0.00595076993141477129560262502588, +0.00673963586681372911811349735623, +0.00761045171299837322070433116670, +0.00856929158745153542728726050674, +0.00962245779832873572690245854193, +0.01077647168883695665617177556310, +0.01203806288536788123878808156746, +0.01341415692334135279803139842992, +0.01491186123369968180032171505537, +0.01653844948257128001833036523305, +0.01830134426673207878624266925140, +0.02020809817806791189569866340858, +0.02226637326120713880733998238444, +0.02448391889977143838668571452861, +0.02686854817820029119729513109423, +0.02942811277775342177744558204085, +0.03217047647699419958395416072235, +0.03510348733871183926970109538183, +0.03823494867675546007436492565069, +0.04157258890753185887801606599357, +0.04512403040186418964004939087610, +0.04889675746342420481031099370853, +0.05289808356994134481892840233434, +0.05713511802276510901007293628661, +0.06161473215902329384830063982633, +0.06634352528849228438584699015456, +0.07132779052429581807377516557786, +0.07657348068260018970052026096202, +0.08208617443150763044908273672262, +0.0878710428733033542817043103120, +0.0939328167470308029827954611885, +0.1002757544400072779860624428609, +0.1069036109973103171592225098772, +0.1138196083174347330899436336723, +0.1210264067202213859669792420239, +0.1285260780697813419156364133806, +0.1363200806304827024531349966765, +0.1444092358281416498341153348924, +0.1527937070813836713066362714703, +0.1614729808597449580170472173813, +0.1704458501155068668214820716660, +0.1797104002255469077569583767922, +0.1892639975677060809000157790299, +0.1991032808433815468445483021142, +0.2092241552443310463622979568190, +0.2196217895471045931434166370431, +0.2302906162031904975140892412129, +0.2412243344769741731293350985271, +0.2524159166670628372463994300812, +0.2638576174295357435495515307263, +0.2755409862043509745237860450997, +0.2874568827285925860110134866717, +0.2995954956025951958334437651411, +0.3119463638573578192626567029370, +0.3244984014541765421903072764578, +0.3372399246302079959411341784319, +0.3501586819868429577753048512722, +0.3632418872014401014103738694015, +0.3764762542272593365183481179365, +0.3898480348314537908663795438645, +0.4033430583068360197915402597148, +0.4169467731799285763401037295609, +0.4306442907256363254915247826210, +0.4444204300878253507887486917206, +0.4582597647952406272219259607917, +0.4721466704536129633322770005995, +0.4860653733875571201965669425722, +0.5000000000000000000000000000000, +0.5139346266124428798034330574278, +0.5278533295463870366677229994005, +0.5417402352047593727780740392083, +0.5555795699121746492112513082794, +0.5693557092743636745084752173790, +0.5830532268200714236598962704391, +0.5966569416931639802084597402852, +0.6101519651685462091336204561355, +0.6235237457727406634816518820635, +0.6367581127985598985896261305985, +0.6498413180131570422246951487278, +0.6627600753697920040588658215681, +0.6755015985458234578096927235422, +0.6880536361426421807373432970630, +0.7004045043974048041665562348589, +0.7125431172714074139889865133283, +0.7244590137956490254762139549003, +0.7361423825704642564504484692737, +0.7475840833329371627536005699188, +0.7587756655230258268706649014729, +0.7697093837968095024859107587871, +0.7803782104528954068565833629569, +0.7907758447556689536377020431810, +0.8008967191566184531554516978858, +0.8107360024322939190999842209701, +0.8202895997744530922430416232078, +0.8295541498844931331785179283340, +0.8385270191402550419829527826187, +0.8472062929186163286933637285297, +0.8555907641718583501658846651076, +0.8636799193695172975468650033235, +0.8714739219302186580843635866194, +0.8789735932797786140330207579761, +0.8861803916825652669100563663277, +0.8930963890026896828407774901228, +0.8997242455599927220139375571391, +0.9060671832529691970172045388115, +0.9121289571266966457182956896880, +0.9179138255684923695509172632774, +0.9234265193173998102994797390380, +0.9286722094757041819262248344221, +0.9336564747115077156141530098454, +0.9383852678409767061516993601737, +0.9428648819772348909899270637134, +0.9471019164300586551810715976657, +0.9511032425365757951896890062915, +0.9548759695981358103599506091239, +0.9584274110924681411219839340064, +0.9617650513232445399256350743493, +0.9648965126612881607302989046182, +0.9678295235230058004160458392777, +0.9705718872222465782225544179592, +0.9731314518217997088027048689058, +0.9755160811002285616133142854714, +0.9777336267387928611926600176156, +0.9797919018219320881043013365914, +0.9816986557332679212137573307486, +0.9834615505174287199816696347670, +0.9850881387663003181996782849446, +0.9865858430766586472019686015701, +0.9879619371146321187612119184325, +0.9892235283111630433438282244369, +0.9903775422016712642730975414581, +0.9914307084125484645727127394933, +0.9923895482870016267792956688333, +0.9932603641331862708818865026438, +0.9940492300685852287043973749741, +0.9947619844262507537996045379655, +0.9954042236808833137024825920618, +0.9959812978458084446461553632396, +0.9964983072857646972954117569722, +0.9969601008850540938556391598396, +0.9973712755060467016009753176219, +0.9977361766692798752356473898201, +0.9980589003834493949538437384913, +0.9983432960514452361480574644982, +0.9985929703772225220601594324689, +0.9988112921976944587292697401778, +0.9990013981639578710879587872296, +0.9991661991969759213509355996050, +0.9993083876443031789092181586571, +0.9994304450664963521656894206198, +0.9995346505844556401475905718983, +0.9996230897220284179608166496444, +0.9996976636817167481050744135694, +0.9997600989951979232961184794172, +0.9998119574945255250655857261888, +0.9998546465542584016843983563171, +0.9998894295592967914349898038830, +0.9999174365578187354921149931306, +0.9999396750633367997551053325860, +0.9999570409744672135011370810570, +0.9999703295854551952705177108601, +0.9999802466647686207742705433171, +0.9999874195830978805200636373289, +0.9999924084758274156348765874593, +0.9999957174284232712208282989902, +0.9999978056761654737682934833532, +0.9999990988122041402938438223562, +1.0000000000000000000000000000000}; + diff --git a/transmitter.c b/transmitter.c index 3bd504f..80e829d 100644 --- a/transmitter.c +++ b/transmitter.c @@ -75,8 +75,12 @@ int cw_key_down = 0; int cw_not_ready=1; // cw_shape_buffer will eventually be integrated into TRANSMITTER -static int *cw_shape_buffer = NULL; +static double *cw_shape_buffer = NULL; static int cw_shape = 0; +// cwramp is the function defining the "ramp" of the CW pulse. +// is *must be* an array with 201 entries. The ramp width (200 samples) +// is hard-coded. +extern double cwramp[]; // see cwramp.c extern void cw_audio_write(double sample); @@ -575,7 +579,7 @@ fprintf(stderr,"transmitter: allocate buffers: mic_input_buffer=%d iq_output_buf tx->samples=0; tx->pixel_samples=malloc(sizeof(float)*tx->pixels); if (cw_shape_buffer) free(cw_shape_buffer); - cw_shape_buffer=malloc(sizeof(int)*tx->buffer_size); + cw_shape_buffer=malloc(sizeof(double)*tx->buffer_size); fprintf(stderr,"transmitter: allocate buffers: mic_input_buffer=%p iq_output_buffer=%p pixels=%p\n",tx->mic_input_buffer,tx->iq_output_buffer,tx->pixel_samples); fprintf(stderr,"create_transmitter: OpenChannel id=%d buffer_size=%d fft_size=%d sample_rate=%d dspRate=%d outputRate=%d\n", @@ -736,7 +740,7 @@ void tx_set_pre_emphasize(TRANSMITTER *tx,int state) { static void full_tx_buffer(TRANSMITTER *tx) { long isample; long qsample; - double gain,fgain,sidevol; + double gain, sidevol, ramp, fgain; int j; int error; int mode; @@ -787,13 +791,14 @@ static void full_tx_buffer(TRANSMITTER *tx) { if ((mode == modeCWL || mode == modeCWU) && !tune) { // // "pulse shape case": - // shape the I/Q samples with the envelope function stored in cw_shape_buffer - // and produce side tone (again with shaped pulses) + // shape the I/Q samples with the envelope stored in cw_shape_buffer. + // We also produce a side tone with same shape. // - fgain=gain*0.005; // will be multiplied with cw_shape - sidevol= 1.29 * cw_keyer_sidetone_volume; // will be multiplied with cw_shape + fgain=gain; // will be multiplied with ramp function + sidevol= 258.0 * cw_keyer_sidetone_volume; // will be multiplied with ramp function for(j=0;joutput_samples;j++) { - gain=fgain*cw_shape_buffer[j]; + ramp=cw_shape_buffer[j]; // between 0 and 1 + gain=fgain*ramp; double is=tx->iq_output_buffer[j*2]; double qs=tx->iq_output_buffer[(j*2)+1]; isample=is>=0.0?(long)floor(is*gain+0.5):(long)ceil(is*gain-0.5); @@ -804,7 +809,7 @@ static void full_tx_buffer(TRANSMITTER *tx) { // since we may use getNextSidetoneSample for local audio, we need // an independent instance thereof here. To be nice to the CW // operator, the audio is shaped the same way as the RF - sidetone=sidevol * cw_shape_buffer[j] * getNextInternalSideToneSample(); + sidetone=sidevol * ramp * getNextInternalSideToneSample(); old_protocol_iq_samples_with_sidetone(isample,qsample,sidetone); break; case NEW_PROTOCOL: @@ -838,7 +843,7 @@ static void full_tx_buffer(TRANSMITTER *tx) { void add_mic_sample(TRANSMITTER *tx,short mic_sample) { int mode; double sample; - double mic_sample_double; + double mic_sample_double, ramp; int i,s; if(split) { @@ -869,35 +874,42 @@ void add_mic_sample(TRANSMITTER *tx,short mic_sample) { // cw_key_down can be zero, for inserting some space // // We HAVE TO shape the signal to avoid hard clicks to be -// heard way beside our frequency. The envelope goes up -// and down linearly within 200 samples (4.16 msec) +// heard way beside our frequency. The envelope (ramp function) +// is stored in cwramp[0::200], so we "move" cw_shape between these +// values. The ramp width is 200 samples (4.16 msec) +// +// Note that usually, the pulse is much broader than the ramp, +// that is, cw_key_down and cw_key_up are much larger than 200. // cw_not_ready=0; if (cw_key_down > 0 ) { - if (cw_shape < 200) cw_shape++; - cw_key_down--; + if (cw_shape < 200) cw_shape++; // walk up the ramp + cw_key_down--; // decrement key-up counter } else { if (cw_key_up >= 0) { // dig into this even if cw_key_up is already zero, to ensure - // that cw_shape eventually reaches zero - if (cw_shape > 0) cw_shape--; - if (cw_key_up > 0) cw_key_up--; + // that we reach the bottom of the ramp for very small pauses + if (cw_shape > 0) cw_shape--; // walk down the ramp + if (cw_key_up > 0) cw_key_up--; // decrement key-down counter } } - cw_audio_write(0.00003937 * getNextSideToneSample() * cw_keyer_sidetone_volume * cw_shape); - cw_shape_buffer[tx->samples]=cw_shape; + // store the ramp value in cw_shape_buffer, but also use it for shaping the "local" + // side tone + ramp=cwramp[cw_shape]; + cw_audio_write(0.00003937 * getNextSideToneSample() * cw_keyer_sidetone_volume * ramp); + cw_shape_buffer[tx->samples]=ramp; } else { // // If no longer transmitting, or no longer doing CW: reset pulse shaper. -// This will also swallow any pending CW in rigtl CAT CW and wipe out the -// cw_shape buffer very quickly. In order to tell rigctl etc. that CW should be +// This will also swallow any pending CW in rigtl CAT CW and wipe out +// cw_shape_buffer very quickly. In order to tell rigctl etc. that CW should be // aborted, we also use the cw_not_ready flag. // cw_not_ready=1; cw_key_up=0; cw_key_down=0; cw_shape=0; - cw_shape_buffer[tx->samples]=0; + cw_shape_buffer[tx->samples]=0.0; } tx->mic_input_buffer[tx->samples*2]=mic_sample_double; tx->mic_input_buffer[(tx->samples*2)+1]=0.0; //mic_sample_double; -- 2.45.2