Files
2026-08-29 23:18:36 +02:00

163 lines
7.1 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @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:
* <pre>
* +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 }
* }
* }
* </pre>
*/
#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_ */