LAL  7.5.0.1-08ee4f4
ComputeStrainDMT.c
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2007 Xavier Siemens
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with with program; see the file COPYING. If not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA
18 */
19 
20 #include <complex.h>
21 #include <math.h>
22 #include <stdio.h>
23 #include <lal/LALStdlib.h>
24 #include <lal/LALConstants.h>
25 #include <lal/Calibration.h>
26 #include <lal/LALDatatypes.h>
27 #include <lal/LALStdlib.h>
28 #include <lal/LALStdio.h>
29 #include <lal/AVFactories.h>
30 #include <lal/Window.h>
31 #include <lal/BandPassTimeSeries.h>
32 #include <lal/TimeFreqFFT.h>
33 #include <lal/RealFFT.h>
34 #include <lal/AVFactories.h>
35 #include <lal/FrequencySeries.h>
36 #include <lal/TimeSeries.h>
37 
38 #include <gsl/gsl_interp.h>
39 #include <gsl/gsl_spline.h>
40 
41 #define MAXALPHAS 10000
42 
43 #define N_FIR_LP_ALPHAS 8192
44 #define fhigh_FIRLP_ALPHAS 0.00001220703125
45 
46 #define N_FIR_LP 100
47 #define fhigh_FIRLP .9
48 
49 #define N_FIR_HP 2000
50 #define fhigh_FIRHP 0.00244140625
51 
55  StrainIn *input
56  )
57 {
58 /* Inverse sensing, servo, analog actuation, digital x
59 actuation digital y actuation */
60 static REAL8TimeSeries uphR,hCX,hCY,ALPHAS,upALPHAS;
61 int p, NWingsR,NWingsC;
62 REAL8IIRFilter LPFIR;
63 REAL8IIRFilter HPFIR;
64 REAL8IIRFilter ALPHASLPFIR;
65 REAL8IIRFilter *CinvWings=NULL, *DWings=NULL, *AAWings=NULL, *AXWings=NULL, *AYWings=NULL;
66 
67 
70 
71  LALGetFactors(status->statusPtr, output, input);
73 
74  LALMakeFIRLP(status->statusPtr, &LPFIR, input->CinvUSF);
76 
77  LALMakeFIRHP(status->statusPtr, &HPFIR);
79 
80  /* Create vectors that will hold the residual, control and net strain signals */
81  LALDCreateVector(status->statusPtr,&hCX.data,input->AS_Q.data->length);
83  LALDCreateVector(status->statusPtr,&hCY.data,input->AS_Q.data->length);
85 
86  NWingsR = (int)(input->wings/input->AS_Q.deltaT + 0.5) * input->CinvUSF;
87  NWingsC = (int)(input->wings/input->AS_Q.deltaT + 0.5);
88 
89  /* copy AS_Q input into residual strain as double */
90  for (p=0; p<(int)output->hR.data->length; p++)
91  {
92  output->hR.data->data[p]=input->DARM_ERR.data->data[p];
93  }
94 
95  /* copy DARM_ERR input into control strain as double */
96  for (p=0; p<(int)output->hC.data->length; p++)
97  {
98  output->hC.data->data[p]=input->DARM_ERR.data->data[p];
99  }
100 
101  /* unit impulse */
102  if (input->delta)
103  {
104  for (p=0; p<(int)output->hR.data->length; p++)
105  {
106  output->hR.data->data[p]=0;
107  }
108  output->hR.data->data[output->hC.data->length/2]=1.0;
109  }
110 
111  /* unit impulse */
112  if (input->delta)
113  {
114  for (p=0; p<(int)output->hC.data->length; p++)
115  {
116  output->hC.data->data[p]=0;
117  }
118  output->hC.data->data[output->hC.data->length/2]=1.0;
119  }
120 
121  /* ---------- Compute Residual Strain -------------*/
122 
123  LALDCreateVector(status->statusPtr,&uphR.data,input->CinvUSF*input->AS_Q.data->length);
125  uphR.deltaT=input->AS_Q.deltaT/input->CinvUSF;
126 
127  /* then we upsample (and smooth it with a low pass filter) */
128  if(XLALUpsample(&uphR, &(output->hR), input->CinvUSF))
129  {
130  ABORT(status,117,"Broke upsampling hR");
131  }
132 
133  /* apply delay (actually an advance) */
134  for (p=0; p<(int)uphR.data->length+input->CinvDelay; p++){
135  uphR.data->data[p]=uphR.data->data[p-input->CinvDelay];
136  }
137 
138  /* An odd filter with N points introduces an (N-1)/2 delay */
139  /* apply advance to compensate for FIR delay */
140  for (p=0; p<(int)uphR.data->length-(2*N_FIR_LP); p++){
141  uphR.data->data[p]=uphR.data->data[p+(2*N_FIR_LP)];
142  }
143  /* Low pass filter twice to smooth time series */
144  LALIIRFilterREAL8Vector(status->statusPtr,uphR.data,&(LPFIR));
146  LALIIRFilterREAL8Vector(status->statusPtr,uphR.data,&(LPFIR));
148 
149  /* ===================== */
150  /* CAREFUL FILTERING: FILTER UP TO WINGS, THEN COPY FILTERS THEN CONTINUE UNTIL END */
151  /* filter only up until the wings */
152  for(p = 0; p < input->NCinv; p++){
153  int k;
154  for(k = NWingsR/2; k < (int)uphR.data->length-3*NWingsR/2; k++){
155  uphR.data->data[k]=LALDIIRFilter(uphR.data->data[k], &(input->Cinv[p]));
156  }
157  }
158  /* Here what I need to record filter histories */
159  LALCopyFilter(status->statusPtr, &CinvWings, input->Cinv, input->NCinv);
161  /* then we filter wings as well */
162  for(p = 0; p < input->NCinv; p++){
163  int k;
164  for(k = uphR.data->length-3*NWingsR/2; k < (int)uphR.data->length-NWingsR/2; k++){
165  uphR.data->data[k]=LALDIIRFilter(uphR.data->data[k], &CinvWings[p]);
166  }
167  }
168  /* Then we need to destroy the filter */
169  LALFreeFilter(status->statusPtr,CinvWings,input->NCinv);
171  /* ===================== */
172 
173  /* apply advance to compensate for Low Pass FIR delay */
174  for (p=0; p<(int)uphR.data->length-(2*N_FIR_LP); p++){
175  uphR.data->data[p]=uphR.data->data[p+(2*N_FIR_LP)];
176  }
177  /* Low pass filter twice to smooth time series */
178  LALIIRFilterREAL8Vector(status->statusPtr,uphR.data,&(LPFIR));
180  LALIIRFilterREAL8Vector(status->statusPtr,uphR.data,&(LPFIR));
182 
183  /* then we downsample and voila' */
184  for (p=0; p<(int)output->hR.data->length; p++) {
185  output->hR.data->data[p]=uphR.data->data[p*input->CinvUSF];
186  }
187 
188  LALDDestroyVector(status->statusPtr,&uphR.data);
190 
191  /* Create time series that hold alpha time series and upsampled alpha time-series */
192  LALDCreateVector(status->statusPtr,&ALPHAS.data,output->alpha.data->length);
194  LALDCreateVector(status->statusPtr,&upALPHAS.data,input->AS_Q.data->length);
196  upALPHAS.deltaT=input->AS_Q.deltaT;
197 
198  /* copy factors into time series */
199  for (p=0; p<(int)ALPHAS.data->length; p++)
200  {
201  ALPHAS.data->data[p]=creal(output->alphabeta.data->data[p]);
202  }
203 
204  /* upsample using a linear interpolation */
205  if(XLALUpsampleLinear(&upALPHAS, &ALPHAS, (int) (output->alphabeta.deltaT/input->AS_Q.deltaT+0.5)))
206  {
207  ABORT(status,117,"Broke upsampling Alphas");
208  }
209 
210  /* Generate alphas LP filter to smooth linearly interpolated factors time series */
211  LALMakeFIRLPALPHAS(status->statusPtr, &ALPHASLPFIR);
213 
214  /* Note that I do not compensate for delay
215  if factors are computed every second then everything is fine */
216  if (input->fftconv)
217  {
218  LALFFTFIRFilter(status->statusPtr,&upALPHAS,&ALPHASLPFIR);
220  }else{
221  LALIIRFilterREAL8Vector(status->statusPtr,upALPHAS.data,&(ALPHASLPFIR));
223  }
224 
225  /* finally we divide by alpha*beta */
226  if (input->usefactors)
227  {
228  if(XLALDivideTimeSeries(&(output->hR), &upALPHAS))
229  {
230  ABORT(status,116,"Broke at hR/alpha");
231  }
232  }
233 
234  if (input->outalphas)
235  {
236  /* set output to alphas */
237  for (p=0; p<(int)output->hR.data->length; p++) {
238  output->hR.data->data[p]=upALPHAS.data->data[p];
239  }
240  }
241 
242  /* destroy low and high pass filters */
243  LALDDestroyVector(status->statusPtr,&(ALPHASLPFIR.directCoef));
245  LALDDestroyVector(status->statusPtr,&(ALPHASLPFIR.recursCoef));
247  LALDDestroyVector(status->statusPtr,&(ALPHASLPFIR.history));
249 
250  /* destroy both alphas time series */
251  LALDDestroyVector(status->statusPtr,&upALPHAS.data);
253  LALDDestroyVector(status->statusPtr,&ALPHAS.data);
255 
256  /* ---------- Compute Control Strain -------------*/
257 
258  /* first implement the time delay filter; start at end to avoid overwriting */
259  for (p = (int)output->hC.data->length-1; p >= input->AADelay; p--){
260  output->hC.data->data[p]=output->hC.data->data[p-input->AADelay];
261  }
262 
263  /* then apply advance to compensate for FIR delay */
264  for (p=0; p<(int)output->hC.data->length-(2*N_FIR_HP); p++){
265  output->hC.data->data[p]=output->hC.data->data[p+(2*N_FIR_HP)];
266  }
267  /* then high pass filter DARM_ERR */
268  if (input->fftconv)
269  {
270  LALFFTFIRFilter(status->statusPtr,&(output->hC),&(HPFIR));
272  LALFFTFIRFilter(status->statusPtr,&(output->hC),&(HPFIR));
274  }else{
275  LALIIRFilterREAL8Vector(status->statusPtr,output->hC.data,&(HPFIR));
277  LALIIRFilterREAL8Vector(status->statusPtr,output->hC.data,&(HPFIR));
279  }
280 
281  /* Filter through digital servo */
282  /* ===================== */
283  /* CAREFUL FILTERING: FILTER UP TO WINGS, THEN COPY FILTERS THEN CONTINUE UNTIL END*/
284  /* we filter but only up until the wings */
285  for(p = 0; p < input->ND; p++){
286  int k;
287  for(k = NWingsC/2; k < (int)output->hC.data->length-3*NWingsC/2; k++){
288  output->hC.data->data[k]=LALDIIRFilter(output->hC.data->data[k], &(input->D[p]));
289  }
290  }
291  /* Here what I need to record filter histories */
292  LALCopyFilter(status->statusPtr, &DWings, input->D, input->ND);
294  /* then we filter wings as well */
295  for(p = 0; p < input->ND; p++){
296  int k;
297  for(k = output->hC.data->length-3*NWingsC/2; k < (int)output->hC.data->length-NWingsC/2; k++){
298  output->hC.data->data[k]=LALDIIRFilter(output->hC.data->data[k], &DWings[p]);
299  }
300  }
301  /* Then we need to destroy the filter */
302  LALFreeFilter(status->statusPtr,DWings,input->ND);
304  /* ===================== */
305 
306  /* At this point I have something very similar to darm_ctrl in hC */
307  /* add the calibration lines */
308  if (!input->delta && (input->DARM_ERR.deltaT == input->EXC.deltaT))
309  {
310  int k;
311  for(k = 0; k < (int)output->hC.data->length; k++){
312  output->hC.data->data[k] += input->EXC.data->data[k];
313  }
314  }
315  else
316  {
317  fprintf(stdout, "Warning: Not adding calibration lines to control signal.\n");
318  }
319 
320 
321  /* Filter through analog actuation */
322  /* ===================== */
323  /* CAREFUL FILTERING: FILTER UP TO WINGS, THEN COPY FILTERS THEN CONTINUE UNTIL END */
324  /* we filter but only up until the wings */
325  for(p = 0; p < input->NAA; p++){
326  int k;
327  for(k = NWingsC/2; k < (int)output->hC.data->length-3*NWingsC/2; k++){
328  output->hC.data->data[k]=LALDIIRFilter(output->hC.data->data[k], &(input->AA[p]));
329  }
330  }
331  /* Here what I need to record filter histories */
332  LALCopyFilter(status->statusPtr, &AAWings, input->AA, input->NAA);
334  /* then we filter wings as well */
335  for(p = 0; p < input->NAA; p++){
336  int k;
337  for(k = output->hC.data->length-3*NWingsC/2; k < (int)output->hC.data->length-NWingsC/2; k++){
338  output->hC.data->data[k]=LALDIIRFilter(output->hC.data->data[k], &AAWings[p]);
339  }
340  }
341  /* Then we need to destroy the filter */
342  LALFreeFilter(status->statusPtr,AAWings,input->NAA);
344  /* ===================== */
345 
346  /* Copy data into x and y time series for parallel filtering */
347  for (p=0; p < (int)output->hC.data->length; p++){
348  hCX.data->data[p] = output->hC.data->data[p];
349  hCY.data->data[p] = output->hC.data->data[p];
350  }
351 
352  /* Filter x-arm */
353  /* ===================== */
354  /* CAREFUL FILTERING: FILTER UP TO WINGS, THEN COPY FILTERS THEN CONTINUE UNTIL END */
355  /* we filter only up until the wings */
356  for(p = 0; p < input->NAX; p++){
357  int k;
358  for(k = NWingsC/2; k < (int)hCX.data->length-3*NWingsC/2; k++){
359  hCX.data->data[k]=LALDIIRFilter(hCX.data->data[k], &(input->AX[p]));
360  }
361  }
362  /* Here what I need to record filter histories */
363  LALCopyFilter(status->statusPtr, &AXWings, input->AX, input->NAX);
365  /* then we filter wings as well */
366  for(p = 0; p < input->NAX; p++){
367  int k;
368  for(k = hCX.data->length-3*NWingsC/2; k < (int)hCX.data->length-NWingsC/2; k++){
369  hCX.data->data[k]=LALDIIRFilter(hCX.data->data[k], &AXWings[p]);
370  }
371  }
372  /* Then we need to destroy the filter */
373  LALFreeFilter(status->statusPtr,AXWings,input->NAX);
375  /* ===================== */
376 
377 
378  /* Filter y-arm */
379  /* ===================== */
380  /* CAREFUL FILTERING: FILTER UP TO WINGS, THEN COPY FILTERS THEN CONTINUE UNTIL END */
381  /* we filter only up until the wings */
382  for(p = 0; p < input->NAY; p++){
383  int k;
384  for(k = NWingsC/2; k < (int)hCY.data->length-3*NWingsC/2; k++){
385  hCY.data->data[k]=LALDIIRFilter(hCY.data->data[k], &(input->AY[p]));
386  }
387  }
388  /* Here what I need to record filter histories */
389  LALCopyFilter(status->statusPtr, &AYWings, input->AY, input->NAY);
391  /* then we filter wings as well */
392  for(p = 0; p < input->NAY; p++){
393  int k;
394  for(k = hCY.data->length-3*NWingsC/2; k < (int)hCY.data->length-NWingsC/2; k++){
395  hCY.data->data[k]=LALDIIRFilter(hCY.data->data[k], &AYWings[p]);
396  }
397  }
398  /* Then we need to destroy the filter */
399  LALFreeFilter(status->statusPtr,AYWings,input->NAY);
401  /* ===================== */
402 
403  /* add them together to make the total control signal */
404  for (p=0; p < (INT4)output-> h.data->length; p++) {
405  output->hC.data->data[p]=(hCX.data->data[p]-hCY.data->data[p]);
406  }
407 
408 
409  /* ---------- Compute Net Strain -------------*/
410 
411  /* add x-arm and y-arm and voila' */
412  for (p=0; p< (int)output->h.data->length; p++){
413  output->h.data->data[p] = output->hC.data->data[p]+output->hR.data->data[p];
414  }
415 
416  /* ------------------------------------------*/
417 
418  /* destroy vectors that hold the data */
419  LALDDestroyVector(status->statusPtr,&hCX.data);
421  LALDDestroyVector(status->statusPtr,&hCY.data);
423 
424  /* destroy low and high pass filters */
425  LALDDestroyVector(status->statusPtr,&(LPFIR.directCoef));
427  LALDDestroyVector(status->statusPtr,&(LPFIR.recursCoef));
429  LALDDestroyVector(status->statusPtr,&(LPFIR.history));
431  LALDDestroyVector(status->statusPtr,&(HPFIR.directCoef));
433  LALDDestroyVector(status->statusPtr,&(HPFIR.recursCoef));
435  LALDDestroyVector(status->statusPtr,&(HPFIR.history));
437 
439  RETURN( status );
440 
441 }
442 
443 
444 /*******************************************************************************/
446 {
447  int n;
450 
451  for(n=0;n<ORDER;n++){
452  LALDDestroyVector(status->statusPtr,&(F2[n].directCoef));
454  LALDDestroyVector(status->statusPtr, &(F2[n].recursCoef));
456  LALDDestroyVector(status->statusPtr,&(F2[n].history));
458  }
459  LALFree(F2);
460 
462  RETURN( status );
463 }
464 
465 /*******************************************************************************/
466 
468 {
469  REAL8IIRFilter *F2Array;
470  int n;
473 
474  /* Allocate inverse sensing funtion filters */
475  *F2=F2Array=(REAL8IIRFilter *)LALMalloc(sizeof(REAL8IIRFilter) * ORDER);
476 
477  for(n = 0; n < ORDER; n++)
478  {
479  int l;
480  F2Array[n].directCoef=NULL;
481  F2Array[n].recursCoef=NULL;
482  F2Array[n].history=NULL;
483 
484  LALDCreateVector(status->statusPtr,&(F2Array[n].directCoef),F1[n].directCoef->length);
486  LALDCreateVector(status->statusPtr,&(F2Array[n].recursCoef),F1[n].recursCoef->length);
488  LALDCreateVector(status->statusPtr,&(F2Array[n].history),F1[n].history->length);
490 
491  for(l=0;l<(int)F1[n].directCoef->length;l++)
492  F2Array[n].directCoef->data[l] = F1[n].directCoef->data[l];
493  for(l=0;l<(int)F1[n].recursCoef->length;l++)
494  F2Array[n].recursCoef->data[l] = F1[n].recursCoef->data[l];
495  for(l=0;l<(int)F1[n].history->length;l++)
496  F2Array[n].history->data[l] = F1[n].history->data[l];
497  }
498 
500  RETURN( status );
501 }
void LALMakeFIRLP(LALStatus *status, REAL8IIRFilter *G, int USF)
void LALMakeFIRHP(LALStatus *status, REAL8IIRFilter *G)
int XLALUpsample(REAL8TimeSeries *uphR, REAL8TimeSeries *hR, int up_factor)
void LALGetFactors(LALStatus *status, StrainOut *output, StrainIn *input)
void LALMakeFIRLPALPHAS(LALStatus *status, REAL8IIRFilter *G)
int XLALUpsampleLinear(REAL8TimeSeries *uphR, REAL8TimeSeries *hR, int up_factor)
int XLALDivideTimeSeries(REAL8TimeSeries *hR, REAL8TimeSeries *ALPHAS)
void LALFFTFIRFilter(LALStatus *status, REAL8TimeSeries *tseries, REAL8IIRFilter *FIR)
#define N_FIR_HP
#define N_FIR_LP
void LALCopyFilter(LALStatus *status, REAL8IIRFilter **F2, REAL8IIRFilter *F1, int ORDER)
void LALFreeFilter(LALStatus *status, REAL8IIRFilter *F2, int ORDER)
void LALComputeStrainDMT(LALStatus *status, StrainOut *output, StrainIn *input)
#define LALDIIRFilter(x, f)
Definition: IIRFilter.h:216
#define LALMalloc(n)
Definition: LALMalloc.h:93
#define LALFree(p)
Definition: LALMalloc.h:96
#define ABORT(statusptr, code, mesg)
#define CHECKSTATUSPTR(statusptr)
#define ATTATCHSTATUSPTR(statusptr)
#define DETATCHSTATUSPTR(statusptr)
#define INITSTATUS(statusptr)
#define RETURN(statusptr)
#define fprintf
void LALIIRFilterREAL8Vector(LALStatus *status, REAL8Vector *vector, REAL8IIRFilter *filter)
WARNING: THIS FUNCTION IS OBSOLETE.
int32_t INT4
Four-byte signed integer.
void LALDCreateVector(LALStatus *, REAL8Vector **, UINT4)
void LALDDestroyVector(LALStatus *, REAL8Vector **)
LAL status structure, see The LALStatus structure for more details.
Definition: LALDatatypes.h:947
REAL4Sequence * data
The sequence of sampled data.
Definition: LALDatatypes.h:576
REAL8 deltaT
The time step between samples of the time series in seconds.
Definition: LALDatatypes.h:573
REAL4 * data
Pointer to the data array.
Definition: LALDatatypes.h:150
UINT4 length
Number of elements in array.
Definition: LALDatatypes.h:149
This structure stores the direct and recursive REAL8 filter coefficients, as well as the history of t...
Definition: IIRFilter.h:168
REAL8Vector * history
The previous values of w.
Definition: IIRFilter.h:173
REAL8Vector * recursCoef
The recursive filter coefficients.
Definition: IIRFilter.h:172
REAL8Vector * directCoef
The direct filter coefficients.
Definition: IIRFilter.h:171
Time series of REAL8 data, see DATATYPE-TimeSeries types for more details.
Definition: LALDatatypes.h:580
REAL8Sequence * data
The sequence of sampled data.
Definition: LALDatatypes.h:586
REAL8 deltaT
The time step between samples of the time series in seconds.
Definition: LALDatatypes.h:583
REAL8 * data
Pointer to the data array.
Definition: LALDatatypes.h:159
UINT4 length
Number of elements in array.
Definition: LALDatatypes.h:158
UNDOCUMENTED.
Definition: Calibration.h:183
REAL4TimeSeries EXC
timeseries containing the excitation
Definition: Calibration.h:187
INT4 NAA
UNDOCUMENTED.
Definition: Calibration.h:218
REAL8IIRFilter * Cinv
Filters for inverse of sensing function.
Definition: Calibration.h:198
INT4 usefactors
UNDOCUMENTED.
Definition: Calibration.h:206
REAL8IIRFilter * D
Filters for analog actuation function.
Definition: Calibration.h:202
INT4 delta
UNDOCUMENTED.
Definition: Calibration.h:205
INT4 fftconv
UNDOCUMENTED.
Definition: Calibration.h:208
INT4 outalphas
UNDOCUMENTED.
Definition: Calibration.h:209
INT4 NAX
UNDOCUMENTED.
Definition: Calibration.h:219
REAL8IIRFilter * AY
Digital filters for y arm actuation function.
Definition: Calibration.h:215
REAL4TimeSeries AS_Q
timeseries containing ASQ
Definition: Calibration.h:184
INT4 AADelay
Overall analog actuation function delay.
Definition: Calibration.h:213
INT4 NAY
UNDOCUMENTED.
Definition: Calibration.h:220
INT4 wings
size of wings in seconds
Definition: Calibration.h:207
INT4 NCinv
Numbers of filters of each type.
Definition: Calibration.h:216
INT4 CinvUSF
Upsampling factor for sensing function.
Definition: Calibration.h:199
REAL8IIRFilter * AX
Digital filters for x arm actuation function.
Definition: Calibration.h:214
REAL8IIRFilter * AA
Filters for analog actuation function.
Definition: Calibration.h:212
INT4 CinvDelay
Overall inverse sensing function delay.
Definition: Calibration.h:200
INT4 ND
UNDOCUMENTED.
Definition: Calibration.h:217
REAL4TimeSeries DARM_ERR
timeseries containing DARM_ERR
Definition: Calibration.h:185
UNDOCUMENTED.
Definition: Calibration.h:171
void output(int gps_sec, int output_type)
Definition: tconvert.c:440