FcuCalibration
FcuCalibration is the assembled, validated form of a Sirah
FCU-style doubling-crystal tuning curve: a pure value type, hardware-free
like LifConversion (its sibling in data/lif/), that
maps a fundamental wavelength (nm) to and from a motor position (steps)
under one of three interchangeable schemes. One of the static factories
— physical(), polynomial(), or spline() — is the sole
validating construction path; a default-constructed instance is the
unconfigured, always-invalid case, since (unlike an empty
LifConversion topology) there is no meaningful identity calibration
for a doubling crystal. wavelengthToPos()/posToWavelength()
delegate to the active scheme and are defined for any finite input
regardless of isValid() — a malformed calibration evaluates to
mathematically well-defined (if meaningless) numbers, and an evaluation
that cannot be carried out for a structural reason (an unbracketed
Physical root find, an out-of-domain Spline query) returns NaN
rather than aborting. isValid()/errorString() report whether the
calibration was assembled from well-formed input, not whether a
particular evaluation succeeded.
SirahFcu::hwReadSettings() is the only current caller: it builds
and caches a FcuCalibration from the driver’s registered
calibrationScheme setting and the active scheme’s settings, and
setPos()/readPos() delegate to it. See
LIF Conversion Stage for the schemes from a
user’s point of view — when to choose each, their user-visible
settings, and the offline python/tools/ workflow (fcu_measure.py
/ fcu_fit.py) that fits the parameters or curves Blackchirp then
imports or has typed in directly.
Schemes
Scheme::Physical evaluates a best-effort Type-I second-harmonic
phase-match law for a BBO or KDP crystal (CrystalType) — the Eimerl
(1987) and Zernike (1964) Sellmeier equations respectively, with a
linear dispersion knob against an arbitrary-units temperature
parameter — combined with the sine-bar drive’s mechanics and a
crystal-face refraction term. phaseMatchAngleDeg() is exposed
publicly so the phase-match law can be validated in isolation (for
example, against the Sirah Autotracker service manual’s Table 6-1 cut
angles) independent of the sine-bar geometry. The inverse direction
(position → wavelength) is a bracketed 1-D root find over the crystal’s
phase-matchable fundamental band, since the forward map has no
closed-form inverse.
Scheme::Polynomial and Scheme::Spline both evaluate imported,
already-fitted data rather than a physical law: Polynomial is
Horner evaluation of separately-imported forward and inverse
coefficient lists (ascending order, so neither direction needs a root
find); Spline builds two monotone (gsl_interp_steffen)
interpolating splines from an imported (wavelength, position) point
table — one keyed by wavelength, one by position — since the mapping
must be invertible in both directions and a single spline object is
keyed by one axis only.
The physical model
The Physical scheme composes three pieces: crystal dispersion
(Sellmeier equations), the Type-I phase-match condition, and the
sine-bar drive geometry. The equations below are best-effort literature
values, fit to the user’s own calibration data rather than matched
bit-for-bit to any vendor curve.
Sellmeier equations. The ordinary and extraordinary refractive indices come from the crystal’s Sellmeier equation (wavelength \(\lambda\) in micrometres) with a linear thermo-optic term applied to \(n\) about a 293-unit reference:
For BBO (Eimerl, 1987), with \(dn_o/dT = -16.6\times10^{-6}\) and \(dn_e/dT = -9.3\times10^{-6}\):
For KDP (Zernike, 1964), with \(dn_o/dT = -3.4\times10^{-5}\) and \(dn_e/dT = -2.4\times10^{-5}\):
The temperature parameter is in arbitrary units — a dispersion fit
knob rather than a controlled physical temperature — and enters only
through the thermo-optic term above.
Phase-match condition. For Type-I second-harmonic generation in a negative uniaxial crystal, the phase-match angle \(\theta_{pm}\) (between the beam and the crystal optic axis) satisfies
with the indices evaluated at the fundamental \(\lambda\) and its
second harmonic \(\lambda/2\). phaseMatchAngleDeg() returns this
angle (clamped to \([0, 90]\) outside the phase-matchable band).
Sine-bar geometry. A lead screw drives a sine bar that rotates the
crystal. With cut angle \(\theta_c\), linear offset \(L_0\),
angle offset \(\alpha_0\), lever length \(\ell\), screw pitch
\(s\), motor resolution \(r\), and sign \(\sigma = \pm 1\)
from the invert flag, the forward map from fundamental wavelength to
motor position \(p\) is
The \(\alpha_\mathrm{int}\!\to\!\alpha_\mathrm{ext}\) step is Snell refraction at the crystal face (using the ordinary index at the fundamental), which makes the cut angle and angle offset independently identifiable rather than degenerate. The inverse direction (position to wavelength) has no closed form and is a bracketed 1-D root find over the crystal’s phase-matchable band.
API Reference
-
class FcuCalibration
Pure value type representing an assembled FCU tuning curve: the doubling crystal’s fundamental wavelength (nm) <-> motor position (steps) mapping, in either direction.
Hardware-free and copyable, like
LifConversion(data/lif/lifconversion.h), its sibling in this directory: callers build aFcuCalibrationfrom a settings snapshot (or, in tests, directly from the static factories below) and hand it to a driver without that driver touching the tuning math itself. One ofphysical(),polynomial(), orspline()is the only validating construction path; a default-constructed instance is the unconfigured, always-invalid case (there is no meaningful identity calibration the way an emptyLifConversiontopology is the identity conversion).wavelengthToPos()/posToWavelength()are defined for any finite input regardless ofisValid()— malformed coefficients evaluate to mathematically well-defined (if meaningless) numbers, and an evaluation that cannot be carried out for a structural reason (aPhysicalfundamental outside the crystal’s phase-matchable band, an unbracketedPhysicalroot find, an out-of-domainSplinequery) returns NaN rather than aborting or throwing.Polynomialhas no such structural bound — imported coefficient lists carry no fit-domain metadata, so Horner evaluation extrapolates freely for any finite input.isValid()reports whether the calibration was assembled from well-formed input, not whether a particular evaluation succeeded.Public Functions
-
FcuCalibration()
Construct the unconfigured, invalid calibration. Use one of the static factories to build a usable instance.
-
double wavelengthToPos(double lamNm) const
Fundamental wavelength (nm) -> motor position (steps), per the active scheme. See the class comment for the no-
isValid()per-call convention.
-
double posToWavelength(double pos) const
Motor position (steps) -> fundamental wavelength (nm), per the active scheme. See the class comment for the no-
isValid()per-call convention.Physicalinverts by a bracketed 1-D root find over the crystal’s phase-matchable fundamental band;Polynomialis Horner evaluation of the inverse coefficient list;Splineevaluates the position-keyed interpolating spline. Returns NaN if pos cannot be inverted (no bracket found, or outside the spline’s domain).
-
bool isValid() const
trueiff this instance was assembled from well-formed input by one of the static factories.
-
QString errorString() const
Populated iff
!isValid(), explaining why assembly failed.
Public Static Functions
-
static FcuCalibration physical(BC::FcuCal::CrystalType crystal, double cutAngleDeg, double temperature, double linearOffsetMm, double angleOffsetDeg, double screwPitchMm, double leverLengthMm, double motorResolution, bool invert)
Assemble the
Physicalscheme: a Type-I SHG phase-match law for crystal (seephaseMatchAngleDeg()) combined with the sine-bar drive mechanics and a crystal-face refraction term.cutAngleDeg is the crystal’s optic-axis-to-face cut angle (Table 6-1 of the Sirah Autotracker service manual, per crystal/band). temperature is an arbitrary-units dispersion fit knob (see the Sellmeier comment in the .cpp), not a controlled physical temperature; it defaults to 293 in
phaseMatchAngleDeg()but must be supplied explicitly here. linearOffsetMm, angleOffsetDeg, screwPitchMm, leverLengthMm, and motorResolution are the sine-bar drive’s mechanical constants. invert selects the phase-match relation’s ± branch. Invalid (non-finite parameters, or a zero screw pitch) yieldsisValid()==falsewitherrorString()explaining why.
-
static FcuCalibration polynomial(std::vector<double> forwardCoeffs, std::vector<double> inverseCoeffs)
Assemble the
Polynomialscheme from imported forward (wavelength (nm) -> position) and inverse (position -> wavelength (nm)) coefficient lists, ascending order (c0+ c1*x + c2*x^2 + …). Evaluated by Horner’s method. Invalid (either list empty) yieldsisValid()==false.
-
static FcuCalibration spline(std::vector<std::pair<double, double>> points)
Assemble the
Splinescheme from an imported(wavelength (nm), position) point table. Builds two monotone (gsl_interp_steffen) interpolating splines internally — one keyed by wavelength forwavelengthToPos(), one keyed by position forposToWavelength()— since the mapping must be invertible in both directions.points need not be pre-sorted. Invalid (fewer than GSL’s minimum point count for a Steffen spline, duplicate wavelengths, or a position sequence that is not strictly monotone in wavelength order and therefore not invertible) yields
isValid()==false.
-
static double phaseMatchAngleDeg(BC::FcuCal::CrystalType crystal, double lamFundNm, double temperature = 293.0)
Type-I second-harmonic phase-match angle (degrees) for crystal at fundamental wavelength lamFundNm (nm) and dispersion-fit temperature (arbitrary units; see the
Physicalfactory comment).Exposed publicly so it can be validated in isolation against the Sirah Autotracker service manual’s Table 6-1 cut angles. The underlying
sin^2(theta_pm) expression is clamped to [0,1] before theasin(), so this always returns a value in [0,90] even outside the crystal’s physically phase-matchable band (where it saturates at the boundary rather than reporting a domain error).
Private Functions
-
double physicalForward(double lamNm) const
Fundamental wavelength (nm) -> motor position (steps),
Physicalscheme. The building blockposToWavelength()'sroot find brackets.
-
double physicalInverse(double pos) const
Motor position (steps) -> fundamental wavelength (nm),
Physicalscheme, by bracketed bisection ofphysicalForward().
Evaluate spline at x, returning NaN if spline is null (an invalidly-assembled instance) or x falls outside [domainMin, domainMax]. GSL’s own domain check on
gsl_spline_eval()routes through the global error handler, which aborts the process unless the application has disabled it (asmain.cppdoes); a value type usable from a hardware-free unit test cannot rely on that, so this guards the domain itself and never calls into GSL out of range.
Private Members
-
bool d_valid = {false}
-
QString d_errorString
-
PhysicalParams d_physical
-
std::vector<double> d_forwardCoeffs
Polynomial:wavelength (nm) -> position.
-
std::vector<double> d_inverseCoeffs
Polynomial:position -> wavelength (nm).
-
std::shared_ptr<gsl_spline> ps_wavelengthToPosSpline
Spline:wavelength (nm) -> position, keyed by wavelength.shared_ptr(custom deletergsl_spline_free) rather thanunique_ptrsoFcuCalibrationstays a cheaply-copyable value type;gsl_splineitself is not copyable and evaluation is not a hot loop (once per acquisition point), so sharing the underlying spline across copies rather than deep-copying it is the simpler and sufficient choice.
-
std::shared_ptr<gsl_spline> ps_posToWavelengthSpline
Spline:position -> wavelength (nm), keyed by position.
-
double d_splineWavelengthMin = {0.0}
-
double d_splineWavelengthMax = {0.0}
-
double d_splinePosMin = {0.0}
-
double d_splinePosMax = {0.0}
-
struct PhysicalParams
Physicalscheme parameters, named and grouped exactly as the forward-map formula (see the .cpp) consumes them.Public Members
-
BC::FcuCal::CrystalType crystal = {BC::FcuCal::CrystalType::BBO}
-
double cutAngleDeg = {0.0}
-
double temperature = {293.0}
-
double linearOffsetMm = {0.0}
-
double angleOffsetDeg = {0.0}
-
double screwPitchMm = {1.0}
-
double leverLengthMm = {0.0}
-
double motorResolution = {1.0}
-
bool invert = {false}
-
BC::FcuCal::CrystalType crystal = {BC::FcuCal::CrystalType::BBO}
-
FcuCalibration()
-
enum class BC::FcuCal::Scheme
Which tuning-curve model a
FcuCalibrationwas assembled from.Values:
-
enumerator Physical
Best-effort Type-I SHG phase-match + sine-bar mechanics.
-
enumerator Polynomial
Imported forward/inverse coefficient lists (Horner evaluation).
-
enumerator Spline
Imported (wavelength, position) point table (GSL monotone splines).
-
enumerator Physical
-
enum class BC::FcuCal::CrystalType
Doubling-crystal species supported by the
Physicalscheme; picks the Sellmeier equations used for the phase-match calculation.Values:
-
enumerator BBO
Beta barium borate (Eimerl 1987 Sellmeier).
-
enumerator KDP
Potassium dihydrogen phosphate (Zernike 1964 Sellmeier).
-
enumerator BBO