Avionics
Core avionics package for CURE flight computers
Loading...
Searching...
No Matches
LaunchDetector.cpp
Go to the documentation of this file.
2#include <cassert>
3
4// #define DEBUG
5
6#ifdef DEBUG
7#include "ArduinoHAL.h"
8#endif
9
10namespace
11{
12uint8_t validateAndComputeWindowSize_slots(uint16_t windowSize_ms, uint16_t windowInterval_ms)
13{
14 assert(windowInterval_ms > 0U);
15
16 const auto windowSize_slots = static_cast<uint16_t>(windowSize_ms / windowInterval_ms);
17 assert(windowSize_slots >= 1U);
18 assert(windowSize_slots <= static_cast<uint16_t>(kCircularArrayAllocatedSlots));
19
20 return static_cast<uint8_t>(windowSize_slots);
21}
22} // namespace
23
24LaunchDetector::LaunchDetector(float accelerationThreshold_ms2, //NOLINT(bugprone-easily-swappable-parameters)
25 uint16_t windowSize_ms,
26 uint16_t windowInterval_ms)
27 : accelerationThresholdSq_ms2_(accelerationThreshold_ms2 * accelerationThreshold_ms2),
28 windowInterval_ms_(windowInterval_ms),
29 acceptableTimeDifference_ms_(static_cast<uint16_t>(static_cast<float>(windowInterval_ms) * kAcceptablePercentDifferenceWindowInterval)),
30 accelMagnitudeSquaredWindow_(validateAndComputeWindowSize_slots(windowSize_ms, windowInterval_ms)),
31 launched_(false),
32 launchedTime_ms_(0),
33 medianAccelerationSquared_(0)
34{
35 // These must remain here because they rely on accelMagnitudeSquaredWindow_ being constructed
36 const uint16_t windowSpan_slots = static_cast<uint16_t>(accelMagnitudeSquaredWindow_.getMaxSize() - 1U);
37 minWindowSize_ms_ = static_cast<uint16_t>(
38 static_cast<uint32_t>(windowInterval_ms_ - acceptableTimeDifference_ms_) * windowSpan_slots);
39 maxWindowSize_ms_ = static_cast<uint16_t>(
40 static_cast<uint32_t>(windowInterval_ms_ + acceptableTimeDifference_ms_) * windowSpan_slots);
41}
42
43
45{
46 // :xac: The x acceleration data point in ms^2
47 // :yac: The y acceleration data point in ms^2
48 // :zac: The z acceleration data point in ms^2
49
50 // Return false if the data is ignored, true if the data is accepted
51 // Will set the launched flag if the data is accepted and the launch is detected
52
53
54 // If launched, don't update
55 if (launched_)
56 {
57 #ifdef DEBUG
58 Serial.println("LaunchDetector: Data point ignored because already launched");
59 #endif
61 }
62 // Calculate the magnitude of the acceleration squared
63 const float aclMagSq = accel.x.data * accel.x.data + accel.y.data * accel.y.data + accel.z.data * accel.z.data;
64
65 // Take the average of the timestamps
66 // Ideally these should all be the same
67 const uint32_t time_ms = (accel.x.timestamp_ms + accel.y.timestamp_ms + accel.z.timestamp_ms) / 3;
68
69 // Making sure the new time is greater than the last time
70 if (time_ms < accelMagnitudeSquaredWindow_.getFromHead(0).timestamp_ms)
71 {
72 #ifdef DEBUG
73 Serial.println("LaunchDetector: Data point ignored because of time is earlier than head");
74 Serial.printf("Incoming time: %lu\n", static_cast<unsigned long>(time_ms));
75 Serial.printf("Head time: %lu\n", static_cast<unsigned long>(accelMagnitudeSquaredWindow_.getFromHead(0).timestamp_ms));
76 #endif
78 }
79
80 // If the window isn't full yet, just push the data point
81 if (!accelMagnitudeSquaredWindow_.isFull())
82 {
83 #ifdef DEBUG
84 // Serial.println("LaunchDetector: Populating initial window");
85 #endif
86 accelMagnitudeSquaredWindow_.push(DataPoint(time_ms, aclMagSq));
87
89 }
90
91 // Make sure we are near the window interval +- kAcceptablePercentDifferenceWindowInterval
92 uint32_t timeDiff_ms = time_ms - accelMagnitudeSquaredWindow_.getFromHead(0).timestamp_ms; //NOLINT(cppcoreguidelines-init-variables)
93 const uint32_t minAllowedDiff_ms = static_cast<uint32_t>(windowInterval_ms_) - static_cast<uint32_t>(acceptableTimeDifference_ms_);
94 const uint32_t maxAllowedDiff_ms = static_cast<uint32_t>(windowInterval_ms_) + static_cast<uint32_t>(acceptableTimeDifference_ms_);
95
96 // Check that the data didn't come in too fast
97 if (timeDiff_ms < minAllowedDiff_ms){
98 #ifdef DEBUG
99 Serial.println("LaunchDetector: DATA TOO EARLY");
100 Serial.printf("Time diff: %lu\n", static_cast<unsigned long>(timeDiff_ms));
101 Serial.printf("Window interval: %u\n", static_cast<unsigned int>(windowInterval_ms_));
102 Serial.printf("Incoming time: %lu\n", static_cast<unsigned long>(time_ms));
103 Serial.printf("Head time: %lu\n", static_cast<unsigned long>(accelMagnitudeSquaredWindow_.getFromHead(0).timestamp_ms));
104 #endif
105 return LP_DATA_TOO_FAST;
106 }
107
108 if (timeDiff_ms > maxAllowedDiff_ms)
109 {
110 #ifdef DEBUG
111 Serial.println("LaunchDetector: DATA TOO LATE");
112 Serial.printf("Time diff: %lu\n", static_cast<unsigned long>(timeDiff_ms));
113 Serial.printf("Window interval: %u\n", static_cast<unsigned int>(windowInterval_ms_));
114 Serial.printf("Incoming time: %lu\n", static_cast<unsigned long>(time_ms));
115 Serial.printf("Head time: %lu\n", static_cast<unsigned long>(accelMagnitudeSquaredWindow_.getFromHead(0).timestamp_ms));
116 Serial.println("LaunchDetector: Clearing window");
117 #endif
118
119 accelMagnitudeSquaredWindow_.clear();
121 }
122
123 // Push the new data point
124 #ifdef DEBUG
125 Serial.print("LaunchDetector: Pushing timestamp: ");
126 Serial.println(time_ms);
127 #endif
128
129 accelMagnitudeSquaredWindow_.push(DataPoint(time_ms, aclMagSq));
130
131 const uint32_t headTimestamp_ms = accelMagnitudeSquaredWindow_.getFromHead(0).timestamp_ms; //NOLINT(cppcoreguidelines-init-variables)
132 const uint32_t tailTimestamp_ms = accelMagnitudeSquaredWindow_.getFromHead(accelMagnitudeSquaredWindow_.getMaxSize() - 1).timestamp_ms; //NOLINT(cppcoreguidelines-init-variables)
133 const uint32_t timeRange_ms = headTimestamp_ms - tailTimestamp_ms;
134
135 // Ensure the time_range isn't too small
136 if (timeRange_ms < minWindowSize_ms_)
137 {
138 #ifdef DEBUG
139 Serial.println("LaunchDetector: Time range too small, waiting...");
140 Serial.printf("Time range: %lu\n", static_cast<unsigned long>(timeRange_ms));
141 Serial.printf("Min Time Range: %u\n", static_cast<unsigned int>(minWindowSize_ms_));
142 Serial.printf("Incoming time: %lu\n", static_cast<unsigned long>(time_ms));
143 Serial.printf("Head time: %lu\n", static_cast<unsigned long>(accelMagnitudeSquaredWindow_.getFromHead(0).timestamp_ms));
144 Serial.printf("Tail time: %lu\n", static_cast<unsigned long>(accelMagnitudeSquaredWindow_.getFromHead(accelMagnitudeSquaredWindow_.getMaxSize() - 1).timestamp_ms));
145 #endif
146
148 }
149
150 // Ensure the time_range isn't too large
151 if (timeRange_ms > maxWindowSize_ms_)
152 {
153 #ifdef DEBUG
154 Serial.println("LaunchDetector: Time range too large, waiting...");
155 Serial.printf("Time range: %lu\n", static_cast<unsigned long>(timeRange_ms));
156 Serial.printf("Max Time Range: %u\n", static_cast<unsigned int>(maxWindowSize_ms_));
157 Serial.printf("Incoming time: %lu\n", static_cast<unsigned long>(time_ms));
158 Serial.printf("Head time: %lu\n", static_cast<unsigned long>(accelMagnitudeSquaredWindow_.getFromHead(0).timestamp_ms));
159 Serial.printf("Tail time: %lu\n", static_cast<unsigned long>(accelMagnitudeSquaredWindow_.getFromHead(accelMagnitudeSquaredWindow_.getMaxSize() - 1).timestamp_ms));
160 #endif
161
163 }
164
165 // Check that the window is full
166 if (!accelMagnitudeSquaredWindow_.isFull())
167 {
168 return LP_WINDOW_NOT_FULL;
169 }
170
171 medianAccelerationSquared_ = accelMagnitudeSquaredWindow_.getMedian().data;
172
173 // Check if the median is above the threshold
174 if (medianAccelerationSquared_ > accelerationThresholdSq_ms2_)
175 {
176 launched_ = true;
177 launchedTime_ms_ = time_ms;
178 return LP_LAUNCH_DETECTED;
179 }
180
181 #ifdef DEBUG
182 Serial.println("LaunchDetector: Median below threshold");
183 // Print the median without being able to use %f because of the Arduino
184 Serial.print("Median: ");
185 Serial.println(medianAccelerationSquared_);
186 Serial.print("Threshold: ");
187 Serial.println(accelerationThresholdSq_ms2_);
188 #endif
189 return LP_ACL_TOO_LOW;
190}
191
193{
194 launched_ = false;
195 launchedTime_ms_ = 0;
196
197 // Clear the window
198 accelMagnitudeSquaredWindow_.clear();
199}
@ LP_LAUNCH_DETECTED
@ LP_WINDOW_NOT_FULL
@ LP_ACL_TOO_LOW
@ LP_INITIAL_POPULATION
@ LP_DATA_TOO_FAST
@ LP_WINDOW_TIME_RANGE_TOO_LARGE
@ LP_YOUNGER_TIMESTAMP
@ LP_WINDOW_DATA_STALE
@ LP_WINDOW_TIME_RANGE_TOO_SMALL
@ LP_ALREADY_LAUNCHED
constexpr std::size_t kCircularArrayAllocatedSlots
constexpr float kAcceptablePercentDifferenceWindowInterval
Timestamped float measurement container.
Definition DataPoint.h:11
float data
Definition DataPoint.h:14
uint32_t timestamp_ms
Definition DataPoint.h:13
LaunchDetector(float accelerationThreshold_ms2, uint16_t windowSize_ms, uint16_t windowInterval_ms)
int update(AccelerationTriplet accel)