/** * @file PulseGeneratorGAM.h * @brief GAM that synthesizes a triggered high-voltage pulse waveform. * @date 29/08/2026 * @author Martino Ferrari * * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and * the Development of Fusion Energy ('Fusion for Energy'). * Licensed under the EUPL, Version 1.1 or - as soon they will be approved * by the European Commission - subsequent versions of the EUPL (the "Licence") * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl * * @warning Unless required by applicable law or agreed to in writing, * software distributed under the Licence is distributed on an "AS IS" * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence permissions and limitations under the Licence. * * @details Emulates a charged-capacitor pulse discharge on a high-voltage bus: * * - on a rising edge of the (optional) Trigger input — the EPICS "start" * setpoint — the output ramps linearly from its current level to HighLevel * over RampUpMs (default 1 ms, the charger ramp); * - it then holds HighLevel flat for PlateauMs — read from the (optional) * PlateauMs input at trigger time, i.e. the EPICS "duration" setpoint; * - finally it ramps back to 0 over RampDownMs (default 100 ms, the * capacitive discharge); * - until the next trigger the output sits at 0 (the "off" phase). * * Gaussian noise (NoiseStdDev, default 333.33 V → 3σ ≈ ±1000 V) is added to * every sample, and random EMI spikes of ±EMIAmplitude (default 5000 V, * probability EMIProbabilityPerSample per sample, EMISpikeSamples long) strike * during both the on and off phases. * * With no Trigger input connected, AutoTriggerPeriodMs > 0 self-triggers the * sequence periodically so the GAM runs standalone. * * Configuration: *
 * +MyGAM = {
 *     Class = PulseGeneratorGAM
 *     SamplingRate            = 1000000.0  // must match the output signal rate
 *     RampUpMs                = 1.0
 *     RampDownMs              = 100.0
 *     HighLevel               = -40000.0
 *     NoiseStdDev             = 333.33
 *     EMIAmplitude            = 5000.0
 *     EMIProbabilityPerSample = 0.00001
 *     EMISpikeSamples         = 5
 *     PlateauMsDefault        = 500.0     // used when PlateauMs input absent
 *     AutoTriggerPeriodMs     = 0.0       // 0 = external trigger only
 *     Seed                    = 0         // PRNG seed; 0 = from wall clock
 *     InputSignals = {                    // optional: 0, 1 or 2 inputs
 *         Trigger   = { DataSource = DDB; Type = float32 }
 *         PlateauMs = { DataSource = DDB; Type = float32 }
 *     }
 *     OutputSignals = {
 *         HV = { DataSource = DDB; Type = float32; NumberOfElements = 1000 }
 *     }
 * }
 * 
*/ #ifndef PULSEGENERATORGAM_H_ #define PULSEGENERATORGAM_H_ /*---------------------------------------------------------------------------*/ /* Standard header includes */ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* Project header includes */ /*---------------------------------------------------------------------------*/ #include "CompilerTypes.h" #include "GAM.h" /*---------------------------------------------------------------------------*/ /* Class declaration */ /*---------------------------------------------------------------------------*/ namespace MARTe { class PulseGeneratorGAM : public GAM { public: CLASS_REGISTER_DECLARATION() /** * @brief Constructor. Sets safe defaults. */ PulseGeneratorGAM(); /** * @brief Destructor. */ virtual ~PulseGeneratorGAM(); /** * @brief Reads the waveform parameters from config and seeds the PRNG. */ virtual bool Initialise(StructuredDataI &data); /** * @brief Resolves the output array and the optional input scalars. * @return true if exactly one float32 output signal and at most two inputs. */ virtual bool Setup(); /** * @brief Emits the next block of waveform samples (noise + EMI included). * @return true always. */ virtual bool Execute(); private: /** @brief Pulse state machine phases. */ typedef enum { PulsePhaseOff = 0u, /**< Output at 0 V, waiting for a trigger */ PulsePhaseRampUp = 1u, /**< Charger ramp: startLevel → HighLevel */ PulsePhaseFlat = 2u, /**< Holding HighLevel for the plateau */ PulsePhaseRampDown = 3u /**< Discharge ramp: HighLevel → 0 */ } PulsePhase; float64 samplingRate; /**< Sample rate [Hz] */ float64 rampUpMs; /**< Charger ramp duration [ms] */ float64 rampDownMs; /**< Discharge ramp duration [ms] */ float64 highLevel; /**< Plateau level [V] */ float64 noiseStdDev; /**< Gaussian noise sigma [V] */ float64 emiAmplitude; /**< EMI spike amplitude [V] */ float64 emiProbabilityPerSample; /**< Probability of starting an EMI spike per sample */ uint32 emiSpikeSamples; /**< Length of one EMI spike [samples] */ float64 plateauMsDefault; /**< Plateau used when the PlateauMs input is absent [ms] */ float64 autoTriggerPeriodMs; /**< Self-trigger period [ms]; 0 = external only */ uint32 seed; /**< PRNG seed; 0 = wall clock */ uint32 nElements; /**< Output samples per Execute() call */ float32 *outputBuf; /**< Output waveform memory */ float32 *triggerIn; /**< Optional rising-edge trigger input */ float32 *plateauMsIn; /**< Optional plateau-duration input [ms] */ PulsePhase phase; /**< Current state machine phase */ float64 startLevel; /**< Waveform level when the current ramp started */ float64 currentLevel; /**< Clean (pre-noise) level of the last emitted sample */ uint64 phaseElapsed; /**< Samples consumed in the current phase */ uint64 phaseTotal; /**< Total samples of the current phase */ uint64 rampUpSamples; /**< RampUpMs expressed in samples */ uint64 rampDownSamples; /**< RampDownMs expressed in samples */ uint64 plateauSamples; /**< Plateau for the current pulse [samples] */ float64 prevTrigger; /**< Previous Trigger input value (edge detection) */ uint64 samplesSinceTrigger; /**< Samples since the last trigger (auto-trigger) */ uint32 spikeRemaining; /**< Samples left in the current EMI spike */ float64 spikeValue; /**< Signed amplitude of the current EMI spike */ /** @brief Starts a new pulse: ramp up from the current level. */ void StartSequence(float64 plateauMs); /** @brief Advances to the next phase, skipping zero-length ones. */ void AdvanceToNextPhase(); }; } /* namespace MARTe */ #endif /* PULSEGENERATORGAM_H_ */