/** * osp_device_take_spectrum_internal: **/ static CdSpectrum * osp_device_take_spectrum_internal (GUsbDevice *device, guint64 sample_duration, GError **error) { CdSpectrum *sp; gdouble val; gsize data_len; guint i; g_autofree guint8 *data = NULL; g_autoptr(GTimer) t = NULL; /* set integral time in us */ if (!osp_device_send_command (device, OSP_CMD_SET_INTEGRATION_TIME, (guint8 *) &sample_duration, 4, error)) return NULL; /* get spectrum */ t = g_timer_new (); if (!osp_device_query (device, OSP_CMD_GET_AND_SEND_RAW_SPECTRUM, NULL, 0, &data, &data_len, error)) return NULL; g_debug ("For integration of %.0fms, sensor took %.0fms", sample_duration / 1000.f, g_timer_elapsed (t, NULL) * 1000.f); /* check values */ if (data_len != 2048) { g_set_error (error, OSP_DEVICE_ERROR, OSP_DEVICE_ERROR_INTERNAL, "Expected %i bytes, got %" G_GSIZE_FORMAT, 2048, data_len); return NULL; } /* export */ sp = cd_spectrum_sized_new (1024); for (i = 0; i < 1024; i++) { val = data[i*2+1] * 256 + data[i*2+0]; cd_spectrum_add_value (sp, val / (gdouble) 0xffff); } /* the maximum value the hardware can return is 0x3fff */ val = cd_spectrum_get_value_max (sp); if (val > 0.25) { g_set_error (error, OSP_DEVICE_ERROR, OSP_DEVICE_ERROR_INTERNAL, "spectral max should be <= 0.25f, was %f", val); cd_spectrum_free (sp); return NULL; } return sp; }
/** * cd_spectrum_to_string: * @spectrum: a #CdSpectrum instance * @max_width: the terminal width * @max_height: the terminal height * * Returns a graphical representation of the spectrum. * * Return value: a printable ASCII string * * Since: 1.3.1 **/ gchar * cd_spectrum_to_string (CdSpectrum *spectrum, guint max_width, guint max_height) { GString *str = g_string_new (""); guint i, j; gdouble val_max; gdouble nm_scale; /* make space for the axes */ max_width -= 9; max_height -= 2; /* find value maximum */ val_max = cd_spectrum_get_value_max (spectrum); if (val_max < 0.001) val_max = 0.001; nm_scale = (cd_spectrum_get_end (spectrum) - cd_spectrum_get_start (spectrum)) / (gdouble) (max_width - 1); /* draw grid */ for (i = 0; i < max_height; i++) { gdouble val; val = val_max / (gdouble) max_height * (max_height - i); g_string_append_printf (str, "%7.3f |", val); for (j = 0; j < max_width; j++) { gdouble nm; nm = ((gdouble) j * nm_scale) + cd_spectrum_get_start (spectrum); if (cd_spectrum_get_value_for_nm (spectrum, nm) >= val) g_string_append (str, "#"); else g_string_append (str, "_"); } g_string_append (str, "\n"); } /* draw x axis */ g_string_append_printf (str, "%7.3f ", 0.f); for (j = 0; j < max_width; j++) g_string_append (str, "-"); g_string_append (str, "\n"); /* draw X labels */ g_string_append_printf (str, " %.0fnm", cd_spectrum_get_start (spectrum)); for (j = 0; j < max_width - 10; j++) g_string_append (str, " "); g_string_append_printf (str, "%.0fnm", cd_spectrum_get_end (spectrum)); g_string_append (str, "\n"); /* success */ return g_string_free (str, FALSE); }
/** * osp_device_take_spectrum: * @device: a #GUsbDevice instance. * @error: A #GError or %NULL * * Returns a spectrum. The optimal sample duration is calculated automatically. * * Return value: A #CdSpectrum, or %NULL for error * * Since: 1.2.11 **/ CdSpectrum * osp_device_take_spectrum (GUsbDevice *device, GError **error) { const guint sample_duration_max_secs = 3; gboolean relax_requirements = FALSE; gdouble max; gdouble scale = 0.f; guint64 sample_duration = 10000; /* us */ CdSpectrum *sp = NULL; guint i; g_return_val_if_fail (G_USB_IS_DEVICE (device), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); /* loop until we're in 1/4 to 3/4 FSD */ for (i = 0; i < 5; i++) { g_autoptr(CdSpectrum) sp_probe = NULL; /* for the last try, relax what we deem acceptable so we can * measure very black things with a long integration time */ if (i == 4) relax_requirements = TRUE; /* take a measurement */ sp_probe = osp_device_take_spectrum_full (device, sample_duration, error); if (sp_probe == NULL) return NULL; /* sensor picked up nothing, take action */ max = cd_spectrum_get_value_max (sp_probe); if (max < 0.001f) { sample_duration *= 100.f; g_debug ("sensor read no data, setting duration " "to %" G_GUINT64_FORMAT , sample_duration); continue; } /* sensor is saturated, take action */ if (max > 0.99f) { sample_duration /= 100.f; g_debug ("sensor saturated, setting duration " "to %" G_GUINT64_FORMAT, sample_duration); continue; } /* break out if we got valid readings */ if (max > 0.25f && max < 0.75f) { sp = cd_spectrum_dup (sp_probe); break; } /* be more accepting */ if (relax_requirements && max > 0.01f) { sp = cd_spectrum_dup (sp_probe); break; } /* aim for FSD / 2 */ scale = (gdouble) 0.5 / max; sample_duration *= scale; g_debug ("for max of %f, using scale=%f for duration %" G_GUINT64_FORMAT, max, scale, sample_duration); /* limit this to something sane */ if (sample_duration / G_USEC_PER_SEC > sample_duration_max_secs) { g_debug ("limiting duration from %us to %us", (guint) (sample_duration / G_USEC_PER_SEC), (guint) (sample_duration_max_secs)); sample_duration = sample_duration_max_secs * G_USEC_PER_SEC; relax_requirements = TRUE; } } /* no suitable readings */ if (sp == NULL) { g_set_error_literal (error, OSP_DEVICE_ERROR, OSP_DEVICE_ERROR_NO_DATA, "Got no valid data"); return NULL; } /* scale with the new integral time */ cd_spectrum_set_norm (sp, cd_spectrum_get_norm (sp) / scale); g_debug ("normalised spectral max is %f", cd_spectrum_get_value_max (sp)); return sp; }