]> git.rkrishnan.org Git - dttsp.git/blob - jDttSP/update.c
a090b76faffc85101c3aad00c77b9c8144d333b7
[dttsp.git] / jDttSP / update.c
1 /* update.c
2
3 common defs and code for parm update 
4    
5 This file is part of a program that implements a Software-Defined Radio.
6
7 Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
23 The authors can be reached by email at
24
25 ab2kt@arrl.net
26 or
27 rwmcgwier@comcast.net
28
29 or by paper mail at
30
31 The DTTS Microwave Society
32 6 Kathleen Place
33 Bridgewater, NJ 08807
34 */
35
36 #include <common.h>
37
38 ////////////////////////////////////////////////////////////////////////////
39
40 PRIVATE REAL
41 dB2lin(REAL dB) { return pow(10.0, dB / 20.0); }
42
43 PRIVATE int
44 setRXFilter(int n, char **p) {
45   REAL low_frequency  = atof(p[0]),
46        high_frequency = atof(p[1]);
47   int ncoef = uni.buflen + 1;
48   int i, fftlen = 2 * uni.buflen;
49   fftw_plan ptmp;
50   COMPLEX *zcvec;
51
52   if (fabs(low_frequency) >= 0.5 * uni.samplerate) return -1;
53   if (fabs(high_frequency) >= 0.5 * uni.samplerate) return -2;
54   if ((low_frequency + 10) >= high_frequency) return -3;
55   delFIR_COMPLEX(rx.filt.coef);
56
57   rx.filt.coef = newFIR_Bandpass_COMPLEX(low_frequency,
58                                          high_frequency,
59                                          uni.samplerate,
60                                          ncoef);
61
62   zcvec = newvec_COMPLEX(fftlen, "filter z vec in setFilter");
63   ptmp = fftw_create_plan(fftlen, FFTW_FORWARD, uni.wisdom.bits);
64 #ifdef LHS
65   for (i = 0; i < ncoef; i++) zcvec[i] = rx.filt.coef->coef[i];
66 #else
67   for (i = 0; i < ncoef; i++) zcvec[fftlen - ncoef + i] = rx.filt.coef->coef[i];
68 #endif
69   fftw_one(ptmp, (fftw_complex *) zcvec, (fftw_complex *) rx.filt.ovsv->zfvec);
70   fftw_destroy_plan(ptmp);
71   delvec_COMPLEX(zcvec);
72   normalize_vec_COMPLEX(rx.filt.ovsv->zfvec,
73                         rx.filt.ovsv->fftlen);
74   memcpy((char *) rx.filt.save,
75          (char *) rx.filt.ovsv->zfvec,
76          rx.filt.ovsv->fftlen * sizeof(COMPLEX));
77
78   return 0;
79 }
80
81
82 PRIVATE int
83 setTXFilter(int n, char **p) {
84   REAL low_frequency  = atof(p[0]),
85        high_frequency = atof(p[1]);
86   int ncoef = uni.buflen + 1;
87   int i, fftlen = 2 * uni.buflen;
88   fftw_plan ptmp;
89   COMPLEX *zcvec;
90
91   if (fabs(low_frequency) >= 0.5 * uni.samplerate) return -1;
92   if (fabs(high_frequency) >= 0.5 * uni.samplerate) return -2;
93   if ((low_frequency + 10) >= high_frequency) return -3;
94   delFIR_COMPLEX(tx.filt.coef);
95   tx.filt.coef = newFIR_Bandpass_COMPLEX(low_frequency,
96                                          high_frequency,
97                                          uni.samplerate,
98                                          ncoef);
99   
100   zcvec = newvec_COMPLEX(fftlen, "filter z vec in setFilter");
101   ptmp = fftw_create_plan(fftlen, FFTW_FORWARD, uni.wisdom.bits);
102 #ifdef LHS
103   for (i = 0; i < ncoef; i++) zcvec[i] = tx.filt.coef->coef[i];
104 #else
105   for (i = 0; i < ncoef; i++) zcvec[fftlen - ncoef + i] = tx.filt.coef->coef[i];
106 #endif
107   fftw_one(ptmp, (fftw_complex *) zcvec, (fftw_complex *) tx.filt.ovsv->zfvec);
108   fftw_destroy_plan(ptmp);
109   delvec_COMPLEX(zcvec);
110   normalize_vec_COMPLEX(tx.filt.ovsv->zfvec,
111                         tx.filt.ovsv->fftlen);
112   memcpy((char *) tx.filt.save,
113          (char *) tx.filt.ovsv->zfvec,
114          tx.filt.ovsv->fftlen * sizeof(COMPLEX));
115
116   return 0;
117 }
118
119 PRIVATE int
120 setFilter(int n, char **p) {
121   if (n == 2) return setRXFilter(n, p);
122   else {
123     int trx = atoi(p[2]);
124     if      (trx == RX) return setRXFilter(n, p);
125     else if (trx == TX) return setTXFilter(n, p);
126     else                return -1;
127   }
128 }
129
130 // setMode <mode> [TRX]
131
132 PRIVATE int
133 setMode(int n, char **p) {
134   int mode = atoi(p[0]);
135   if (n > 1) {
136     int trx = atoi(p[1]);
137     switch (trx) {
138     case TX: tx.mode = mode; break;
139     case RX: 
140     default: rx.mode = mode; break;
141     }
142   } else
143     tx.mode = rx.mode = uni.mode.sdr = mode;
144   if (rx.mode == AM) rx.am.gen->mode = AMdet;
145   if (rx.mode == SAM) rx.am.gen->mode = SAMdet;
146   return 0;
147 }
148
149 // setOsc <freq> [TRX]
150 PRIVATE int
151 setOsc(int n, char **p) {
152   REAL newfreq = atof(p[0]);
153   if (fabs(newfreq) >= 0.5 * uni.samplerate) return -1;
154   newfreq *= 2.0 * M_PI / uni.samplerate;
155   if (n > 1) {
156     int trx = atoi(p[1]);
157     switch (trx) {
158     case TX: tx.osc.gen->Frequency = newfreq; break;
159     case RX:
160     default: rx.osc.gen->Frequency = newfreq; break;
161     }
162   } else
163     tx.osc.gen->Frequency = rx.osc.gen->Frequency = newfreq;
164   return 0;
165 }
166
167 PRIVATE int
168 setSampleRate(int n, char **p) {
169   REAL samplerate = atof(p[0]);
170   uni.samplerate = samplerate;
171   return 0;
172 }
173
174 PRIVATE int
175 setNR(int n, char **p) {
176   rx.anr.flag = atoi(p[0]);
177   return 0;
178 }
179
180 PRIVATE int
181 setANF(int n, char **p) {
182   rx.anf.flag = atoi(p[0]);
183   return 0;
184 }
185
186 PRIVATE int
187 setNB(int n, char **p) {
188   rx.nb.flag = atoi(p[0]);
189   return 0;
190 }
191
192 PRIVATE int
193 setNBvals(int n, char **p) {
194  REAL threshold = atof(p[0]);
195   rx.nb.gen->threshold = rx.nb.thresh = threshold;
196   return 0;
197 }
198
199 PRIVATE int
200 setSDROM(int n, char **p) {
201   rx.nb_sdrom.flag = atoi(p[0]);
202   return 0;
203 }
204
205 PRIVATE int
206 setSDROMvals(int n, char **p) {
207  REAL threshold = atof(p[0]);
208   rx.nb_sdrom.gen->threshold = rx.nb_sdrom.thresh = threshold;
209   return 0;
210 }
211
212 PRIVATE int
213 setBIN(int n, char **p) {
214   rx.bin.flag = atoi(p[0]);
215   return 0;
216 }
217
218 // setfixedAGC <gain> [TRX]
219 PRIVATE int
220 setfixedAGC(int n, char **p) {
221   REAL gain = atof(p[0]);
222   if (n > 1) {
223     int trx = atoi(p[1]);
224     switch(trx) {
225     case TX: tx.agc.gen->gain.now = gain; break;
226     case RX:
227     default: rx.agc.gen->gain.now = gain; break;
228     }
229   } else
230     tx.agc.gen->gain.now = rx.agc.gen->gain.now = gain;
231   return 0;
232 }
233
234 PRIVATE int
235 setRXAGC(int n, char **p) {
236   int setit = atoi(p[0]);
237   switch (setit) {
238   case agcOFF:
239     rx.agc.gen->mode = agcOFF;
240     rx.agc.flag = TRUE;
241     break;
242   case agcSLOW:
243     rx.agc.gen->mode = agcSLOW;
244     rx.agc.gen->hang = 10;
245     rx.agc.flag = TRUE;
246     break;
247   case agcMED:
248     rx.agc.gen->mode = agcMED;
249     rx.agc.gen->hang = 6;
250     rx.agc.flag = TRUE;
251     break;
252   case agcFAST:
253     rx.agc.gen->mode = agcFAST;
254     rx.agc.gen->hang = 3;
255     rx.agc.flag = TRUE;
256     break;
257   case agcLONG:
258     rx.agc.gen->mode = agcLONG;
259     rx.agc.gen->hang = 23;
260     rx.agc.flag = TRUE;
261     break;
262   }
263   return 0;
264 }
265
266 PRIVATE int
267 setRXAGCCompression(int n, char **p) {
268   REAL rxcompression = atof(p[0]);
269   rx.agc.gen->gain.top = pow(10.0 , rxcompression * 0.05);
270   return 0;
271 }
272
273 PRIVATE int
274 setRXAGCHang(int n, char **p) {
275   int hang = atoi(p[0]);
276   rx.agc.gen->hang =
277     max(1,
278         min(23,
279             hang * uni.samplerate / (1e3 * uni.buflen)));
280   return 0;
281 }
282
283 PRIVATE int
284 setRXAGCLimit(int n, char **p) {
285   REAL limit = atof(p[0]);
286   rx.agc.gen->gain.lim = 0.001 * limit;
287   return 0;
288 }
289
290 PRIVATE int
291 setTXAGC(int n, char **p) {
292   int setit = atoi(p[0]);
293   switch (setit) {
294   case agcOFF:
295     tx.agc.gen->mode = agcOFF;
296     tx.agc.flag = FALSE;
297     break;
298   case agcSLOW:
299     tx.agc.gen->mode = agcSLOW;
300     tx.agc.gen->hang = 10;
301     tx.agc.flag = TRUE;
302     break;
303   case agcMED:
304     tx.agc.gen->mode = agcMED;
305     tx.agc.gen->hang = 6;
306     tx.agc.flag = TRUE;
307     break;
308   case agcFAST:
309     tx.agc.gen->mode = agcFAST;
310     tx.agc.gen->hang = 3;
311     tx.agc.flag = TRUE;
312     break;
313   case agcLONG:
314     tx.agc.gen->mode = agcLONG;
315     tx.agc.gen->hang = 23;
316     tx.agc.flag = TRUE;
317     break;
318   }
319   return 0;
320 }
321
322 PRIVATE int
323 setTXAGCCompression(int n, char **p) {
324   REAL txcompression = atof(p[0]);
325   tx.agc.gen->gain.top = pow(10.0 , txcompression * 0.05);
326   return 0;
327 }
328
329 PRIVATE int
330 setTXAGCFF(int n, char **p) {
331   tx.spr.flag = atoi(p[0]);
332   return 0;
333 }
334
335 PRIVATE int
336 setTXAGCFFCompression(int n, char **p) {
337   REAL txcompression = atof(p[0]);
338   tx.spr.gen->MaxGain = pow(10.0 , txcompression * 0.05);
339   return 0;
340 }
341
342 PRIVATE int
343 setTXAGCHang(int n, char **p) {
344   int hang = atoi(p[0]);
345   tx.agc.gen->hang =
346     max(1,
347         min(23,
348             hang * uni.samplerate / (1e3 * uni.buflen)));
349   return 0;
350 }
351
352 PRIVATE int
353 setTXAGCLimit(int n, char **p) {
354   REAL limit = atof(p[0]);
355   tx.agc.gen->gain.lim = 0.001 * limit;
356   return 0;
357 }
358
359 PRIVATE int
360 setTXSpeechCompression(int n, char **p) {
361   tx.spr.flag = atoi(p[0]);
362   return 0;
363 }
364
365 PRIVATE int
366 setTXSpeechCompressionGain(int n, char **p) {
367   tx.spr.gen->MaxGain = dB2lin(atof(p[0]));
368   return 0;
369 }
370
371 //============================================================
372
373 PRIVATE int
374 f2x(REAL f) {
375   REAL fix = tx.filt.ovsv->fftlen * f / uni.samplerate;
376   return (int) (fix + 0.5);
377 }
378
379 //------------------------------------------------------------
380
381 PRIVATE void
382 apply_txeq_band(REAL lof, REAL dB, REAL hif) {
383   int i,
384       lox = f2x(lof),
385       hix = f2x(hif),
386       l = tx.filt.ovsv->fftlen;
387   REAL g = dB2lin(dB);
388   COMPLEX *src = tx.filt.save,
389           *trg = tx.filt.ovsv->zfvec;
390    for (i = lox; i < hix; i++) {
391     trg[i] = Cscl(src[i], g);
392     if (i) {
393       int j = l - i;
394       trg[j] = Cscl(src[j], g);
395     }
396   }
397 }
398
399 // typical:
400 // 0 dB1 75 dB2 150 dB3 300 dB4 600 dB5 1200 dB6 2000 dB7 2800 dB8 3600
401 // approximates W2IHY bandcenters.
402 // no args, revert to no EQ.
403 PRIVATE int
404 setTXEQ(int n, char **p) {
405   if (n < 3) {
406     // revert to no EQ
407     memcpy((char *) tx.filt.ovsv->zfvec,
408            (char *) tx.filt.save,
409            tx.filt.ovsv->fftlen * sizeof(COMPLEX));
410     return 0;
411   } else {
412     int i;
413     REAL lof = atof(p[0]);
414     for (i = 0; i < n - 2; i += 2) {
415       REAL dB = atof(p[i + 1]),
416            hif = atof(p[i + 2]);
417       if (lof < 0.0 || hif <= lof) return -1;
418       apply_txeq_band(lof, dB, hif);
419       lof = hif;
420     }
421     return 0;
422   }
423 }
424
425 //------------------------------------------------------------
426
427 PRIVATE void
428 apply_rxeq_band(REAL lof, REAL dB, REAL hif) {
429   int i,
430       lox = f2x(lof),
431       hix = f2x(hif),
432       l = rx.filt.ovsv->fftlen;
433   REAL g = dB2lin(dB);
434   COMPLEX *src = rx.filt.save,
435           *trg = rx.filt.ovsv->zfvec;
436   for (i = lox; i < hix; i++) {
437     trg[i] = Cscl(src[i], g);
438     if (i) {
439       int j = l - i;
440       trg[j] = Cscl(src[j], g);
441     }
442   }
443 }
444
445 PRIVATE int
446 setRXEQ(int n, char **p) {
447   if (n < 3) {
448     // revert to no EQ
449     memcpy((char *) rx.filt.ovsv->zfvec,
450            (char *) rx.filt.save,
451            rx.filt.ovsv->fftlen * sizeof(COMPLEX));
452     return 0;
453   } else {
454     int i;
455     REAL lof = atof(p[0]);
456     for (i = 0; i < n - 2; i += 2) {
457       REAL dB = atof(p[i + 1]),
458            hif = atof(p[i + 2]);
459       if (lof < 0.0 || hif <= lof) return -1;
460       apply_rxeq_band(lof, dB, hif);
461       lof = hif;
462     }
463     return 0;
464   }
465 }
466
467 //============================================================
468
469 PRIVATE int
470 setANFvals(int n, char **p) {
471   int taps  = atoi(p[0]),
472       delay = atoi(p[1]);
473   REAL gain = atof(p[2]),
474        leak = atof(p[3]);
475   rx.anf.gen->adaptive_filter_size = taps;
476   rx.anf.gen->delay = delay;
477   rx.anf.gen->adaptation_rate = gain;
478   rx.anf.gen->leakage = leak;
479   return 0;
480 }
481
482 PRIVATE int
483 setNRvals(int n, char **p) {
484   int taps  = atoi(p[0]),
485       delay = atoi(p[1]);
486   REAL gain = atof(p[2]),
487        leak = atof(p[3]);
488   rx.anr.gen->adaptive_filter_size = taps;
489   rx.anr.gen->delay = delay;
490   rx.anr.gen->adaptation_rate = gain;
491   rx.anr.gen->leakage = leak;
492   return 0;
493 }
494
495 PRIVATE int
496 setcorrectIQ(int n, char **p) {
497   int phaseadjustment = atoi(p[0]),
498       gainadjustment  = atoi(p[1]);
499   rx.iqfix->phase = 0.001 * (REAL) phaseadjustment;
500   rx.iqfix->gain  = 1.0+ 0.001 * (REAL) gainadjustment;
501   return 0;
502 }
503
504 PRIVATE int
505 setcorrectIQphase(int n, char **p) {
506   int phaseadjustment = atoi(p[0]);
507   rx.iqfix->phase = 0.001 * (REAL) phaseadjustment;
508   return 0;
509 }
510
511 PRIVATE int
512 setcorrectIQgain(int n, char **p) {
513   int gainadjustment = atoi(p[0]);
514   rx.iqfix->gain = 1.0 + 0.001 * (REAL) gainadjustment;
515   return 0;
516 }
517
518 PRIVATE int
519 setSquelch(int n, char **p) {
520   rx.squelch.thresh = -atof(p[0]);
521   return 0;
522 }
523
524 PRIVATE int
525 setSquelchSt(int n, char **p) {
526   rx.squelch.flag = atoi(p[0]);
527   return 0;
528 }
529
530 PRIVATE int
531 setTRX(int n, char **p) {
532   uni.mode.trx = atoi(p[0]);
533   return 0;
534 }
535
536 PRIVATE int
537 setRunState(int n, char **p) {
538   RUNMODE rs = (RUNMODE) atoi(p[0]);
539   top.state = rs;
540   return 0;
541 }
542
543 PRIVATE int
544 setSpotToneVals(int n, char **p) {
545   REAL gain = atof(p[0]),
546        freq = atof(p[1]),
547        rise = atof(p[2]),
548        fall = atof(p[3]);
549   setSpotToneGenVals(rx.spot.gen, gain, freq, rise, fall);
550   return 0;
551 }
552
553 PRIVATE int
554 setSpotTone(int n, char **p) {
555   if (atoi(p[0])) {
556     SpotToneOn(rx.spot.gen);
557     rx.spot.flag = TRUE;
558   } else
559     SpotToneOff(rx.spot.gen);
560   return 0;
561 }
562
563 PRIVATE int
564 setRXPreScl(int n, char **p) {
565   rx.scl.pre.flag = atoi(p[0]);
566   return 0;
567 }
568
569 PRIVATE int
570 setRXPreSclVal(int n, char **p) {
571   rx.scl.pre.val = dB2lin(atof(p[0]));
572   return 0;
573 }
574
575 PRIVATE int
576 setTXPreScl(int n, char **p) {
577   tx.scl.pre.flag = atoi(p[0]);
578   return 0;
579 }
580
581 PRIVATE int
582 setTXPreSclVal(int n, char **p) {
583   tx.scl.pre.val = dB2lin(atof(p[0]));
584   return 0;
585 }
586
587 PRIVATE int
588 setRXPostScl(int n, char **p) {
589   rx.scl.post.flag = atoi(p[0]);
590   return 0;
591 }
592
593 PRIVATE int
594 setRXPostSclVal(int n, char **p) {
595   rx.scl.post.val = dB2lin(atof(p[0]));
596   return 0;
597 }
598
599 PRIVATE int
600 setTXPostScl(int n, char **p) {
601   tx.scl.post.flag = atoi(p[0]);
602   return 0;
603 }
604
605 PRIVATE int
606 setTXPostSclVal(int n, char **p) {
607   tx.scl.post.val = dB2lin(atof(p[0]));
608   return 0;
609 }
610
611 PRIVATE int
612 setFinished(int n, char **p) {
613   top.running = FALSE;
614   pthread_cancel(top.thrd.trx.id);
615   pthread_cancel(top.thrd.upd.id);
616   pthread_cancel(top.thrd.mon.id);
617   return 0;
618 }
619
620 // next-trx-mode [nbufs-to-zap]
621 PRIVATE int
622 setSWCH(int n, char **p) {
623   top.swch.trx.next = atoi(p[0]);
624   if (n > 1) top.swch.bfct.want = atoi(p[1]);
625   else top.swch.bfct.want = 0;
626   top.swch.bfct.have = 0;
627   top.swch.run.last = top.state;
628   top.state = RUN_SWCH;
629   return 0;
630 }
631
632 PRIVATE int
633 setMonDump(int n, char **p) {
634   sem_post(&top.sync.mon.sem);
635   return 0;
636 }
637
638 PRIVATE int
639 setRingBufferReset(int n, char **p) {
640   extern void clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes);
641   jack_ringbuffer_reset(top.jack.ring.i.l);
642   jack_ringbuffer_reset(top.jack.ring.i.r);
643   jack_ringbuffer_reset(top.jack.ring.o.l);
644   jack_ringbuffer_reset(top.jack.ring.o.r);
645   clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes);
646   clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes);
647   return 0;
648 }
649
650 //========================================================================
651
652 #include <thunk.h>
653
654 CTE update_cmds[] = {
655   {"setANF", setANF},
656   {"setANFvals", setANFvals},
657   {"setBIN", setBIN},
658   {"setFilter", setFilter},
659   {"setFinished", setFinished},
660   {"setMode", setMode},
661   {"setNB", setNB},
662   {"setNBvals", setNBvals},
663   {"setNR", setNR},
664   {"setNRvals", setNRvals},
665   {"setOsc", setOsc},
666   {"setRXAGC", setRXAGC},
667   {"setRXAGCCompression", setRXAGCCompression},
668   {"setRXAGCHang", setRXAGCHang},
669   {"setRXAGCLimit", setRXAGCLimit},
670   {"setRXEQ", setRXEQ},
671   {"setRXPostScl", setRXPostScl},
672   {"setRXPostSclVal", setRXPostSclVal},
673   {"setRXPreScl", setRXPreScl},
674   {"setRXPreSclVal", setRXPreSclVal},
675   {"setRunState", setRunState},
676   {"setSDROM", setSDROM},
677   {"setSDROMvals",setSDROMvals},
678   {"setSWCH", setSWCH},
679   {"setSampleRate", setSampleRate},
680   {"setSpotTone", setSpotTone},
681   {"setSpotToneVals", setSpotToneVals},
682   {"setSquelch", setSquelch},
683   {"setSquelchSt", setSquelchSt},
684   {"setTRX", setTRX},
685   {"setTXAGC", setTXAGC},
686   {"setTXAGCCompression", setTXAGCCompression},
687   {"setTXAGCFFCompression",setTXAGCFFCompression},
688   {"setTXAGCHang", setTXAGCHang},
689   {"setTXAGCLimit", setTXAGCLimit},
690   {"setTXEQ", setTXEQ},
691   {"setTXPostScl", setTXPostScl},
692   {"setTXPostSclVal", setTXPostSclVal},
693   {"setTXPreScl", setTXPreScl},
694   {"setTXPreSclVal", setTXPreSclVal},
695   {"setTXSpeechCompression", setTXSpeechCompression},
696   {"setTXSpeechCompressionGain", setTXSpeechCompressionGain},
697   {"setcorrectIQ", setcorrectIQ},
698   {"setcorrectIQgain", setcorrectIQgain},
699   {"setcorrectIQphase", setcorrectIQphase},
700   {"setfixedAGC", setfixedAGC},
701   {"setMonDump", setMonDump},
702   {"setRingBufferReset", setRingBufferReset},
703   { 0, 0 }
704 };
705
706 //........................................................................
707
708 int
709 do_update(char *str, FILE *log) {
710   SPLIT splt = &uni.update.splt;
711
712   split(splt, str);
713
714   if (NF(splt) < 1) return -1;
715
716   else {
717     Thunk thk = Thunk_lookup(update_cmds, F(splt, 0));
718     if (!thk) return -1;
719     else {
720       int val;
721
722       sem_wait(&top.sync.upd.sem);
723       val = (*thk)(NF(splt) - 1, Fptr(splt, 1));
724       sem_post(&top.sync.upd.sem);
725
726       if (log) {
727         int i;
728         char *s = since(&top.start_tv);
729         fprintf(log, "update[%s]: returned %d from", s, val);
730         for (i = 0; i < NF(splt); i++)
731           fprintf(log, " %s", F(splt, i));
732         putc('\n', log);
733         fflush(log);
734       }
735
736       return val;
737     }
738   }
739 }