257 lines
7.1 KiB
C
257 lines
7.1 KiB
C
|
/*
|
|||
|
* @Author: mypx
|
|||
|
* @Date: 2025-09-24 11:10:45
|
|||
|
* @LastEditTime: 2025-09-24 13:32:18
|
|||
|
* @LastEditors: mypx mypx_coder@163.com
|
|||
|
* @Description:
|
|||
|
*/
|
|||
|
#include "pm_params.h"
|
|||
|
#include "et_log.h"
|
|||
|
|
|||
|
static pm_params_t *pm_params = NULL;
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Calculate actual rotation (measured value - zero point)
|
|||
|
*
|
|||
|
* @param params Pointer to measurement parameters
|
|||
|
* @return float Actual rotation value (°)
|
|||
|
*/
|
|||
|
float pm_calculate_actual_rotation(pm_params_t *params)
|
|||
|
{
|
|||
|
return params->measured.measured_value - params->measured.zero_point;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Calculate specific rotation
|
|||
|
*
|
|||
|
* @param params Pointer to measurement parameters
|
|||
|
* @return float Specific rotation ([α], °)
|
|||
|
*/
|
|||
|
float pm_calculate_specific_rotation(pm_params_t *params)
|
|||
|
{
|
|||
|
float actual_rotation = pm_calculate_actual_rotation(params);
|
|||
|
|
|||
|
/* Specific rotation formula: [α] = (100 × α) / (L × C)
|
|||
|
* L: tube length (dm) from config
|
|||
|
* C: concentration (g/100ml) from measured
|
|||
|
*/
|
|||
|
float L = params->config.tube_length;
|
|||
|
float C = params->measured.concentration;
|
|||
|
|
|||
|
if (L == 0.0f || C == 0.0f)
|
|||
|
{
|
|||
|
ET_ERR("Error: tube length or concentration must not be zero when calculating specific rotation\n");
|
|||
|
return NAN;
|
|||
|
}
|
|||
|
|
|||
|
return (100.0f * actual_rotation) / (L * C);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Calculate concentration
|
|||
|
*
|
|||
|
* @param params Pointer to measurement parameters
|
|||
|
* @return float Concentration (g/100ml)
|
|||
|
*/
|
|||
|
float pm_calculate_concentration(pm_params_t *params)
|
|||
|
{
|
|||
|
float actual_rotation = pm_calculate_actual_rotation(params);
|
|||
|
|
|||
|
/* Concentration formula: C = (100 × α) / (L × [α])
|
|||
|
* L: tube length from config
|
|||
|
* [α]: specific rotation from measured
|
|||
|
*/
|
|||
|
float L = params->config.tube_length;
|
|||
|
float alpha = params->measured.specific_rotation;
|
|||
|
|
|||
|
if (L == 0.0f || alpha == 0.0f)
|
|||
|
{
|
|||
|
ET_ERR("Error: tube length or specific rotation must not be zero when calculating concentration\n");
|
|||
|
return NAN;
|
|||
|
}
|
|||
|
|
|||
|
return (100.0f * actual_rotation) / (L * alpha);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Calculate international sugar scale (°Z)
|
|||
|
*
|
|||
|
* @param params Pointer to measurement parameters
|
|||
|
* @return float Sugar scale value (°Z)
|
|||
|
*/
|
|||
|
float pm_calculate_sugar_scale(pm_params_t *params)
|
|||
|
{
|
|||
|
float actual_rotation = pm_calculate_actual_rotation(params);
|
|||
|
|
|||
|
// International sugar scale formula: °Z = α × 2.888
|
|||
|
return actual_rotation * 2.888f;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Perform calculation based on selected measurement mode
|
|||
|
*
|
|||
|
* @param params Pointer to measurement parameters
|
|||
|
* @return float Calculation result
|
|||
|
*/
|
|||
|
float pm_calculate_measurement(pm_params_t *params)
|
|||
|
{
|
|||
|
switch (params->config.mode)
|
|||
|
{
|
|||
|
case PM_MEAS_MODE_OPTICAL_ROTATION:
|
|||
|
return pm_calculate_actual_rotation(params);
|
|||
|
|
|||
|
case PM_MEAS_MODE_SPECIFIC_ROTATION:
|
|||
|
return pm_calculate_specific_rotation(params);
|
|||
|
|
|||
|
case PM_MEAS_MODE_CONCENTRATION:
|
|||
|
return pm_calculate_concentration(params);
|
|||
|
|
|||
|
case PM_MEAS_MODE_SUGAR_SCALE:
|
|||
|
return pm_calculate_sugar_scale(params);
|
|||
|
|
|||
|
default:
|
|||
|
ET_ERR("Error: invalid measurement mode\n");
|
|||
|
return NAN;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Get the name of the measurement mode
|
|||
|
*
|
|||
|
* @param mode Measurement mode
|
|||
|
* @return const char* Mode name string
|
|||
|
*/
|
|||
|
const char *pm_get_mode_name(PM_MeasMode_t mode)
|
|||
|
{
|
|||
|
switch (mode)
|
|||
|
{
|
|||
|
case PM_MEAS_MODE_OPTICAL_ROTATION:
|
|||
|
return "Optical rotation";
|
|||
|
case PM_MEAS_MODE_SPECIFIC_ROTATION:
|
|||
|
return "Specific rotation";
|
|||
|
case PM_MEAS_MODE_CONCENTRATION:
|
|||
|
return "Concentration";
|
|||
|
case PM_MEAS_MODE_SUGAR_SCALE:
|
|||
|
return "International sugar scale";
|
|||
|
default:
|
|||
|
return "Unknown mode";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @brief Print measurement results
|
|||
|
*
|
|||
|
* @param params Pointer to measurement parameters
|
|||
|
* @param result Calculation result
|
|||
|
*/
|
|||
|
void pm_print_measurement_result(pm_params_t *params, float result)
|
|||
|
{
|
|||
|
ET_INFO("=== Polarimeter Measurement Result ===\n");
|
|||
|
ET_INFO("Mode: %s\n", pm_get_mode_name(params->config.mode));
|
|||
|
ET_INFO("Zero point: %.4f °\n", params->measured.zero_point);
|
|||
|
ET_INFO("Measured value: %.4f °\n", params->measured.measured_value);
|
|||
|
ET_INFO("Actual rotation: %.4f °\n", pm_calculate_actual_rotation(params));
|
|||
|
ET_INFO("Tube length: %.1f dm\n", params->config.tube_length);
|
|||
|
ET_INFO("Temperature: %.1f °C\n", params->measured.temperature);
|
|||
|
ET_INFO("Wavelength: %.0f nm\n", params->config.wavelength);
|
|||
|
|
|||
|
if (!isnan(result))
|
|||
|
{
|
|||
|
switch (params->config.mode)
|
|||
|
{
|
|||
|
case PM_MEAS_MODE_OPTICAL_ROTATION:
|
|||
|
ET_INFO("Result: %.4f °\n", result);
|
|||
|
break;
|
|||
|
|
|||
|
case PM_MEAS_MODE_SPECIFIC_ROTATION:
|
|||
|
ET_INFO("Concentration: %.2f g/100ml\n", params->measured.concentration);
|
|||
|
ET_INFO("Result: [α] = %.4f °\n", result);
|
|||
|
break;
|
|||
|
|
|||
|
case PM_MEAS_MODE_CONCENTRATION:
|
|||
|
ET_INFO("Specific rotation: %.2f °\n", params->measured.specific_rotation);
|
|||
|
ET_INFO("Result: %.4f g/100ml\n", result);
|
|||
|
break;
|
|||
|
|
|||
|
case PM_MEAS_MODE_SUGAR_SCALE:
|
|||
|
ET_INFO("Result: %.4f °Z\n", result);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ET_ERR("Calculation failed!\n");
|
|||
|
}
|
|||
|
ET_INFO("===========================\n\n");
|
|||
|
}
|
|||
|
|
|||
|
void pm_params_recompute(pm_params_t *params)
|
|||
|
{
|
|||
|
if (params == NULL)
|
|||
|
return;
|
|||
|
|
|||
|
params->computed.actual_rotation = pm_calculate_actual_rotation(params);
|
|||
|
|
|||
|
/* Compute specific rotation if concentration provided */
|
|||
|
if (params->measured.concentration != 0.0f)
|
|||
|
{
|
|||
|
params->computed.specific_rotation = pm_calculate_specific_rotation(params);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
params->computed.specific_rotation = params->measured.specific_rotation;
|
|||
|
}
|
|||
|
|
|||
|
/* Compute concentration if specific rotation provided */
|
|||
|
if (params->measured.specific_rotation != 0.0f)
|
|||
|
{
|
|||
|
params->computed.concentration = pm_calculate_concentration(params);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
params->computed.concentration = params->measured.concentration;
|
|||
|
}
|
|||
|
|
|||
|
params->computed.sugar_scale = pm_calculate_sugar_scale(params);
|
|||
|
}
|
|||
|
|
|||
|
void pm_params_reset(pm_params_t *params)
|
|||
|
{
|
|||
|
if (params == NULL)
|
|||
|
return;
|
|||
|
/* measured */
|
|||
|
params->measured.zero_point = 0.0f;
|
|||
|
params->measured.measured_value = 0.0f;
|
|||
|
params->measured.concentration = 10.0f; /* default 10 g/100ml */
|
|||
|
params->measured.specific_rotation = 66.5f; /* default for sucrose */
|
|||
|
params->measured.temperature = PM_TEMP_DEF; /* default 20°C */
|
|||
|
|
|||
|
pm_params_recompute(params);
|
|||
|
}
|
|||
|
|
|||
|
pm_params_t *pm_params_get_instance(void)
|
|||
|
{
|
|||
|
return pm_params;
|
|||
|
}
|
|||
|
|
|||
|
void pm_params_init(pm_params_t *params)
|
|||
|
{
|
|||
|
if (params == NULL)
|
|||
|
return;
|
|||
|
pm_params = params;
|
|||
|
/* measured */
|
|||
|
params->measured.zero_point = 0.0f;
|
|||
|
params->measured.measured_value = 0.0f;
|
|||
|
params->measured.concentration = 0.0f;
|
|||
|
params->measured.specific_rotation = 0.0f;
|
|||
|
params->measured.temperature = PM_TEMP_DEF;
|
|||
|
|
|||
|
/* config */
|
|||
|
params->config.tube_length = PM_TUBE_LENGTH_DEF; /* default 1 dm */
|
|||
|
params->config.wavelength = PM_WAVE_LENGTH_DEF; /* sodium D */
|
|||
|
params->config.mode = PM_MEAS_MODE_DEF;
|
|||
|
|
|||
|
pm_params_recompute(params);
|
|||
|
}
|
|||
|
|