Metropoli BBS
VIEWER: calibrat.c MODE: TEXT (ASCII)
/***************************************************************************
 *		  Copyright (C) 1994  Charles P. Peterson                  *
 *	     4007 Enchanted Sun, San Antonio, Texas 78244-1254             *
 *              Email: Charles_P_Peterson@fcircus.sat.tx.us                *
 *                                                                         *
 *		  This is free software with NO WARRANTY.                  *
 *	      See gfft.c, or run program itself, for details.              *
 *		      Support is available for a fee.                      *
 ***************************************************************************
 *
 * Program:     gfft--General FFT analysis
 * File:        calibrat.c
 * Purpose:     calibration(s) 
 * Author:      Charles Peterson (CPP)
 * History:     15-March-1994 CPP; Created.
 *              5-Aug-94 CPP (1.05); add new calibrations to end of list
 * Comment:     Simple 'relative' calibration with linear interpolation.
 *              Any number of calibration(s) may be in effect.
 *              NOT complex deconvolution (maybe in a few years).
 *              Linear interpolation, and flat extension are used
 *              (Flat extension helps with fuzzy endpoints).
 */

#include <stdio.h>
#include <stdlib.h>  /* strtod */
#include <string.h>  /* strtok */
#include <math.h>    /* log10, pow */

#include "gfft.h"
#include "settings.h"
#include "wbench.h"

#define CAL_BUFSIZ 133
#define PROGRESS_INCREMENT 128

int calibration_list__count (struct cal_st **clistp)
{
    int count = 0;
    struct cal_st *clist = *clistp;

    while (clist)
    {
	count++;
	clist = clist->next;
    }
    return count;
}


void calibration_list__cancel (struct cal_st **clistp)
{
    struct cal_st *clist = *clistp;
/*
 * Free tail of list recursively, then free this node
 */
    if (clist)
    {
	if (clist->next)
	{
	    calibration_list__cancel (&clist->next);
	}
	gfree (clist->frequencies);
	gfree (clist->amplitudes);
/*	gfree (clist->filename); potentially dangerous */
	gfree (clist);
    }
    *clistp = NULL;
}


void calibration_list__add (struct cal_st **clistp,
			    char *cname, 
			    FILE *cfile,
			    BOOLEAN db_scale)
{
    int progress_count = 0;
    struct cal_st *clist = *clistp;
    char cal_buf[CAL_BUFSIZ];
    char *token;
    char *beyondpointer;
    double *freq_array = NULL;
    float *ampl_array = NULL;
    double frequency;
    float amplitude;
    long size = 0;
    struct cal_st *new_cal;
/*
 * First, read in file data
 */
    while (fgets (cal_buf, CAL_BUFSIZ, cfile))
    {
	if (progress_count++ % PROGRESS_INCREMENT == 0)
	{
	    progress_requester__update (-1);
	}
    /*
     * Skip over comment lines
     */
	if (cal_buf[0] == '#' || cal_buf[0] == ';' || cal_buf[0] == '!')
	{
	    continue;
	}
    /*
     * get frequency value
     */
	token = strtok (cal_buf, " ");
	if (!token)
	{
	    error_message (INVALID_CALIBRATION_FILE);
	    RAISE_ERROR (NOTHING_SPECIAL);    /* longjmp outa here */
	}
	frequency = strtod (token, &beyondpointer);
	if (*beyondpointer != '\0')
	{
	    error_message (INVALID_CALIBRATION_FILE);
	    RAISE_ERROR (NOTHING_SPECIAL);    /* longjmp outa here */
	}
	token = strtok (NULL, " ");
	if (!token)
	{
	    error_message (INVALID_CALIBRATION_FILE);
	    RAISE_ERROR (NOTHING_SPECIAL);    /* longjmp outa here */
	}
	amplitude = (float) strtod (token, &beyondpointer);
	if (*beyondpointer != '\0' && *beyondpointer != '\n')
	{
	    error_message (INVALID_CALIBRATION_FILE);
	    RAISE_ERROR (NOTHING_SPECIAL);    /* longjmp outa here */
	}
    /*
     * Add new values to arrays
     */
	size++;
	freq_array = grealloc (freq_array, (size * sizeof frequency), 
			       NOTHING_SPECIAL);
	ampl_array = grealloc (ampl_array, (size * sizeof amplitude), 
			       NOTHING_SPECIAL);
	freq_array[size-1] = frequency;
	ampl_array[size-1] = amplitude;
    }
    if (size <= 0)
    {
	error_message (INVALID_CALIBRATION_FILE);
	RAISE_ERROR (NOTHING_SPECIAL);    /* longjmp outa here */
    }

/*
 * create new node
 */
    new_cal = gmalloc (sizeof (struct cal_st), NOTHING_SPECIAL);
    new_cal->next = NULL;
    new_cal->filename = cname;
    new_cal->size = size;
    new_cal->db = db_scale;
    new_cal->frequencies = freq_array;
    new_cal->amplitudes = ampl_array;
    new_cal->index = 0;
/*
 * insert new node in list, or at start
 */
    if (clist)
    {
	while (clist->next)
	{
	    clist = clist->next;
	}
	clist->next = new_cal;
    }
    else
    {
	*clistp = new_cal;
    }
}

void calibration_list__reset (struct cal_st **clistp)
{
    if (*clistp)
    {
	(*clistp)->index = 0;
	calibration_list__reset (&((*clistp)->next));
    }
}

void calibration_list__write (FILE *fp, char *cstr, struct cal_st **clistp)
{
    struct cal_st *clist = *clistp;

    while (clist)
    {
	if (clist->db)
	{
	    fprintf (fp, "%sDBCalibrate %s\n", cstr, clist->filename);
	}
	else
	{
	    fprintf (fp, "%sCalibrate %s\n", cstr, clist->filename);
	}
	clist = clist->next;
    }
}


float calibration_list__apply (struct cal_st **clistp,
			       float value, double frequency)
{
    struct cal_st *clist = *clistp;
    long index;
    BOOLEAN outrange = TRUE;
    double factor;

/*
 * Assumption is made that frequencies are applied in an increasing
 * order.  When starting over, call calibration_list__reset
 *
 * Make local copy of 'index'
 */
    index = clist->index;
/*
 * Find applicable index
 */
    while (frequency > clist->frequencies[index])
    {
	outrange = FALSE;
	if (++index >= clist->size)
	{
	    index--;
	    outrange = TRUE;
	    break;
	}
    }
    clist->index = index;  /* update */
    
/*
 * Compute the factor to use
 */

    if (frequency == clist->frequencies[index] || outrange)
    {
	factor = clist->amplitudes[index];
    }
    else
    {
    /*
     * linear interpolation is fun
     * (Sorry I don't bother with anything more sophisticated yet)
     */
	double x1 = clist->frequencies[index-1];
	double x2 = clist->frequencies[index];
	double y1 = clist->amplitudes[index-1];
	double y2 = clist->amplitudes[index];
	double slope = (y2 - y1) / (x2 - x1);
	double offset = y1 - (slope * x1);

	factor = (slope * frequency) + offset;
    }

/*
 * Apply
 */
    if (Db)
    {
	if (clist->db)
	{
	    value -= factor;
	}
	else
	{
	    double dbfactor;
	    if (factor <= 0.0) RAISE_ERROR (NOTHING_SPECIAL);
	    dbfactor = 20.0 * log10 (factor);
	    value -= dbfactor;
	}
    }
    else
    {
	if (!clist->db)
	{
	    if (factor == 0.0) RAISE_ERROR (NOTHING_SPECIAL);
	    value /= factor;
	}
	else
	{
	    value /= pow (10.0, (factor * 0.05));
	}
    }
/*
 * Do next calibration in list
 */
    if (clist->next)
    {
	value = calibration_list__apply (&clist->next, value, frequency);
    }

    return value;
}
	

[ RETURN TO DIRECTORY ]