コード例 #1
0
int main(int argc, char *argv[]) {

    /* Cmd line */
    static struct option long_opts[] = {
        {"output",  1, NULL, 'o'},
        {"npulse",  1, NULL, 'n'},
        {"nbin",    1, NULL, 'b'},
        {"nthread", 1, NULL, 'j'},
        {"initial", 1, NULL, 'i'},
        {"final",   1, NULL, 'f'},
        {"time",    1, NULL, 'T'},
        {"length",  1, NULL, 'L'},
        {"src",     1, NULL, 's'},
        {"polyco",  1, NULL, 'p'},
        {"parfile", 1, NULL, 'P'},
        {"foldfreq",1, NULL, 'F'},
        {"cal",     0, NULL, 'C'},
        {"unsigned",0, NULL, 'u'},
        {"quiet",   0, NULL, 'q'},
        {"help",    0, NULL, 'h'},
        {0,0,0,0}
    };
    int opt, opti;
    int nbin=256, nthread=4, fnum_start=1, fnum_end=0;
    int quiet=0, raw_signed=1, use_polycos=1, cal=0;
    int npulse_per_file = 64;
    double start_time=0.0, process_time=0.0;
    double fold_frequency=0.0;
    char output_base[256] = "";
    char polyco_file[256] = "";
    char par_file[256] = "";
    char source[24];  source[0]='\0';
    while ((opt=getopt_long(argc,argv,"o:n:b:j:i:f:T:L:s:p:P:F:Cuqh",long_opts,&opti))!=-1) {
        switch (opt) {
            case 'o':
                strncpy(output_base, optarg, 255);
                output_base[255]='\0';
                break;
            case 'n':
                npulse_per_file = atoi(optarg);
                break;
            case 'b':
                nbin = atoi(optarg);
                break;
            case 'j':
                nthread = atoi(optarg);
                break;
            case 'i':
                fnum_start = atoi(optarg);
                break;
            case 'f':
                fnum_end = atoi(optarg);
                break;
            case 'T':
                start_time = atof(optarg);
                break;
            case 'L':
                process_time = atof(optarg);
                break;
            case 's':
                strncpy(source, optarg, 24);
                source[23]='\0';
                break;
            case 'p':
                strncpy(polyco_file, optarg, 255);
                polyco_file[255]='\0';
                use_polycos = 1;
                break;
            case 'P':
                strncpy(par_file, optarg, 255);
                par_file[255] = '\0';
                break;
            case 'F':
                fold_frequency = atof(optarg);
                use_polycos = 0;
                break;
            case 'C':
                cal = 1;
                use_polycos = 0;
                break;
            case 'u':
                raw_signed=0;
                break;
            case 'q':
                quiet=1;
                break;
            case 'h':
            default:
                usage();
                exit(0);
                break;
        }

    }
    if (optind==argc) { 
        usage();
        exit(1);
    }

    /* If no polyco/par file given, default to polyco.dat */
    if (use_polycos && (par_file[0]=='\0' && polyco_file[0]=='\0'))
        sprintf(polyco_file, "polyco.dat");

    /* Open first file */
    struct psrfits pf;
    strcpy(pf.basefilename, argv[optind]);
    pf.filenum = fnum_start;
    pf.tot_rows = pf.N = pf.T = pf.status = 0;
    pf.hdr.chan_dm = 0.0; // What if folding data that has been partially de-dispersed?
    pf.filename[0]='\0';
    int rv = psrfits_open(&pf);
    if (rv) { fits_report_error(stderr, rv); exit(1); }

    /* Check any constraints */
    if (pf.hdr.nbits!=8) { 
        fprintf(stderr, "Only implemented for 8-bit data (read nbits=%d).\n",
                pf.hdr.nbits);
        exit(1);
    }

    /* Check for calfreq */
    if (cal) {
        if (pf.hdr.cal_freq==0.0) {
            if (fold_frequency==0.0) {
                fprintf(stderr, "Error: Cal mode selected, but CAL_FREQ=0.  "
                        "Set cal frequency with -F\n");
                exit(1);
            } else {
                pf.hdr.cal_freq = fold_frequency;
            }
        } else {
            fold_frequency = pf.hdr.cal_freq;
        }
    }

    /* Set up output file */
    struct psrfits pf_out;
    memcpy(&pf_out, &pf, sizeof(struct psrfits));
    if (source[0]!='\0') { strncpy(pf_out.hdr.source, source, 24); }
    else { strncpy(source, pf.hdr.source, 24); source[23]='\0'; }
    if (output_base[0]=='\0') {
        /* Set up default output filename */
        if (start_time>0.0) 
            sprintf(output_base, "%s_SP_%s_%5.5d_%5.5d_%4.4d_%3.3d%s", 
                    pf_out.hdr.backend, 
                    pf_out.hdr.source, pf_out.hdr.start_day, 
                    (int)pf_out.hdr.start_sec, fnum_start,
                    (int)start_time,
                    cal ? "_cal" : "");
        else
            sprintf(output_base, "%s_SP_%s_%5.5d_%5.5d%s", pf_out.hdr.backend, 
                    pf_out.hdr.source, pf_out.hdr.start_day, 
                    (int)pf_out.hdr.start_sec, cal ? "_cal" : "");
    }
    strcpy(pf_out.basefilename, output_base);
    if (cal) {
        sprintf(pf_out.hdr.obs_mode, "CAL");
        sprintf(pf_out.hdr.cal_mode, "SYNC");
    } else
        sprintf(pf_out.hdr.obs_mode, "PSR");
    strncpy(pf_out.fold.parfile,par_file,255); pf_out.fold.parfile[255]='\0';
    pf_out.fptr = NULL;
    pf_out.filenum=0;
    pf_out.status=0;
    pf_out.hdr.nbin=nbin;
    pf_out.sub.FITS_typecode = TFLOAT;
    pf_out.sub.bytes_per_subint = sizeof(float) * 
        pf_out.hdr.nchan * pf_out.hdr.npol * pf_out.hdr.nbin;
    pf_out.multifile = 1;
    pf_out.quiet = 1;
    pf_out.rows_per_file = npulse_per_file;
    rv = psrfits_create(&pf_out);
    if (rv) { fits_report_error(stderr, rv); exit(1); }

    /* Alloc data buffers */
    pf.sub.dat_freqs = (float *)malloc(sizeof(float) * pf.hdr.nchan);
    pf_out.sub.dat_freqs = pf.sub.dat_freqs;
    pf.sub.dat_weights = (float *)malloc(sizeof(float) * pf.hdr.nchan);
    pf_out.sub.dat_weights = (float *)malloc(sizeof(float) * pf.hdr.nchan);
    pf.sub.dat_offsets = (float *)malloc(sizeof(float) 
            * pf.hdr.nchan * pf.hdr.npol);
    pf_out.sub.dat_offsets = (float *)malloc(sizeof(float) 
            * pf.hdr.nchan * pf.hdr.npol);
    pf.sub.dat_scales  = (float *)malloc(sizeof(float) 
            * pf.hdr.nchan * pf.hdr.npol);
    pf_out.sub.dat_scales  = (float *)malloc(sizeof(float) 
            * pf.hdr.nchan * pf.hdr.npol);
    pf_out.sub.data  = (unsigned char *)malloc(pf_out.sub.bytes_per_subint);

    /* Output scale/offset */
    int i, j, ipol, ichan;
    float offset_uv=0.0;  
    // Extra cross-term offset for GUPPI
    if (strcmp("GUPPI",pf.hdr.backend)==0) { 
        offset_uv=0.5;
        fprintf(stderr, "Found backend=GUPPI, setting offset_uv=%f\n",
                offset_uv);
    }
    // TODO: copy these from the input file
    for (ipol=0; ipol<pf.hdr.npol; ipol++) {
        for (ichan=0; ichan<pf.hdr.nchan; ichan++) {
            float offs = 0.0;
            if (ipol>1) offs = offset_uv;
            pf_out.sub.dat_scales[ipol*pf.hdr.nchan + ichan] = 1.0;
            pf_out.sub.dat_offsets[ipol*pf.hdr.nchan + ichan] = offs;
        }
    }
    for (i=0; i<pf.hdr.nchan; i++) { pf_out.sub.dat_weights[i]=1.0; }

    /* Read or make polycos */
    int npc=0, ipc=0;
    struct polyco *pc = NULL;
    if (use_polycos) {
        if (polyco_file[0]=='\0') {
            /* Generate from par file */
            npc = make_polycos(par_file, &pf.hdr, source, &pc);
            if (npc<=0) {
                fprintf(stderr, "Error generating polycos.\n");
                exit(1);
            }
            printf("Auto-generated %d polycos, src=%s\n", npc, source);
        } else {
            /* Read from polyco file */
            FILE *pcfile = fopen(polyco_file, "r");
            if (pcfile==NULL) { 
                fprintf(stderr, "Couldn't open polyco file.\n");
                exit(1);
            }
            npc = read_all_pc(pcfile, &pc);
            if (npc==0) {
                fprintf(stderr, "Error parsing polyco file.\n");
                exit(1);
            }
            fclose(pcfile);
        }
    } else {
        // Const fold period mode, generate a fake polyco?
        pc = (struct polyco *)malloc(sizeof(struct polyco));
        sprintf(pc[0].psr, "CONST");
        pc[0].mjd = (int)pf.hdr.MJD_epoch;
        pc[0].fmjd = fmod(pf.hdr.MJD_epoch,1.0);
        pc[0].rphase = 0.0;
        pc[0].f0 = fold_frequency;
        pc[0].nsite = 0; // Does this matter?
        pc[0].nmin = 24 * 60;
        pc[0].nc = 1;
        pc[0].rf = pf.hdr.fctr;
        pc[0].c[0] = 0.0;
        pc[0].used = 0;
        npc = 1;
    }
    int *pc_written = (int *)malloc(sizeof(int) * npc);
    for (i=0; i<npc; i++) pc_written[i]=0;

    /* Set up fold buf */
    struct foldbuf fb;
    fb.nchan = pf.hdr.nchan;
    fb.npol = pf.hdr.npol;
    fb.nbin = pf_out.hdr.nbin;
    malloc_foldbuf(&fb);
    clear_foldbuf(&fb);
    struct fold_args fargs;
    fargs.data = (char *)malloc(sizeof(char)*pf.sub.bytes_per_subint);
    fargs.fb = &fb;
    fargs.nsamp = 1;
    fargs.tsamp = pf.hdr.dt;
    fargs.raw_signed = raw_signed;

    /* Main loop */
    rv=0;
    int imjd;
    double fmjd, fmjd0=0, fmjd_samp, fmjd_epoch;
    long long cur_pulse=0, last_pulse=0;
    double psr_freq=0.0;
    int first_loop=1, first_data=1, sampcount=0, last_filenum=0;
    int bytes_per_sample = pf.hdr.nchan * pf.hdr.npol;
    signal(SIGINT, cc);
    while (run) { 

        /* Read data block */
        pf.sub.data = (unsigned char *)fargs.data;
        rv = psrfits_read_subint(&pf);
        if (rv) { 
            if (rv==FILE_NOT_OPENED) rv=0; // Don't complain on file not found
            run=0; 
            break; 
        }

        /* If we've passed final file, exit */
        if (fnum_end && pf.filenum>fnum_end) { run=0; break; }

        /* Get start date, etc */
        imjd = (int)pf.hdr.MJD_epoch;
        fmjd = (double)(pf.hdr.MJD_epoch - (long double)imjd);
        fmjd += (pf.sub.offs-0.5*pf.sub.tsubint)/86400.0;

        /* Select polyco set.
         * We'll assume same one is valid for whole data block.
         */
        if (use_polycos) {
            ipc = select_pc(pc, npc, source, imjd, fmjd);
            //ipc = select_pc(pc, npc, NULL, imjd, fmjd);
            if (ipc<0) { 
                fprintf(stderr, 
                        "No matching polycos (src=%s, imjd=%d, fmjd=%f)\n",
                        source, imjd, fmjd);
                break;
            }
        } else {
            ipc = 0;
        }
        pc[ipc].used = 1; // Mark this polyco set as used for folding

        /* First time stuff */
        if (first_loop) {
            fmjd0 = fmjd;
            psr_phase(&pc[ipc], imjd, fmjd, NULL, &last_pulse);
            pf_out.sub.offs=0.0;
            first_loop=0;
            for (i=0; i<pf.hdr.nchan; i++) { 
                pf_out.sub.dat_weights[i]=pf.sub.dat_weights[i];
            }
            last_filenum = pf_out.filenum;
        }

        /* Check to see if its time to process data */
        if (start_time>0.0) {
            double cur_time = (fmjd - fmjd0) * 86400.0;
            if (cur_time<start_time) 
                continue; 
        }

        if (first_data) {
           psr_phase(&pc[ipc], imjd, fmjd, NULL, &last_pulse);
           first_data=0;
        }

        /* Check to see if we're done */
        if (process_time>0.0) {
            double cur_time = (fmjd - fmjd0) * 86400.0;
            if (cur_time > start_time + process_time) {
                run=0;
                break;
            }
        }

        /* for singlepulse: loop over samples, output a new subint
         * whenever pulse number increases.
         */
        for (i=0; i<pf.hdr.nsblk; i++) {
        
            /* Keep track of timestamp */
            // TODO also pointing stuff?
            fmjd_samp = fmjd + i*pf.hdr.dt/86400.0;
            pf_out.sub.offs += pf.sub.offs - 0.5*pf.sub.tsubint + i*pf.hdr.dt;
            sampcount++;

            /* Calc current pulse number */
            psr_phase(&pc[ipc], imjd, fmjd_samp, &psr_freq, &cur_pulse);

            /* TODO: deal with scale/offset? */

            /* Fold this sample */
            fargs.pc = &pc[ipc];
            fargs.imjd = imjd;
            fargs.fmjd = fmjd_samp;
            rv = fold_8bit_power(fargs.pc, 
                    fargs.imjd, fargs.fmjd, 
                    fargs.data + i*bytes_per_sample,
                    fargs.nsamp, fargs.tsamp, fargs.raw_signed, fargs.fb);
            if (rv!=0) {
                fprintf(stderr, "Fold error.\n");
                exit(1);
            }

            /* See if integration needs to be written, etc */
            if (cur_pulse > last_pulse) {

                /* Figure out timestamp */
                pf_out.sub.offs /= (double)sampcount;
                pf_out.sub.tsubint = 1.0/psr_freq;
                fmjd_epoch = fmjd0 + pf_out.sub.offs/86400.0;

                /* Transpose, output subint */
                normalize_transpose_folds((float *)pf_out.sub.data, &fb);
                psrfits_write_subint(&pf_out);

                /* If file incremented, clear polyco flags */
                if (pf_out.filenum > last_filenum) 
                    for (j=0; j<npc; j++) 
                        pc_written[j]=0;

                /* Write this polyco if needed */
                if (pc_written[ipc]==0) {
                    psrfits_write_polycos(&pf_out, pc, npc);
                    pc_written[ipc] = 1;
                }

                /* Check for write errors */
                if (pf_out.status) {
                    fprintf(stderr, "Error writing subint.\n");
                    fits_report_error(stderr, pf_out.status);
                    exit(1);
                }

                /* Clear counters, avgs */
                clear_foldbuf(&fb);
                pf_out.sub.offs = 0.0;
                sampcount=0;
                last_pulse = cur_pulse;
                last_filenum = pf_out.filenum;

            }
        }


        /* Progress report */
        if (!quiet) {
            printf("\rFile %d %5.1f%%", pf.filenum, 
                    100.0 * (float)(pf.rownum-1)/(float)pf.rows_per_file);
            fflush(stdout);
        }
    }

    psrfits_close(&pf_out);
    psrfits_close(&pf);

    if (rv) { fits_report_error(stderr, rv); }
    exit(0);
}
コード例 #2
0
void guppi_psrfits_thread(void *_args) {
    
    /* Get args */
    struct guppi_thread_args *args = (struct guppi_thread_args *)_args;
    pthread_cleanup_push((void *)guppi_thread_set_finished, args);
    
    /* Set cpu affinity */
    cpu_set_t cpuset, cpuset_orig;
    sched_getaffinity(0, sizeof(cpu_set_t), &cpuset_orig);
    CPU_ZERO(&cpuset);
    CPU_SET(1, &cpuset);
    int rv = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
    if (rv<0) { 
        guppi_error("guppi_psrfits_thread", "Error setting cpu affinity.");
        perror("sched_setaffinity");
    }

    /* Set priority */
    rv = setpriority(PRIO_PROCESS, 0, args->priority);
    if (rv<0) {
        guppi_error("guppi_psrfits_thread", "Error setting priority level.");
        perror("set_priority");
    }
    
    /* Attach to status shared mem area */
    struct guppi_status st;
    rv = guppi_status_attach(&st);
    if (rv!=GUPPI_OK) {
        guppi_error("guppi_psrfits_thread", 
                    "Error attaching to status shared memory.");
        pthread_exit(NULL);
    }
    pthread_cleanup_push((void *)guppi_status_detach, &st);
    pthread_cleanup_push((void *)set_exit_status, &st);
    
    /* Init status */
    guppi_status_lock_safe(&st);
    hputs(st.buf, STATUS_KEY, "init");
    guppi_status_unlock_safe(&st);
    
    /* Initialize some key parameters */
    struct guppi_params gp;
    struct psrfits pf;
    pf.sub.data = NULL;
    pf.sub.dat_freqs = pf.sub.dat_weights = NULL;
    pf.sub.dat_offsets = pf.sub.dat_scales = NULL;
    pf.hdr.chan_dm = 0.0;
    pf.filenum = 0; // This is crucial
    pthread_cleanup_push((void *)guppi_free_psrfits, &pf);
    pthread_cleanup_push((void *)psrfits_close, &pf);
    //pf.multifile = 0;  // Use a single file for fold mode
    pf.multifile = 1;  // Use a multiple files for fold mode
    pf.quiet = 0;      // Print a message per each subint written
    
    /* Attach to databuf shared mem */
    struct guppi_databuf *db;
    db = guppi_databuf_attach(args->input_buffer);
    if (db==NULL) {
        guppi_error("guppi_psrfits_thread",
                    "Error attaching to databuf shared memory.");
        pthread_exit(NULL);
    }
    pthread_cleanup_push((void *)guppi_databuf_detach, db);
    
    /* Loop */
    int curblock=0, total_status=0, firsttime=1, run=1, got_packet_0=0;
    int mode=SEARCH_MODE;
    char *ptr;
    char tmpstr[256];
    struct foldbuf fb;
    struct polyco pc[64];  
    memset(pc, 0, sizeof(pc));
    int n_polyco_written=0;
    float *fold_output_array = NULL;
    int scan_finished=0;
    signal(SIGINT, cc);
    do {
        /* Note waiting status */
        guppi_status_lock_safe(&st);
        if (got_packet_0)
            sprintf(tmpstr, "waiting(%d)", curblock);
        else
            sprintf(tmpstr, "ready");
        hputs(st.buf, STATUS_KEY, tmpstr);
        guppi_status_unlock_safe(&st);
        
        /* Wait for buf to have data */
        rv = guppi_databuf_wait_filled(db, curblock);
        if (rv!=0) {
            // This is a big ol' kludge to avoid this process hanging
            // due to thread synchronization problems.
            sleep(1);
            continue; 
        }

        /* Note current block */
        guppi_status_lock_safe(&st);
        hputi4(st.buf, "CURBLOCK", curblock);
        guppi_status_unlock_safe(&st);

        /* See how full databuf is */
        total_status = guppi_databuf_total_status(db);
        
        /* Read param structs for this block */
        ptr = guppi_databuf_header(db, curblock);
        if (firsttime) {
            guppi_read_obs_params(ptr, &gp, &pf);
            firsttime = 0;
        } else {
            guppi_read_subint_params(ptr, &gp, &pf);
        }

        /* Find out what mode this data is in */
        mode = psrfits_obs_mode(pf.hdr.obs_mode);

        /* Check if we got both packet 0 and a valid observation
         * start time.  If so, flag writing to start.
         */
        if (got_packet_0==0 && gp.packetindex==0 && gp.stt_valid==1) {
            got_packet_0 = 1;
            guppi_read_obs_params(ptr, &gp, &pf);
            guppi_update_ds_params(&pf);
            memset(pc, 0, sizeof(pc));
            n_polyco_written=0;
        }

        /* If actual observation has started, write the data */
        if (got_packet_0) { 

            /* Note waiting status */
            guppi_status_lock_safe(&st);
            hputs(st.buf, STATUS_KEY, "writing");
            guppi_status_unlock_safe(&st);
            
            /* Get the pointer to the current data */
            if (mode==FOLD_MODE) {
                fb.nchan = pf.hdr.nchan;
                fb.npol = pf.hdr.npol;
                fb.nbin = pf.hdr.nbin;
                fb.data = (float *)guppi_databuf_data(db, curblock);
                fb.count = (unsigned *)(guppi_databuf_data(db, curblock)
                        + foldbuf_data_size(&fb));
                fold_output_array = (float *)realloc(fold_output_array,
                        sizeof(float) * pf.hdr.nbin * pf.hdr.nchan * 
                        pf.hdr.npol);
                pf.sub.data = (unsigned char *)fold_output_array;
                pf.fold.pc = (struct polyco *)(guppi_databuf_data(db,curblock)
                        + foldbuf_data_size(&fb) + foldbuf_count_size(&fb));
            } else 
                pf.sub.data = (unsigned char *)guppi_databuf_data(db, curblock);
            
            /* Set the DC and Nyquist channels explicitly to zero */
            /* because of the "FFT Problem" that splits DC power  */
            /* into those two bins.                               */
            zero_end_chans(&pf);

            /* Output only Stokes I (in place) */
            if (pf.hdr.onlyI && pf.hdr.npol==4)
                get_stokes_I(&pf);

            /* Downsample in frequency (in place) */
            if (pf.hdr.ds_freq_fact > 1)
                downsample_freq(&pf);

            /* Downsample in time (in place) */
            if (pf.hdr.ds_time_fact > 1)
                downsample_time(&pf);

            /* Folded data needs a transpose */
            if (mode==FOLD_MODE)
                normalize_transpose_folds(fold_output_array, &fb);

            /* Write the data */
            int last_filenum = pf.filenum;
            psrfits_write_subint(&pf);

            /* Any actions that need to be taken when a new file
             * is created.
             */
            if (pf.filenum!=last_filenum) {
                /* No polycos yet written to the new file */
                n_polyco_written=0;
            }

            /* Write the polycos if needed */
            int write_pc=0, i, j;
            for (i=0; i<pf.fold.n_polyco_sets; i++) {
                if (pf.fold.pc[i].used==0) continue; 
                int new_pc=1;
                for (j=0; j<n_polyco_written; j++) {
                    if (polycos_differ(&pf.fold.pc[i], &pc[j])==0) {
                        new_pc=0;
                        break;
                    }
                }
                if (new_pc || n_polyco_written==0) {
                    pc[n_polyco_written] = pf.fold.pc[i];
                    n_polyco_written++;
                    write_pc=1;
                } else {
                    pf.fold.pc[i].used = 0; // Already have this one
                }
            }
            if (write_pc) 
                psrfits_write_polycos(&pf, pf.fold.pc, pf.fold.n_polyco_sets);

            /* Is the scan complete? */
            if ((pf.hdr.scanlen > 0.0) && 
                (pf.T > pf.hdr.scanlen)) scan_finished = 1;
            
            /* For debugging... */
            if (gp.drop_frac > 0.0) {
               printf("Block %d dropped %.3g%% of the packets\n", 
                      pf.tot_rows, gp.drop_frac*100.0);
            }

        }

        /* Mark as free */
        guppi_databuf_set_free(db, curblock);
        
        /* Go to next block */
        curblock = (curblock + 1) % db->n_block;
        
        /* Check for cancel */
        pthread_testcancel();
        
    } while (run && !scan_finished);
    
    /* Cleanup */
    
    if (fold_output_array!=NULL) free(fold_output_array);

    pthread_exit(NULL);
    
    pthread_cleanup_pop(0); /* Closes psrfits_close */
    pthread_cleanup_pop(0); /* Closes guppi_free_psrfits */
    pthread_cleanup_pop(0); /* Closes set_exit_status */
    pthread_cleanup_pop(0); /* Closes set_finished */
    pthread_cleanup_pop(0); /* Closes guppi_status_detach */
    pthread_cleanup_pop(0); /* Closes guppi_databuf_detach */
}
コード例 #3
0
ファイル: fold_psrfits.c プロジェクト: andriu5/vegas_devel
int main(int argc, char *argv[]) {

    /* Cmd line */
    static struct option long_opts[] = {
        {"output",  1, NULL, 'o'},
        {"nbin",    1, NULL, 'b'},
        {"tsub",    1, NULL, 't'},
        {"nthread", 1, NULL, 'j'},
        {"initial", 1, NULL, 'i'},
        {"final",   1, NULL, 'f'},
        {"src",     1, NULL, 's'},
        {"polyco",  1, NULL, 'p'},
        {"parfile", 1, NULL, 'P'},
        {"foldfreq",1, NULL, 'F'},
        {"cal",     0, NULL, 'C'},
        {"unsigned",0, NULL, 'u'},
        {"nunsigned",1, NULL, 'U'},
        {"split",   1, NULL, 'S'},
        {"apply",   0, NULL, 'A'},
        {"quiet",   0, NULL, 'q'},
        {"help",    0, NULL, 'h'},
        {0,0,0,0}
    };
    int opt, opti;
    int nbin=256, nthread=4, fnum_start=1, fnum_end=0;
    int quiet=0, raw_signed=1, use_polycos=1, cal=0, apply_scale=0;
    double split_size_gb = 1.0;
    double tfold = 60.0; 
    double fold_frequency=0.0;
    char output_base[256] = "";
    char polyco_file[256] = "";
    char par_file[256] = "";
    char source[24];  source[0]='\0';
    while ((opt=getopt_long(argc,argv,"o:b:t:j:i:f:s:p:P:F:CuU:S:Aqh",long_opts,&opti))!=-1) {
        switch (opt) {
            case 'o':
                strncpy(output_base, optarg, 255);
                output_base[255]='\0';
                break;
            case 'b':
                nbin = atoi(optarg);
                break;
            case 't':
                tfold = atof(optarg);
                break;
            case 'j':
                nthread = atoi(optarg);
                break;
            case 'i':
                fnum_start = atoi(optarg);
                break;
            case 'f':
                fnum_end = atoi(optarg);
                break;
            case 's':
                strncpy(source, optarg, 24);
                source[23]='\0';
                break;
            case 'p':
                strncpy(polyco_file, optarg, 255);
                polyco_file[255]='\0';
                use_polycos = 1;
                break;
            case 'P':
                strncpy(par_file, optarg, 255);
                par_file[255] = '\0';
                break;
            case 'F':
                fold_frequency = atof(optarg);
                use_polycos = 0;
                break;
            case 'C':
                cal = 1;
                use_polycos = 0;
                break;
            case 'u':
                raw_signed=0;
                break;
            case 'U':
                raw_signed = 4 - atoi(optarg);
                break;
            case 'S':
                split_size_gb = atof(optarg);
                break;
            case 'A':
                apply_scale = 1;
                break;
            case 'q':
                quiet=1;
                break;
            case 'h':
            default:
                usage();
                exit(0);
                break;
        }

    }
    if (optind==argc) { 
        usage();
        exit(1);
    }

    /* If no polyco/par file given, default to polyco.dat */
    if (use_polycos && (par_file[0]=='\0' && polyco_file[0]=='\0'))
        sprintf(polyco_file, "polyco.dat");

    /* Open first file */
    struct psrfits pf;
    sprintf(pf.basefilename, argv[optind]);
    pf.filenum = fnum_start;
    pf.tot_rows = pf.N = pf.T = pf.status = 0;
    pf.hdr.chan_dm = 0.0; // What if folding data that has been partially de-dispersed?
    pf.filename[0]='\0';
    int rv = psrfits_open(&pf);
    if (rv) { fits_report_error(stderr, rv); exit(1); }

    /* Check any constraints */
    if (pf.hdr.nbits!=8) { 
        fprintf(stderr, "Only implemented for 8-bit data (read nbits=%d).\n",
                pf.hdr.nbits);
        exit(1);
    }

    /* Check for calfreq */
    if (cal) {
        if (pf.hdr.cal_freq==0.0) {
            if (fold_frequency==0.0) {
                fprintf(stderr, "Error: Cal mode selected, but CAL_FREQ=0.  "
                        "Set cal frequency with -F\n");
                exit(1);
            } else {
                pf.hdr.cal_freq = fold_frequency;
            }
        } else {
            fold_frequency = pf.hdr.cal_freq;
        }
    }

    /* Set up output file */
    struct psrfits pf_out;
    memcpy(&pf_out, &pf, sizeof(struct psrfits));
    if (source[0]!='\0') { strncpy(pf_out.hdr.source, source, 24); }
    else { strncpy(source, pf.hdr.source, 24); source[23]='\0'; }
    if (output_base[0]=='\0') {
        /* Set up default output filename */
        sprintf(output_base, "%s_%s_%5.5d_%5.5d%s", pf_out.hdr.backend, 
                pf_out.hdr.source, pf_out.hdr.start_day, 
                (int)pf_out.hdr.start_sec, cal ? "_cal" : "");
    }
    sprintf(pf_out.basefilename, output_base);
    if (cal) {
        sprintf(pf_out.hdr.obs_mode, "CAL");
        sprintf(pf_out.hdr.cal_mode, "SYNC");
    } else
        sprintf(pf_out.hdr.obs_mode, "PSR");
    strncpy(pf_out.fold.parfile,par_file,255); pf_out.fold.parfile[255]='\0';
    pf_out.fptr = NULL;
    pf_out.filenum=0;
    pf_out.status=0;
    pf_out.quiet=0;
    pf_out.hdr.nbin=nbin;
    pf_out.sub.FITS_typecode = TFLOAT;
    pf_out.sub.bytes_per_subint = sizeof(float) * 
        pf_out.hdr.nchan * pf_out.hdr.npol * pf_out.hdr.nbin;
    if (split_size_gb > 0.0) { 
        pf_out.multifile = 1;
        pf_out.rows_per_file = (int) (split_size_gb * (1024.0*1024.0*1024.0)
                / (double)pf_out.sub.bytes_per_subint);
        printf("Writing a maximum of %d subintegrations (~%.1f GB) per output file.\n", 
            pf_out.rows_per_file, split_size_gb);
    } else {
        pf_out.multifile = 0;
        printf("Writing a single output file.\n");
    }

    rv = psrfits_create(&pf_out);
    if (rv) { fits_report_error(stderr, rv); exit(1); }

    /* Alloc data buffers */
    pf.sub.dat_freqs = (float *)malloc(sizeof(float) * pf.hdr.nchan);
    pf_out.sub.dat_freqs = pf.sub.dat_freqs;
    pf.sub.dat_weights = (float *)malloc(sizeof(float) * pf.hdr.nchan);
    pf_out.sub.dat_weights = (float *)malloc(sizeof(float) * pf.hdr.nchan);
    pf.sub.dat_offsets = (float *)malloc(sizeof(float) 
            * pf.hdr.nchan * pf.hdr.npol);
    pf_out.sub.dat_offsets = (float *)malloc(sizeof(float) 
            * pf.hdr.nchan * pf.hdr.npol);
    pf.sub.dat_scales  = (float *)malloc(sizeof(float) 
            * pf.hdr.nchan * pf.hdr.npol);
    pf_out.sub.dat_scales  = (float *)malloc(sizeof(float) 
            * pf.hdr.nchan * pf.hdr.npol);
    pf_out.sub.data  = (unsigned char *)malloc(pf_out.sub.bytes_per_subint);

    /* Output scale/offset */
    int i, ipol, ichan;
    float offset_uv=0.0;  
    // Extra cross-term offset for GUPPI
    if (strcmp("GUPPI",pf.hdr.backend)==0 && apply_scale==0) { 
        offset_uv=0.5;
        fprintf(stderr, "Found backend=GUPPI, setting offset_uv=%f\n",
                offset_uv);
    }
    // Initialize scale/output and weights.
    // These get copied from the input file later during the main loop.
    for (ipol=0; ipol<pf.hdr.npol; ipol++) {
        for (ichan=0; ichan<pf.hdr.nchan; ichan++) {
            float offs = 0.0;
            if (ipol>1) offs = offset_uv;
            pf_out.sub.dat_scales[ipol*pf.hdr.nchan + ichan] = 1.0;
            pf_out.sub.dat_offsets[ipol*pf.hdr.nchan + ichan] = offs;
        }
    }
    for (i=0; i<pf.hdr.nchan; i++) { pf_out.sub.dat_weights[i]=1.0; }

    /* Read or make polycos */
    int npc=0, ipc=0;
    struct polyco *pc = NULL;
    if (use_polycos) {
        if (polyco_file[0]=='\0') {
            /* Generate from par file */
            npc = make_polycos(par_file, &pf.hdr, source, &pc);
            if (npc<=0) {
                fprintf(stderr, "Error generating polycos.\n");
                exit(1);
            }
            printf("Auto-generated %d polycos, src=%s\n", npc, source);
        } else {
            /* Read from polyco file */
            FILE *pcfile = fopen(polyco_file, "r");
            if (pcfile==NULL) { 
                fprintf(stderr, "Couldn't open polyco file.\n");
                exit(1);
            }
            npc = read_all_pc(pcfile, &pc);
            if (npc==0) {
                fprintf(stderr, "Error parsing polyco file.\n");
                exit(1);
            }
            fclose(pcfile);
        }
    } else {
        // Const fold period mode, generate a fake polyco?
        pc = (struct polyco *)malloc(sizeof(struct polyco));
        sprintf(pc[0].psr, "CONST");
        pc[0].mjd = (int)pf.hdr.MJD_epoch;
        pc[0].fmjd = fmod(pf.hdr.MJD_epoch,1.0);
        pc[0].rphase = 0.0;
        pc[0].f0 = fold_frequency;
        pc[0].nsite = 0; // Does this matter?
        pc[0].nmin = 24 * 60;
        pc[0].nc = 1;
        pc[0].rf = pf.hdr.fctr;
        pc[0].c[0] = 0.0;
        pc[0].used = 0;
        npc = 1;
    }
    int *pc_written = (int *)malloc(sizeof(int) * npc);
    for (i=0; i<npc; i++) pc_written[i]=0;

    /* Alloc total fold buf */
    struct foldbuf fb;
    fb.nchan = pf.hdr.nchan;
    fb.npol = pf.hdr.npol;
    fb.nbin = pf_out.hdr.nbin;
    malloc_foldbuf(&fb);
    clear_foldbuf(&fb);

    /* Set up thread management */
    pthread_t *thread_id;
    struct fold_args *fargs;
    thread_id = (pthread_t *)malloc(sizeof(pthread_t) * nthread);
    fargs = (struct fold_args *)malloc(sizeof(struct fold_args) * nthread);
    for (i=0; i<nthread; i++) { 
        thread_id[i] = 0; 
        fargs[i].data = (char *)malloc(sizeof(char)*pf.sub.bytes_per_subint);
        fargs[i].fb = (struct foldbuf *)malloc(sizeof(struct foldbuf));
        fargs[i].fb->nbin = pf_out.hdr.nbin;
        fargs[i].fb->nchan = pf.hdr.nchan;
        fargs[i].fb->npol = pf.hdr.npol;
        fargs[i].nsamp = pf.hdr.nsblk;
        fargs[i].tsamp = pf.hdr.dt;
        fargs[i].raw_signed=raw_signed;
        malloc_foldbuf(fargs[i].fb);
        clear_foldbuf(fargs[i].fb);
        fargs[i].scale = (float *)malloc(sizeof(float) 
                * pf.hdr.nchan * pf.hdr.npol);
        fargs[i].offset = (float *)malloc(sizeof(float) 
                * pf.hdr.nchan * pf.hdr.npol);
    }

    /* Main loop */
    rv=0;
    int imjd;
    double fmjd, fmjd0=0, fmjd_next=0, fmjd_epoch;
    double offs0=0, offs1=0;
    //double phase=0.0, freq=1.0;
    int first=1, subcount=0;
    int cur_thread = 0;
    signal(SIGINT, cc);
    while (run) { 

        /* Read data block */
        pf.sub.data = (unsigned char *)fargs[cur_thread].data;
        rv = psrfits_read_subint(&pf);
        if (rv) { 
            if (rv==FILE_NOT_OPENED) rv=0; // Don't complain on file not found
            run=0; 
            break; 
        }

        /* If we've passed final file, exit */
        if (fnum_end && pf.filenum>fnum_end) { run=0; break; }

        /* Get start date, etc */
        imjd = (int)pf.hdr.MJD_epoch;
        fmjd = (double)(pf.hdr.MJD_epoch - (long double)imjd);
        fmjd += (pf.sub.offs-0.5*pf.sub.tsubint)/86400.0;

        /* First time stuff */
        if (first) {
            fmjd0 = fmjd;
            fmjd_next = fmjd + tfold/86400.0;
            pf_out.sub.offs=0.0;
            offs0 = pf.sub.offs - 0.5*pf.sub.tsubint;
            offs1 = pf.sub.offs + 0.5*pf.sub.tsubint;
            first=0;
            for (i=0; i<pf.hdr.nchan; i++) { 
                pf_out.sub.dat_weights[i]=pf.sub.dat_weights[i];
            }
        }

        /* Keep track of timestamp */
        // TODO also pointing stuff.
        pf_out.sub.offs += pf.sub.offs;
        subcount++;

        /* Update output block end time */
        offs1 = pf.sub.offs + 0.5*pf.sub.tsubint;

        /* Select polyco set */
        if (use_polycos) {
            ipc = select_pc(pc, npc, source, imjd, fmjd);
            //ipc = select_pc(pc, npc, NULL, imjd, fmjd);
            if (ipc<0) { 
                fprintf(stderr, "No matching polycos (src=%s, imjd=%d, fmjd=%f)\n",
                        source, imjd, fmjd);
                break;
            }
        } else {
            ipc = 0;
        }
        pc[ipc].used = 1; // Mark this polyco set as used for folding

        /* Copy scale/offset from input to output if we're not applying it */
        if (apply_scale==0) {
            for (i=0; i<pf.hdr.nchan*pf.hdr.npol; i++) {
                pf_out.sub.dat_scales[i] = pf.sub.dat_scales[i];
                pf_out.sub.dat_offsets[i] = pf.sub.dat_offsets[i];
            }
        }

        /* Fold this subint */
        fargs[cur_thread].pc = &pc[ipc];
        fargs[cur_thread].imjd = imjd;
        fargs[cur_thread].fmjd = fmjd;
        rv = pthread_create(&thread_id[cur_thread], NULL, 
                fold_8bit_power_thread, &fargs[cur_thread]);
        if (rv) {
            fprintf(stderr, "Thread creation error.\n");
            exit(1);
        }
        if (apply_scale) {
            for (i=0; i<pf.hdr.nchan*pf.hdr.npol; i++) {
                fargs[cur_thread].scale[i] = pf.sub.dat_scales[i];
                fargs[cur_thread].offset[i] = pf.sub.dat_offsets[i];
            }
        }
        cur_thread++;

        /* Combine thread results if needed */
        if (cur_thread==nthread || fmjd>fmjd_next) {

            /* Loop over active threads */
            for (i=0; i<cur_thread; i++) {

                /* Wait for thread to finish */
                rv = pthread_join(thread_id[i], NULL);
                if (rv) { 
                    fprintf(stderr, "Thread join error.\n");
                    exit(1);
                }

                /* Apply scale and offset here */
                if (apply_scale) 
                    scale_offset_folds(fargs[i].fb, fargs[i].scale,
                            fargs[i].offset);

                /* Combine its result into total fold */
                accumulate_folds(&fb, fargs[i].fb);

                /* Reset thread info */
                clear_foldbuf(fargs[i].fb);
                thread_id[i] = 0;

            }

            /* Reset active thread count */
            cur_thread = 0;
        }

        /* See if integration needs to be written, etc */
        if (fmjd > fmjd_next) {

            /* Figure out timestamp */
            pf_out.sub.offs /= (double)subcount;
            pf_out.sub.tsubint = offs1 - offs0;
            fmjd_epoch = fmjd0 + pf_out.sub.offs/86400.0;
            /*
            // Don't need this stuff if we set EPOCHS=MIDTIME
            ipc = select_pc(pc, npc, pf.hdr.source, imjd, fmjd_epoch); 
            if (ipc<0) { 
                fprintf(stderr, "Polyco error, exiting.\n");
                exit(1);
            }
            phase = psr_phase(&pc[ipc], imjd, fmjd_epoch, &freq);
            phase = fmod(phase, 1.0);
            pf_out.sub.offs -= phase/freq; // ref epoch needs 0 phase
            */

            /* Transpose, output subint */
            normalize_transpose_folds((float *)pf_out.sub.data, &fb);
            int last_filenum = pf_out.filenum;
            psrfits_write_subint(&pf_out);


            /* Check for write errors */
            if (pf_out.status) {
                fprintf(stderr, "Error writing subint.\n");
                fits_report_error(stderr, pf_out.status);
                exit(1);
            }

            /* Check if we started a new file */
            if (pf_out.filenum!=last_filenum) {
                /* No polycos yet written to this file */
                for (i=0; i<npc; i++) pc_written[i]=0;
            }

            /* Write the current polyco if needed */
            if (pc_written[ipc]==0) {
                psrfits_write_polycos(&pf_out, &pc[ipc], 1);
                if (pf_out.status) {
                    fprintf(stderr, "Error writing polycos.\n");
                    fits_report_error(stderr, pf_out.status);
                    exit(1);
                }
                pc_written[ipc] = 1;
            }

            /* Clear counters, avgs */
            clear_foldbuf(&fb);
            pf_out.sub.offs = 0.0;
            offs0 = pf.sub.offs - 0.5*pf.sub.tsubint;
            subcount=0;

            /* Set next output time */
            fmjd_next = fmjd + tfold/86400.0;
        }


        /* Progress report */
        if (!quiet) {
            printf("\rFile %d %5.1f%%", pf.filenum, 
                    100.0 * (float)(pf.rownum-1)/(float)pf.rows_per_file);
            fflush(stdout);
        }
    }

    /* Join any running threads */
    for (i=0; i<cur_thread; i++)  
        if (thread_id[i]) pthread_join(thread_id[i], NULL);

    /* Remove polyco table in cal mode */
    if (cal) {
        rv = psrfits_remove_polycos(&pf_out);
        if (rv) { fits_report_error(stderr, rv); }
    }

    psrfits_close(&pf_out);
    psrfits_close(&pf);

    if (rv) { fits_report_error(stderr, rv); }
    exit(0);
}