]> git.rkrishnan.org Git - pihpsdr.git/commitdiff
Measured "spectral pollution" and included the measured data
authorc vw <dl1ycf@darc.de>
Wed, 11 Jul 2018 09:48:37 +0000 (11:48 +0200)
committerc vw <dl1ycf@darc.de>
Wed, 11 Jul 2018 09:48:37 +0000 (11:48 +0200)
into the comment at the beginning of cwramp.c

Makefile
Makefile.mac
cwramp.c [new file with mode: 0644]
transmitter.c

index 5c0a4fa8f0829888637cc1caefebe8a053ccd1ff..c2b5d51d836e06d98047065178d4236c2b170fde 100644 (file)
--- 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)
index 1a16b8c22566091d4a30670ad96c5c89c6d6c9fb..514ab0355a55035d6cf3960050836f932ddf4e4f 100644 (file)
@@ -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 (file)
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};
+
index 3bd504fada8791046c925e7920ea77f78146fb37..80e829dc0cbbf3d2c297fbeff801ea54febcd5f3 100644 (file)
@@ -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;j<tx->output_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;